]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Reduce size of CompactAttribute struct to 8 bytes
authorDavid Rowley <drowley@postgresql.org>
Tue, 17 Mar 2026 02:06:31 +0000 (15:06 +1300)
committerDavid Rowley <drowley@postgresql.org>
Tue, 17 Mar 2026 02:06:31 +0000 (15:06 +1300)
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 <dgrowleyml@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Álvaro Herrera <alvherre@kurilemu.de>
Discussion: https://postgr.es/m/CAApHDvodSVBj3ypOYbYUCJX%2BNWL%3DVZs63RNBQ_FxB_F%2B6QXF-A%40mail.gmail.com

src/backend/access/common/tupdesc.c
src/backend/executor/execTuples.c
src/include/access/tupdesc.h

index c68561337d7f0bab1430da800b1add240fcac292..d771a265b34168d7ee4b1e744cc20f258b546c03 100644 (file)
@@ -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;
index 94f644cd7b3270a96dc940f863c63f0818233415..b717b03b3d2c68e24e3a72b3439877b57b921ca7 100644 (file)
@@ -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 */
index fd0d1b2d532de949c565f3bb8b75b0a837cd935f..62ef6b384973b4a80f56f6b89ca85bbf14f43844 100644 (file)
@@ -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 */