From d8a859d22b1accd8ea749394a488e4de014b2396 Mon Sep 17 00:00:00 2001 From: David Rowley Date: Tue, 17 Mar 2026 15:06:31 +1300 Subject: [PATCH] Reduce size of CompactAttribute struct to 8 bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, this was 16 bytes. With the use of some bitflags and by reducing the attcacheoff field size to a 16-bit type, we can halve the size of the struct. It's unlikely that caching the offsets for offsets larger than what will fit in a 16-bit int will help much as the tuple is very likely to have some non-fixed-width types anyway, the offsets of which we cannot cache. Shrinking this down to 8 bytes helps by accessing fewer cachelines when performing tuple deformation. The fields used there are all fully fledged fields, which don't require any bitmasking to extract the value of. It also helps to more efficiently calculate the address of a compact_attrs[] element in TupleDesc as the x86 LEA instruction can work with 8 byte offsets, which allows the element address to be calculated from the TupleDesc's address in a single instruction using LEA's concurrent shift and add. Author: David Rowley Reviewed-by: Andres Freund Reviewed-by: Álvaro Herrera Discussion: https://postgr.es/m/CAApHDvodSVBj3ypOYbYUCJX%2BNWL%3DVZs63RNBQ_FxB_F%2B6QXF-A%40mail.gmail.com --- src/backend/access/common/tupdesc.c | 12 +++++++++++- src/backend/executor/execTuples.c | 17 ++++++++++++----- src/include/access/tupdesc.h | 16 ++++++++-------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index c68561337d7..d771a265b34 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -530,7 +530,17 @@ TupleDescFinalize(TupleDesc tupdesc) off = att_nominal_alignby(off, cattr->attalignby); - cattr->attcacheoff = off; + /* + * attcacheoff is an int16, so don't try to cache any offsets larger + * than will fit in that type. Any attributes which are offset more + * than 2^15 are likely due to variable-length attributes. Since we + * don't cache offsets for or beyond variable-length attributes, using + * an int16 rather than an int32 here is unlikely to cost us anything. + */ + if (off > PG_INT16_MAX) + break; + + cattr->attcacheoff = (int16) off; off += cattr->attlen; firstNonCachedOffsetAttr = i + 1; diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 94f644cd7b3..b717b03b3d2 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1013,6 +1013,7 @@ static pg_attribute_always_inline void slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, int reqnatts) { + CompactAttribute *cattrs; CompactAttribute *cattr; TupleDesc tupleDesc = slot->tts_tupleDescriptor; HeapTupleHeader tup = tuple->t_data; @@ -1095,6 +1096,13 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, values = slot->tts_values; slot->tts_nvalid = reqnatts; + /* + * We store the tupleDesc's CompactAttribute array in 'cattrs' as gcc + * seems to be unwilling to optimize accessing the CompactAttribute + * element efficiently when accessing it via TupleDescCompactAttr(). + */ + cattrs = tupleDesc->compact_attrs; + /* Ensure we calculated tp correctly */ Assert(tp == (char *) tup + tup->t_hoff); @@ -1105,7 +1113,7 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, do { isnull[attnum] = false; - cattr = TupleDescCompactAttr(tupleDesc, attnum); + cattr = &cattrs[attnum]; attlen = cattr->attlen; /* We don't expect any non-byval types */ @@ -1150,9 +1158,8 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, do { isnull[attnum] = false; - cattr = TupleDescCompactAttr(tupleDesc, attnum); + cattr = &cattrs[attnum]; attlen = cattr->attlen; - off = cattr->attcacheoff; values[attnum] = fetch_att_noerr(tp + off, cattr->attbyval, @@ -1179,7 +1186,7 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, int attlen; isnull[attnum] = false; - cattr = TupleDescCompactAttr(tupleDesc, attnum); + cattr = &cattrs[attnum]; attlen = cattr->attlen; /* @@ -1212,7 +1219,7 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, continue; } - cattr = TupleDescCompactAttr(tupleDesc, attnum); + cattr = &cattrs[attnum]; attlen = cattr->attlen; /* As above, we don't expect cstrings */ diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index fd0d1b2d532..62ef6b38497 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -55,7 +55,7 @@ typedef struct TupleConstr * directly after the FormData_pg_attribute struct is populated or * altered in any way. * - * Currently, this struct is 16 bytes. Any code changes which enlarge this + * Currently, this struct is 8 bytes. Any code changes which enlarge this * struct should be considered very carefully. * * Code which must access a TupleDesc's attribute data should always make use @@ -67,17 +67,17 @@ typedef struct TupleConstr */ typedef struct CompactAttribute { - int32 attcacheoff; /* fixed offset into tuple, if known, or -1 */ + int16 attcacheoff; /* fixed offset into tuple, if known, or -1 */ int16 attlen; /* attr len in bytes or -1 = varlen, -2 = * cstring */ bool attbyval; /* as FormData_pg_attribute.attbyval */ - bool attispackable; /* FormData_pg_attribute.attstorage != - * TYPSTORAGE_PLAIN */ - bool atthasmissing; /* as FormData_pg_attribute.atthasmissing */ - bool attisdropped; /* as FormData_pg_attribute.attisdropped */ - bool attgenerated; /* FormData_pg_attribute.attgenerated != '\0' */ - char attnullability; /* status of not-null constraint, see below */ uint8 attalignby; /* alignment requirement in bytes */ + bool attispackable:1; /* FormData_pg_attribute.attstorage != + * TYPSTORAGE_PLAIN */ + bool atthasmissing:1; /* as FormData_pg_attribute.atthasmissing */ + bool attisdropped:1; /* as FormData_pg_attribute.attisdropped */ + bool attgenerated:1; /* FormData_pg_attribute.attgenerated != '\0' */ + char attnullability; /* status of not-null constraint, see below */ } CompactAttribute; /* Valid values for CompactAttribute->attnullability */