Reduce size of CompactAttribute struct to 8 bytes

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
This commit is contained in:
David Rowley 2026-03-17 15:06:31 +13:00
parent d927b4bd97
commit d8a859d22b
3 changed files with 31 additions and 14 deletions

View 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;

View 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 */

View 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 */