Skip to content

Commit 5983a4c

Browse files
committed
Introduce CompactAttribute array in TupleDesc, take 2
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. 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 Reviewed-by: Andres Freund, Victor Yegorov Discussion: https://postgr.es/m/CAApHDvrBztXP3yx=NKNmo3xwFAFhEdyPnvrDg3=M0RhDs+4vYw@mail.gmail.com
1 parent 8ac0021 commit 5983a4c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+374
-154
lines changed

contrib/amcheck/verify_heapam.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1571,11 +1571,11 @@ check_tuple_attribute(HeapCheckContext *ctx)
15711571
struct varlena *attr;
15721572
char *tp; /* pointer to the tuple data */
15731573
uint16 infomask;
1574-
Form_pg_attribute thisatt;
1574+
CompactAttribute *thisatt;
15751575
struct varatt_external toast_pointer;
15761576

15771577
infomask = ctx->tuphdr->t_infomask;
1578-
thisatt = TupleDescAttr(RelationGetDescr(ctx->rel), ctx->attnum);
1578+
thisatt = TupleDescCompactAttr(RelationGetDescr(ctx->rel), ctx->attnum);
15791579

15801580
tp = (char *) ctx->tuphdr + ctx->tuphdr->t_hoff;
15811581

contrib/pageinspect/gistfuncs.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,8 @@ gist_page_items(PG_FUNCTION_ARGS)
242242
}
243243
else
244244
{
245-
tupdesc = CreateTupleDescCopy(RelationGetDescr(indexRel));
246-
tupdesc->natts = IndexRelationGetNumberOfKeyAttributes(indexRel);
245+
tupdesc = CreateTupleDescTruncatedCopy(RelationGetDescr(indexRel),
246+
IndexRelationGetNumberOfKeyAttributes(indexRel));
247247
printflags |= RULE_INDEXDEF_KEYS_ONLY;
248248
}
249249

contrib/pageinspect/heapfuncs.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,11 @@ tuple_data_split_internal(Oid relid, char *tupdata,
334334

335335
for (i = 0; i < nattrs; i++)
336336
{
337-
Form_pg_attribute attr;
337+
CompactAttribute *attr;
338338
bool is_null;
339339
bytea *attr_data = NULL;
340340

341-
attr = TupleDescAttr(tupdesc, i);
341+
attr = TupleDescCompactAttr(tupdesc, i);
342342

343343
/*
344344
* Tuple header can specify fewer attributes than tuple descriptor as

contrib/postgres_fdw/postgres_fdw.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -1818,7 +1818,7 @@ postgresPlanForeignModify(PlannerInfo *root,
18181818

18191819
for (attnum = 1; attnum <= tupdesc->natts; attnum++)
18201820
{
1821-
Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
1821+
CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
18221822

18231823
if (!attr->attisdropped)
18241824
targetAttrs = lappend_int(targetAttrs, attnum);
@@ -2191,7 +2191,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
21912191
/* We transmit all columns that are defined in the foreign table. */
21922192
for (attnum = 1; attnum <= tupdesc->natts; attnum++)
21932193
{
2194-
Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2194+
CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
21952195

21962196
if (!attr->attisdropped)
21972197
targetAttrs = lappend_int(targetAttrs, attnum);
@@ -4311,7 +4311,7 @@ convert_prep_stmt_params(PgFdwModifyState *fmstate,
43114311
foreach(lc, fmstate->target_attrs)
43124312
{
43134313
int attnum = lfirst_int(lc);
4314-
Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
4314+
CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1);
43154315
Datum value;
43164316
bool isnull;
43174317

src/backend/access/brin/brin_inclusion.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,12 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS)
146146
Datum result;
147147
bool new = false;
148148
AttrNumber attno;
149-
Form_pg_attribute attr;
149+
CompactAttribute *attr;
150150

151151
Assert(!isnull);
152152

153153
attno = column->bv_attno;
154-
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
154+
attr = TupleDescCompactAttr(bdesc->bd_tupdesc, attno - 1);
155155

156156
/*
157157
* If the recorded value is null, copy the new value (which we know to be
@@ -479,15 +479,15 @@ brin_inclusion_union(PG_FUNCTION_ARGS)
479479
BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
480480
Oid colloid = PG_GET_COLLATION();
481481
AttrNumber attno;
482-
Form_pg_attribute attr;
482+
CompactAttribute *attr;
483483
FmgrInfo *finfo;
484484
Datum result;
485485

486486
Assert(col_a->bv_attno == col_b->bv_attno);
487487
Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
488488

489489
attno = col_a->bv_attno;
490-
attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
490+
attr = TupleDescCompactAttr(bdesc->bd_tupdesc, attno - 1);
491491

492492
/* If B includes empty elements, mark A similarly, if needed. */
493493
if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&

src/backend/access/brin/brin_tuple.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ brin_deconstruct_tuple(BrinDesc *brdesc,
699699
datumno < brdesc->bd_info[attnum]->oi_nstored;
700700
datumno++)
701701
{
702-
Form_pg_attribute thisatt = TupleDescAttr(diskdsc, stored);
702+
CompactAttribute *thisatt = TupleDescCompactAttr(diskdsc, stored);
703703

704704
if (thisatt->attlen == -1)
705705
{

src/backend/access/common/attmap.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ build_attrmap_by_position(TupleDesc indesc,
135135
/* Check for unused input columns */
136136
for (; j < indesc->natts; j++)
137137
{
138-
if (TupleDescAttr(indesc, j)->attisdropped)
138+
if (TupleDescCompactAttr(indesc, j)->attisdropped)
139139
continue;
140140
nincols++;
141141
same = false; /* we'll complain below */
@@ -299,8 +299,8 @@ check_attrmap_match(TupleDesc indesc,
299299

300300
for (i = 0; i < attrMap->maplen; i++)
301301
{
302-
Form_pg_attribute inatt = TupleDescAttr(indesc, i);
303-
Form_pg_attribute outatt = TupleDescAttr(outdesc, i);
302+
CompactAttribute *inatt = TupleDescCompactAttr(indesc, i);
303+
CompactAttribute *outatt;
304304

305305
/*
306306
* If the input column has a missing attribute, we need a conversion.
@@ -311,6 +311,8 @@ check_attrmap_match(TupleDesc indesc,
311311
if (attrMap->attnums[i] == (i + 1))
312312
continue;
313313

314+
outatt = TupleDescCompactAttr(outdesc, i);
315+
314316
/*
315317
* If it's a dropped column and the corresponding input column is also
316318
* dropped, we don't need a conversion. However, attlen and attalign

src/backend/access/common/heaptuple.c

+28-25
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
{

0 commit comments

Comments
 (0)