v1-0003-Experiment-without-a-dedicated-hasnulls-loop.patch

text/plain

Filename: v1-0003-Experiment-without-a-dedicated-hasnulls-loop.patch
Type: text/plain
Part: 5
Message: More speedups for tuple deformation
From 8064b149dbf465d8b17a8b2aed35b6b079262860 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
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