Skip to content

Commit d28dff3

Browse files
committed
Introduce CompactAttribute array in TupleDesc
The new compact_attrs array stores a few select fields from FormData_pg_attribute in a more compact way, using only 16 bytes per column instead of the 104 bytes that FormData_pg_attribute uses. Using CompactAttribute allows performance-critical operations such as tuple deformation to be performed without looking at the FormData_pg_attribute element in TupleDesc which means fewer cacheline accesses. With this change, NAMEDATALEN could be increased with a much smaller negative impact on performance. For some workloads, tuple deformation can be the most CPU intensive part of processing the query. Some testing with 16 columns on a table where the first column is variable length showed around a 10% increase in transactions per second for an OLAP type query performing aggregation on the 16th column. However, in certain cases, the increases were much higher, up to ~25% on one AMD Zen4 machine. This also makes pg_attribute.attcacheoff redundant. A follow-on commit will remove it, thus shrinking the FormData_pg_attribute struct by 4 bytes. Author: David Rowley Discussion: https://postgr.es/m/CAApHDvrBztXP3yx=NKNmo3xwFAFhEdyPnvrDg3=M0RhDs+4vYw@mail.gmail.com Reviewed-by: Andres Freund, Victor Yegorov
1 parent e4c8865 commit d28dff3

File tree

14 files changed

+246
-68
lines changed

14 files changed

+246
-68
lines changed

src/backend/access/common/heaptuple.c

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@
8383
#define VARLENA_ATT_IS_PACKABLE(att) \
8484
((att)->attstorage != TYPSTORAGE_PLAIN)
8585

