From 8064b149dbf465d8b17a8b2aed35b6b079262860 Mon Sep 17 00:00:00 2001 From: David Rowley Date: Sat, 27 Dec 2025 20:54:16 +1300 Subject: [PATCH v1 3/3] Experiment without a dedicated !hasnulls loop This makes the code smaller and seems to make some tests go faster --- src/backend/executor/execTuples.c | 76 ++++++++++--------------------- 1 file changed, 24 insertions(+), 52 deletions(-) diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 18e7db12dab..d6e9c91adaa 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1050,7 +1050,7 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, else { bp = NULL; - nextNullAttr = natts; + nextNullSeqEnd = nextNullAttr = natts; } #ifdef OPTIMIZE_BYVAL @@ -1129,15 +1129,21 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, off = *offp; } - /* Handle the remaining part of the tuple. */ - if (!hasnulls) + /* + * Handle the remaining part of the tuple. Rather than going to the + * trouble of calling att_isnull(), we instead do some processing on the + * bit mask to find the next NULL bit and how many follow that then + * process using two loops, the first of the inner loops here never sees a + * NULL attribute as the loop will end before we get to a NULL attr, the + * 2nd loop takes over and processes all the NULLs and we'll go back to + * the first loop and handle any remaining non-NULL attributes. + */ + for (;;) { - /* - * If there are no NULLs before natts, then use a simple loop without - * NULL handling. - */ - for (; attnum < natts; attnum++) + for (; attnum < nextNullAttr; attnum++) { + Assert(!att_isnull(attnum, bp)); + cattr = TupleDescCompactAttr(tupleDesc, attnum); /* align the offset for this attribute */ @@ -1152,53 +1158,19 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, /* move the offset beyond this attribute */ off = att_addlength_pointer(off, cattr->attlen, tp + off); } - } - else - { - /* - * Otherwise, we need to handle NULLs. Rather than going to the - * trouble of calling att_isnull(), we instead do some processing on - * the bit mask to find the next NULL bit and how many follow that - * then process using two loops, the first of the inner loops here - * never sees a NULL attribute as the loop will end before we get to a - * NULL attr, the 2nd loop takes over and processes all the NULLs and - * we'll go back to the first loop and handle any remaining non-NULL - * attributes. - */ - for (;;) - { - for (; attnum < nextNullAttr; attnum++) - { - Assert(!att_isnull(attnum, bp)); - cattr = TupleDescCompactAttr(tupleDesc, attnum); - - /* align the offset for this attribute */ - off = att_pointer_alignby(off, - cattr->attalignby, - cattr->attlen, - tp + off); - - values[attnum] = fetchatt(cattr, tp + off); - isnull[attnum] = false; - - /* move the offset beyond this attribute */ - off = att_addlength_pointer(off, cattr->attlen, tp + off); - } - - if (likely(attnum == natts)) - break; - - /* Handle the NULLs */ - for (; unlikely(attnum < nextNullSeqEnd); attnum++) - { - Assert(att_isnull(attnum, bp)); - isnull[attnum] = true; - } + if (likely(attnum == natts)) + break; - /* Locate the next NULL, if any */ - next_null_until(bp, attnum, natts, &nextNullAttr, &nextNullSeqEnd); + /* Handle the NULLs */ + for (; unlikely(attnum < nextNullSeqEnd); attnum++) + { + Assert(att_isnull(attnum, bp)); + isnull[attnum] = true; } + + /* Locate the next NULL, if any */ + next_null_until(bp, attnum, natts, &nextNullAttr, &nextNullSeqEnd); } /* -- 2.43.0