From 29b999df643e8ad52f90f07c11755dfe710f2e5c Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Fri, 19 Sep 2025 06:58:29 +0800
Subject: [PATCH v1] Optimize multiplications/divisions by 2 using bit shifts
 in hot paths

Replace occurrences of "* 2" with "<< 1" and "/ 2" with ">> 1" in
functions along critical execution paths. These changes are safe because:

  - For unsigned types, left/right shifts by 1 are defined to behave
    exactly like multiply/divide by 2, modulo wraparound semantics that
    already apply to unsigned arithmetic.
  - For signed integer types, only non-negative values are affected by
    these operations, ensuring that "x >> 1" is equivalent to "x / 2".
    Negative values are not encountered in these code paths.

This avoids unnecessary arithmetic instructions and makes the intent
explicit, while preserving correctness.

Author: Chao Li <lic@highgo.com>
---
 src/backend/access/gin/ginbulk.c           |  2 +-
 src/backend/access/gin/ginfast.c           |  2 +-
 src/backend/access/gin/ginpostinglist.c    |  4 ++--
 src/backend/access/gin/ginscan.c           |  2 +-
 src/backend/access/gist/gistbuildbuffers.c |  4 ++--
 src/backend/access/nbtree/nbtinsert.c      |  2 +-
 src/backend/access/nbtree/nbtutils.c       |  4 ++--
 src/backend/executor/nodeHash.c            | 12 ++++++------
 8 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/src/backend/access/gin/ginbulk.c b/src/backend/access/gin/ginbulk.c
index 302cb2092a9..22bed52de5b 100644
--- a/src/backend/access/gin/ginbulk.c
+++ b/src/backend/access/gin/ginbulk.c
@@ -45,7 +45,7 @@ ginCombineData(RBTNode *existing, const RBTNode *newdata, void *arg)
 					 errhint("Reduce \"maintenance_work_mem\".")));
 
 		accum->allocatedMemory -= GetMemoryChunkSpace(eo->list);
-		eo->maxcount *= 2;
+		eo->maxcount <<= 1;
 		eo->list = (ItemPointerData *)
 			repalloc_huge(eo->list, sizeof(ItemPointerData) * eo->maxcount);
 		accum->allocatedMemory += GetMemoryChunkSpace(eo->list);
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index a6d88572cc2..6020aedaf67 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -686,7 +686,7 @@ addDatum(KeyArray *keys, Datum datum, GinNullCategory category)
 {
 	if (keys->nvalues >= keys->maxvalues)
 	{
-		keys->maxvalues *= 2;
+		keys->maxvalues <<= 1;
 		keys->keys = repalloc_array(keys->keys, Datum, keys->maxvalues);
 		keys->categories = repalloc_array(keys->categories, GinNullCategory, keys->maxvalues);
 	}
diff --git a/src/backend/access/gin/ginpostinglist.c b/src/backend/access/gin/ginpostinglist.c
index 48eadec87b0..89f0f2eeeed 100644
--- a/src/backend/access/gin/ginpostinglist.c
+++ b/src/backend/access/gin/ginpostinglist.c
@@ -316,7 +316,7 @@ ginPostingListDecodeAllSegments(GinPostingList *segment, int len, int *ndecoded_
 		/* enlarge output array if needed */
 		if (ndecoded >= nallocated)
 		{
-			nallocated *= 2;
+			nallocated <<= 1;
 			result = repalloc(result, nallocated * sizeof(ItemPointerData));
 		}
 
@@ -334,7 +334,7 @@ ginPostingListDecodeAllSegments(GinPostingList *segment, int len, int *ndecoded_
 			/* enlarge output array if needed */
 			if (ndecoded >= nallocated)
 			{
-				nallocated *= 2;
+				nallocated <<= 1;
 				result = repalloc(result, nallocated * sizeof(ItemPointerData));
 			}
 
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 26081693383..5b21d3e89f3 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -122,7 +122,7 @@ ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum,
 	/* Add it to so's array */
 	if (so->totalentries >= so->allocentries)
 	{
-		so->allocentries *= 2;
+		so->allocentries <<= 1;
 		so->entries = (GinScanEntry *)
 			repalloc(so->entries, so->allocentries * sizeof(GinScanEntry));
 	}
diff --git a/src/backend/access/gist/gistbuildbuffers.c b/src/backend/access/gist/gistbuildbuffers.c
index 0707254d18e..9a62110a1b2 100644
--- a/src/backend/access/gist/gistbuildbuffers.c
+++ b/src/backend/access/gist/gistbuildbuffers.c
@@ -204,7 +204,7 @@ gistAddLoadedBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
 	/* Enlarge the array if needed */
 	if (gfbb->loadedBuffersCount >= gfbb->loadedBuffersLen)
 	{
-		gfbb->loadedBuffersLen *= 2;
+		gfbb->loadedBuffersLen <<= 1;
 		gfbb->loadedBuffers = (GISTNodeBuffer **)
 			repalloc(gfbb->loadedBuffers,
 					 gfbb->loadedBuffersLen * sizeof(GISTNodeBuffer *));
@@ -489,7 +489,7 @@ gistBuffersReleaseBlock(GISTBuildBuffers *gfbb, long blocknum)
 	/* Enlarge freeBlocks array if full. */
 	if (gfbb->nFreeBlocks >= gfbb->freeBlocksLen)
 	{
-		gfbb->freeBlocksLen *= 2;
+		gfbb->freeBlocksLen <<= 1;
 		gfbb->freeBlocks = (long *) repalloc(gfbb->freeBlocks,
 											 gfbb->freeBlocksLen *
 											 sizeof(long));
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index be60781fc98..85d1ee26553 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -2972,7 +2972,7 @@ _bt_deadblocks(Page page, OffsetNumber *deletable, int ndeletable,
 		{
 			if (ntids + 1 > spacentids)
 			{
-				spacentids *= 2;
+				spacentids <<= 1;
 				tidblocks = (BlockNumber *)
 					repalloc(tidblocks, sizeof(BlockNumber) * spacentids);
 			}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 41b4fbd1c37..6a33c2652d4 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -3289,8 +3289,8 @@ _bt_checkkeys_look_ahead(IndexScanDesc scan, BTReadPageState *pstate,
 	 */
 	if (!pstate->targetdistance)
 		pstate->targetdistance = LOOK_AHEAD_DEFAULT_DISTANCE;
-	else if (pstate->targetdistance < MaxIndexTuplesPerPage / 2)
-		pstate->targetdistance *= 2;
+	else if (pstate->targetdistance < MaxIndexTuplesPerPage >> 1)
+		pstate->targetdistance <<= 1;
 
 	/* Don't read past the end (or before the start) of the page, though */
 	if (ScanDirectionIsForward(dir))
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index a3415db4e20..b34de4e8ce4 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -925,10 +925,10 @@ ExecChooseHashTableSize(double ntuples, int tupwidth, bool useskew,
 		 * It's better to use half the batches, so do that and adjust the
 		 * nbucket in the opposite direction, and double the allowance.
 		 */
-		nbatch /= 2;
-		nbuckets *= 2;
+		nbatch >>= 1;
+		nbuckets <<= 1;
 
-		*space_allowed = (*space_allowed) * 2;
+		*space_allowed = (*space_allowed) << 1;
 	}
 
 	Assert(nbuckets > 0);
@@ -1791,10 +1791,10 @@ ExecHashTableInsert(HashJoinTable hashtable,
 			ntuples > (hashtable->nbuckets_optimal * NTUP_PER_BUCKET))
 		{
 			/* Guard against integer overflow and alloc size overflow */
-			if (hashtable->nbuckets_optimal <= INT_MAX / 2 &&
-				hashtable->nbuckets_optimal * 2 <= MaxAllocSize / sizeof(HashJoinTuple))
+			if (hashtable->nbuckets_optimal <= INT_MAX >> 1 &&
+				hashtable->nbuckets_optimal << 1 <= MaxAllocSize / sizeof(HashJoinTuple))
 			{
-				hashtable->nbuckets_optimal *= 2;
+				hashtable->nbuckets_optimal <<= 1;
 				hashtable->log2_nbuckets_optimal += 1;
 			}
 		}
-- 
2.39.5 (Apple Git-154)