86+
/* FormData_pg_attribute.attstorage != TYPSTORAGE_PLAIN and an attlen of -1 */
87+
#define COMPACT_ATTR_IS_PACKABLE(att) \
88+
((att)->attlen == -1 && (att)->attispackable)
89+
8690
/*
8791
* Setup for caching pass-by-ref missing attributes in a way that survives
8892
* tupleDesc destruction.
@@ -147,12 +151,12 @@ Datum
147151
getmissingattr(TupleDesc tupleDesc,
148152
int attnum, bool *isnull)
149153
{
150-
Form_pg_attribute att;
154+
CompactAttribute *att;
151155

152156
Assert(attnum <= tupleDesc->natts);
153157
Assert(attnum > 0);
154158

155-
att = TupleDescAttr(tupleDesc, attnum - 1);
159+
att = TupleDescCompactAttr(tupleDesc, attnum - 1);
156160

157161
if (att->atthasmissing)
158162
{
@@ -223,15 +227,15 @@ heap_compute_data_size(TupleDesc tupleDesc,
223227
for (i = 0; i < numberOfAttributes; i++)
224228
{
225229
Datum val;
226-
Form_pg_attribute atti;
230+
CompactAttribute *atti;
227231

228232
if (isnull[i])
229233
continue;
230234

231235
val = values[i];
232-
atti = TupleDescAttr(tupleDesc, i);
236+
atti = TupleDescCompactAttr(tupleDesc, i);
233237

234-
if (ATT_IS_PACKABLE(atti) &&
238+
if (COMPACT_ATTR_IS_PACKABLE(atti) &&
235239
VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
236240
{
237241
/*
@@ -268,7 +272,7 @@ heap_compute_data_size(TupleDesc tupleDesc,
268272
* Fill in either a data value or a bit in the null bitmask
269273
*/
270274
static inline void
271-
fill_val(Form_pg_attribute att,
275+
fill_val(CompactAttribute *att,
272276
bits8 **bit,
273277
int *bitmask,
274278
char **dataP,
@@ -349,8 +353,7 @@ fill_val(Form_pg_attribute att,
349353
data_length = VARSIZE_SHORT(val);
350354
memcpy(data, val, data_length);
351355
}
352-
else if (VARLENA_ATT_IS_PACKABLE(att) &&
353-
VARATT_CAN_MAKE_SHORT(val))
356+
else if (att->attispackable && VARATT_CAN_MAKE_SHORT(val))
354357
{
355358
/* convert to short varlena -- no alignment */
356359
data_length = VARATT_CONVERTED_SHORT_SIZE(val);
@@ -427,7 +430,7 @@ heap_fill_tuple(TupleDesc tupleDesc,
427430

428431
for (i = 0; i < numberOfAttributes; i++)
429432
{
430-
Form_pg_attribute attr = TupleDescAttr(tupleDesc, i);
433+
CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, i);
431434

432435
fill_val(attr,
433436
bitP ? &bitP : NULL,
@@ -461,7 +464,8 @@ heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
461464
Assert(!tupleDesc || attnum <= tupleDesc->natts);
462465
if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data))
463466
{
464-
if (tupleDesc && TupleDescAttr(tupleDesc, attnum - 1)->atthasmissing)
467+
if (tupleDesc &&
468+
TupleDescCompactAttr(tupleDesc, attnum - 1)->atthasmissing)
465469
return false;
466470
else
467471
return true;
@@ -570,13 +574,13 @@ nocachegetattr(HeapTuple tup,
570574

571575
if (!slow)
572576
{
573-
Form_pg_attribute att;
577+
CompactAttribute *att;
574578

575579
/*
576580
* If we get here, there are no nulls up to and including the target
577581
* attribute. If we have a cached offset, we can use it.
578582
*/
579-
att = TupleDescAttr(tupleDesc, attnum);
583+
att = TupleDescCompactAttr(tupleDesc, attnum);
580584
if (att->attcacheoff >= 0)
581585
return fetchatt(att, tp + att->attcacheoff);
582586

@@ -591,7 +595,7 @@ nocachegetattr(HeapTuple tup,
591595

592596
for (j = 0; j <= attnum; j++)
593597
{
594-
if (TupleDescAttr(tupleDesc, j)->attlen <= 0)
598+
if (TupleDescCompactAttr(tupleDesc, j)->attlen <= 0)
595599
{
596600
slow = true;
597601
break;
@@ -614,18 +618,18 @@ nocachegetattr(HeapTuple tup,
614618
* fixed-width columns, in hope of avoiding future visits to this
615619
* routine.
616620
*/
617-
TupleDescAttr(tupleDesc, 0)->attcacheoff = 0;
621+
TupleDescCompactAttr(tupleDesc, 0)->attcacheoff = 0;
618622

619623
/* we might have set some offsets in the slow path previously */
620-
while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0)
624+
while (j < natts && TupleDescCompactAttr(tupleDesc, j)->attcacheoff > 0)
621625
j++;
622626

623-
off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff +
624-
TupleDescAttr(tupleDesc, j - 1)->attlen;
627+
off = TupleDescCompactAttr(tupleDesc, j - 1)->attcacheoff +
628+
TupleDescCompactAttr(tupleDesc, j - 1)->attlen;
625629

626630
for (; j < natts; j++)
627631
{
628-
Form_pg_attribute att = TupleDescAttr(tupleDesc, j);
632+
CompactAttribute *att = TupleDescCompactAttr(tupleDesc, j);
629633

630634
if (att->attlen <= 0)
631635
break;
@@ -639,7 +643,7 @@ nocachegetattr(HeapTuple tup,
639643

640644
Assert(j > attnum);
641645

642-
off = TupleDescAttr(tupleDesc, attnum)->attcacheoff;
646+
off = TupleDescCompactAttr(tupleDesc, attnum)->attcacheoff;
643647
}
644648
else
645649
{
@@ -659,7 +663,7 @@ nocachegetattr(HeapTuple tup,
659663
off = 0;
660664
for (i = 0;; i++) /* loop exit is at "break" */
661665
{
662-
Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
666+
CompactAttribute *att = TupleDescCompactAttr(tupleDesc, i);
663667

664668
if (HeapTupleHasNulls(tup) && att_isnull(i, bp))
665669
{
@@ -707,7 +711,7 @@ nocachegetattr(HeapTuple tup,
707711
}
708712
}
709713

710-
return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off);
714+
return fetchatt(TupleDescCompactAttr(tupleDesc, attnum), tp + off);
711715
}
712716

713717
/* ----------------
@@ -892,7 +896,7 @@ expand_tuple(HeapTuple *targetHeapTuple,
892896
{
893897
if (attrmiss[attnum].am_present)
894898
{
895-
Form_pg_attribute att = TupleDescAttr(tupleDesc, attnum);
899+
CompactAttribute *att = TupleDescCompactAttr(tupleDesc, attnum);
896900

897901
targetDataLen = att_align_datum(targetDataLen,
898902
att->attalign,
@@ -1020,8 +1024,7 @@ expand_tuple(HeapTuple *targetHeapTuple,
10201024
/* Now fill in the missing values */
10211025
for (attnum = sourceNatts; attnum < natts; attnum++)
10221026
{
1023-
1024-
Form_pg_attribute attr = TupleDescAttr(tupleDesc, attnum);
1027+
CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, attnum);
10251028

10261029
if (attrmiss && attrmiss[attnum].am_present)
10271030
{
@@ -1370,7 +1373,7 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
13701373

13711374
for (attnum = 0; attnum < natts; attnum++)
13721375
{
1373-
Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
1376+
CompactAttribute *thisatt = TupleDescCompactAttr(tupleDesc, attnum);
13741377

13751378
if (hasnulls && att_isnull(attnum, bp))
13761379
{

src/backend/access/common/indextuple.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -303,13 +303,13 @@ nocache_index_getattr(IndexTuple tup,
303303

304304
if (!slow)
305305
{
306-
Form_pg_attribute att;
306+
CompactAttribute *att;
307307

308308
/*
309309
* If we get here, there are no nulls up to and including the target
310310
* attribute. If we have a cached offset, we can use it.
311311
*/
312-
att = TupleDescAttr(tupleDesc, attnum);
312+
att = TupleDescCompactAttr(tupleDesc, attnum);
313313
if (att->attcacheoff >= 0)
314314
return fetchatt(att, tp + att->attcacheoff);
315315

@@ -324,7 +324,7 @@ nocache_index_getattr(IndexTuple tup,
324324

325325
for (j = 0; j <= attnum; j++)
326326
{
327-
if (TupleDescAttr(tupleDesc, j)->attlen <= 0)
327+
if (TupleDescCompactAttr(tupleDesc, j)->attlen <= 0)
328328
{
329329
slow = true;
330330
break;
@@ -347,18 +347,18 @@ nocache_index_getattr(IndexTuple tup,
347347
* fixed-width columns, in hope of avoiding future visits to this
348348
* routine.
349349
*/
350-
TupleDescAttr(tupleDesc, 0)->attcacheoff = 0;
350+
TupleDescCompactAttr(tupleDesc, 0)->attcacheoff = 0;
351351

352352
/* we might have set some offsets in the slow path previously */
353-
while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0)
353+
while (j < natts && TupleDescCompactAttr(tupleDesc, j)->attcacheoff > 0)
354354
j++;
355355

356-
off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff +
357-
TupleDescAttr(tupleDesc, j - 1)->attlen;
356+
off = TupleDescCompactAttr(tupleDesc, j - 1)->attcacheoff +
357+
TupleDescCompactAttr(tupleDesc, j - 1)->attlen;
358358

359359
for (; j < natts; j++)
360360
{
361-
Form_pg_attribute att = TupleDescAttr(tupleDesc, j);
361+
CompactAttribute *att = TupleDescCompactAttr(tupleDesc, j);
362362

363363
if (att->attlen <= 0)
364364
break;
@@ -372,7 +372,7 @@ nocache_index_getattr(IndexTuple tup,
372372

373373
Assert(j > attnum);
374374

375-
off = TupleDescAttr(tupleDesc, attnum)->attcacheoff;
375+
off = TupleDescCompactAttr(tupleDesc, attnum)->attcacheoff;
376376
}
377377
else
378378
{
@@ -392,7 +392,7 @@ nocache_index_getattr(IndexTuple tup,
392392
off = 0;
393393
for (i = 0;; i++) /* loop exit is at "break" */
394394
{
395-
Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
395+
CompactAttribute *att = TupleDescCompactAttr(tupleDesc, i);
396396

397397
if (IndexTupleHasNulls(tup) && att_isnull(i, bp))
398398
{
@@ -440,7 +440,7 @@ nocache_index_getattr(IndexTuple tup,
440440
}
441441
}
442442

443-
return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off);
443+
return fetchatt(TupleDescCompactAttr(tupleDesc, attnum), tp + off);
444444
}
445445

446446
/*
@@ -490,7 +490,7 @@ index_deform_tuple_internal(TupleDesc tupleDescriptor,
490490

491491
for (attnum = 0; attnum < natts; attnum++)
492492
{
493-
Form_pg_attribute thisatt = TupleDescAttr(tupleDescriptor, attnum);
493+
CompactAttribute *thisatt = TupleDescCompactAttr(tupleDescriptor, attnum);
494494

495495
if (hasnulls && att_isnull(attnum, bp))
496496
{
@@ -588,7 +588,7 @@ index_truncate_tuple(TupleDesc sourceDescriptor, IndexTuple source,
588588
return CopyIndexTuple(source);
589589

590590
/* Create temporary descriptor to scribble on */
591-
truncdesc = palloc(TupleDescSize(sourceDescriptor));
591+
truncdesc = CreateTemplateTupleDesc(sourceDescriptor->natts);
592592
TupleDescCopy(truncdesc, sourceDescriptor);
593593
truncdesc->natts = leavenatts;
594594

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy