Small optimization set tuple block/tableOid once

Started by Ranier Vilela9 months ago3 messages
#1Ranier Vilela
ranier.vf@gmail.com
7 attachment(s)

Hi.

Inspired by [1]Re: AIO writes vs hint bits vs checksums </messages/by-id/lxzj26ga6ippdeunz6kuncectr5gfuugmm2ry22qu6hcx6oid6@lzx3sjsqhmt6&gt;
There is an opportunity for optimization according to the commit:
2904324 <http://2904324a88f672b2ecc22735279c16d6e1ee178c&gt;
" Due to splitting the block id into two 16 bit integers, BlockIdSet() is
more expensive than one might think. Doing it once per returned tuple shows
up as a small but reliably reproducible cost. It's simple enough to set the
block number just once per block in pagemode, so do so."

By moving the invariants out of the loop, it really is an improvement.

Then the following sources can be optimized:
contrib/pg_visibility/pg_visibility.c
contrib/pgstattuple/pgstatapprox.c
src/backend/access/heap/heapam.c
src/backend/access/heap/heapam_handler.c
src/backend/access/heap/pruneheap.c
src/backend/access/heap/vacuumlazy.c
src/backend/commands/dbcommands.c

Attached all patchs.

best regards,
Ranier Vilela

[1]: Re: AIO writes vs hint bits vs checksums </messages/by-id/lxzj26ga6ippdeunz6kuncectr5gfuugmm2ry22qu6hcx6oid6@lzx3sjsqhmt6&gt;
</messages/by-id/lxzj26ga6ippdeunz6kuncectr5gfuugmm2ry22qu6hcx6oid6@lzx3sjsqhmt6&gt;

Attachments:

pg_visibility_set_tuple_block_once.patchapplication/octet-stream; name=pg_visibility_set_tuple_block_once.patchDownload
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index d79ef35006..d0c73eaeb0 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -769,6 +769,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
 		OffsetNumber offnum,
 					maxoff;
 		BlockNumber blkno;
+		HeapTupleData tuple;
 
 		/* Make sure we are interruptible. */
 		CHECK_FOR_INTERRUPTS();
@@ -793,12 +794,15 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
 			continue;
 		}
 
+		/* block and tableOid is the same for all tuples, set it once outside the loop */
+		ItemPointerSetBlockNumber(&tuple.t_self, blkno);
+		tuple.t_tableOid = relid;
+
 		/* Iterate over each tuple on the page. */
 		for (offnum = FirstOffsetNumber;
 			 offnum <= maxoff;
 			 offnum = OffsetNumberNext(offnum))
 		{
-			HeapTupleData tuple;
 			ItemId		itemid;
 
 			itemid = PageGetItemId(page, offnum);
@@ -807,19 +811,18 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
 			if (!ItemIdIsUsed(itemid) || ItemIdIsRedirected(itemid))
 				continue;
 
+			ItemPointerSetOffsetNumber(&tuple.t_self, offnum);
+
 			/* Dead line pointers are neither all-visible nor frozen. */
 			if (ItemIdIsDead(itemid))
 			{
-				ItemPointerSet(&(tuple.t_self), blkno, offnum);
 				record_corrupt_item(items, &tuple.t_self);
 				continue;
 			}
 
 			/* Initialize a HeapTupleData structure for checks below. */
-			ItemPointerSet(&(tuple.t_self), blkno, offnum);
 			tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
 			tuple.t_len = ItemIdGetLength(itemid);
-			tuple.t_tableOid = relid;
 
 			/*
 			 * If we're checking whether the page is all-visible, we expect
pgstattuple_set_tuple_block_once.patchapplication/octet-stream; name=pgstattuple_set_tuple_block_once.patchDownload
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index a59ff4e9d4..26afe55c01 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -78,6 +78,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		OffsetNumber offnum,
 					maxoff;
 		Size		freespace;
+		HeapTupleData tuple;
 
 		CHECK_FOR_INTERRUPTS();
 
@@ -118,12 +119,15 @@ statapprox_heap(Relation rel, output_type *stat)
 		 */
 		maxoff = PageGetMaxOffsetNumber(page);
 
+		/* block and tableOid is the same for all tuples, set it once outside the loop */
+		ItemPointerSetBlockNumber(&tuple.t_self, blkno);
+		tuple.t_tableOid = RelationGetRelid(rel);
+
 		for (offnum = FirstOffsetNumber;
 			 offnum <= maxoff;
 			 offnum = OffsetNumberNext(offnum))
 		{
 			ItemId		itemid;
-			HeapTupleData tuple;
 
 			itemid = PageGetItemId(page, offnum);
 
@@ -135,11 +139,9 @@ statapprox_heap(Relation rel, output_type *stat)
 
 			Assert(ItemIdIsNormal(itemid));
 
-			ItemPointerSet(&(tuple.t_self), blkno, offnum);
-
 			tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
 			tuple.t_len = ItemIdGetLength(itemid);
-			tuple.t_tableOid = RelationGetRelid(rel);
+			ItemPointerSetOffsetNumber(&tuple.t_self, offnum);
 
 			/*
 			 * We follow VACUUM's lead in counting INSERT_IN_PROGRESS tuples
heapam_set_tuple_block_once.patchapplication/octet-stream; name=heapam_set_tuple_block_once.patchDownload
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index cedaa195cb..148a979699 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -508,13 +508,17 @@ page_collect_tuples(HeapScanDesc scan, Snapshot snapshot,
 					BlockNumber block, int lines,
 					bool all_visible, bool check_serializable)
 {
+	HeapTupleData loctup;
 	int			ntup = 0;
 	OffsetNumber lineoff;
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&loctup.t_self, block);
+	loctup.t_tableOid = RelationGetRelid(scan->rs_base.rs_rd);
+
 	for (lineoff = FirstOffsetNumber; lineoff <= lines; lineoff++)
 	{
 		ItemId		lpp = PageGetItemId(page, lineoff);
-		HeapTupleData loctup;
 		bool		valid;
 
 		if (!ItemIdIsNormal(lpp))
@@ -522,8 +526,7 @@ page_collect_tuples(HeapScanDesc scan, Snapshot snapshot,
 
 		loctup.t_data = (HeapTupleHeader) PageGetItem(page, lpp);
 		loctup.t_len = ItemIdGetLength(lpp);
-		loctup.t_tableOid = RelationGetRelid(scan->rs_base.rs_rd);
-		ItemPointerSet(&(loctup.t_self), block, lineoff);
+		ItemPointerSetOffsetNumber(&loctup.t_self, lineoff);
 
 		if (all_visible)
 			valid = true;
@@ -931,6 +934,10 @@ heapgettup(HeapScanDesc scan,
 
 		LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
 		page = heapgettup_start_page(scan, dir, &linesleft, &lineoff);
+
+		/* block is the same for all tuples, set it once outside the loop */
+		ItemPointerSetBlockNumber(&tuple->t_self, scan->rs_cblock);
+
 continue_page:
 
 		/*
@@ -950,7 +957,7 @@ continue_page:
 
 			tuple->t_data = (HeapTupleHeader) PageGetItem(page, lpp);
 			tuple->t_len = ItemIdGetLength(lpp);
-			ItemPointerSet(&(tuple->t_self), scan->rs_cblock, lineoff);
+			ItemPointerSetOffsetNumber(&tuple->t_self, lineoff);
 
 			visible = HeapTupleSatisfiesVisibility(tuple,
 												   scan->rs_base.rs_snapshot,
@@ -1766,6 +1773,10 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
 	Assert(TransactionIdIsValid(RecentXmin));
 	Assert(BufferGetBlockNumber(buffer) == blkno);
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&heapTuple->t_self, blkno);
+	heapTuple->t_tableOid = RelationGetRelid(relation);
+
 	/* Scan through possible multiple members of HOT-chain */
 	for (;;)
 	{
@@ -1800,8 +1811,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
 		 */
 		heapTuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
 		heapTuple->t_len = ItemIdGetLength(lp);
-		heapTuple->t_tableOid = RelationGetRelid(relation);
-		ItemPointerSet(&heapTuple->t_self, blkno, offnum);
+		ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
 
 		/*
 		 * Shouldn't see a HEAP_ONLY tuple at chain start.
heapam_handler_set_tuple_block_once.patchapplication/octet-stream; name=heapam_handler_set_tuple_block_once.patchDownload
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 24d3765aa2..5c42bce2de 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -1031,6 +1031,7 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
 							   TupleTableSlot *slot)
 {
 	HeapScanDesc hscan = (HeapScanDesc) scan;
+	HeapTuple	targtuple;
 	Page		targpage;
 	OffsetNumber maxoffset;
 	BufferHeapTupleTableSlot *hslot;
@@ -1038,14 +1039,18 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
 	Assert(TTS_IS_BUFFERTUPLE(slot));
 
 	hslot = (BufferHeapTupleTableSlot *) slot;
+	targtuple = &hslot->base.tupdata;
 	targpage = BufferGetPage(hscan->rs_cbuf);
 	maxoffset = PageGetMaxOffsetNumber(targpage);
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&targtuple->t_self, hscan->rs_cblock);
+	targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
+
 	/* Inner loop over all tuples on the selected page */
 	for (; hscan->rs_cindex <= maxoffset; hscan->rs_cindex++)
 	{
 		ItemId		itemid;
-		HeapTuple	targtuple = &hslot->base.tupdata;
 		bool		sample_it = false;
 
 		itemid = PageGetItemId(targpage, hscan->rs_cindex);
@@ -1063,11 +1068,9 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
 			continue;
 		}
 
-		ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);
-
-		targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
 		targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
 		targtuple->t_len = ItemIdGetLength(itemid);
+		ItemPointerSetOffsetNumber(&targtuple->t_self, hscan->rs_cindex);
 
 		switch (HeapTupleSatisfiesVacuum(targtuple, OldestXmin,
 										 hscan->rs_cbuf))
@@ -2291,6 +2294,7 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
 							  TupleTableSlot *slot)
 {
 	HeapScanDesc hscan = (HeapScanDesc) scan;
+	HeapTuple	tuple = &(hscan->rs_ctup);
 	TsmRoutine *tsm = scanstate->tsmroutine;
 	BlockNumber blockno = hscan->rs_cblock;
 	bool		pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
@@ -2311,6 +2315,9 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
 		!scan->rs_snapshot->takenDuringRecovery;
 	maxoffset = PageGetMaxOffsetNumber(page);
 
+	/* block is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&tuple->t_self, blockno);
+
 	for (;;)
 	{
 		OffsetNumber tupoffset;
@@ -2326,7 +2333,6 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
 		{
 			ItemId		itemid;
 			bool		visible;
-			HeapTuple	tuple = &(hscan->rs_ctup);
 
 			/* Skip invalid tuple pointers. */
 			itemid = PageGetItemId(page, tupoffset);
@@ -2335,8 +2341,7 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
 
 			tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
 			tuple->t_len = ItemIdGetLength(itemid);
-			ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
-
+			ItemPointerSetOffsetNumber(&tuple->t_self, blockno);
 
 			if (all_visible)
 				visible = true;
@@ -2578,18 +2583,21 @@ BitmapHeapScanNextBlock(TableScanDesc scan,
 		 * tbmres; but we have to follow any HOT chain starting at each such
 		 * offset.
 		 */
+		ItemPointerData tid;
 		int			curslot;
 
 		/* We must have extracted the tuple offsets by now */
 		Assert(noffsets > -1);
 
+		/* block is the same for all tuples, set it once outside the loop */
+		ItemPointerSetBlockNumber(&tid, block);
+
 		for (curslot = 0; curslot < noffsets; curslot++)
 		{
 			OffsetNumber offnum = offsets[curslot];
-			ItemPointerData tid;
 			HeapTupleData heapTuple;
 
-			ItemPointerSet(&tid, block, offnum);
+			ItemPointerSetOffsetNumber(&tid, offnum);
 			if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
 									   &heapTuple, NULL, true))
 				hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
@@ -2604,11 +2612,15 @@ BitmapHeapScanNextBlock(TableScanDesc scan,
 		Page		page = BufferGetPage(buffer);
 		OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
 		OffsetNumber offnum;
+		HeapTupleData loctup;
+
+		/* block and tableOid is the same for all tuples, set it once outside the loop */
+		ItemPointerSetBlockNumber(&loctup.t_self, block);
+		loctup.t_tableOid = scan->rs_rd->rd_id;
 
 		for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
 		{
 			ItemId		lp;
-			HeapTupleData loctup;
 			bool		valid;
 
 			lp = PageGetItemId(page, offnum);
@@ -2616,8 +2628,8 @@ BitmapHeapScanNextBlock(TableScanDesc scan,
 				continue;
 			loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
 			loctup.t_len = ItemIdGetLength(lp);
-			loctup.t_tableOid = scan->rs_rd->rd_id;
-			ItemPointerSet(&loctup.t_self, block, offnum);
+			ItemPointerSetOffsetNumber(&loctup.t_self, offnum);
+
 			valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
 			if (valid)
 			{
pruneheap_set_tuple_block_once.patchapplication/octet-stream; name=pruneheap_set_tuple_block_once.patchDownload
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index a8025889be..7bf73f4d93 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -467,6 +467,9 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
 	prstate.visibility_cutoff_xid = InvalidTransactionId;
 
 	maxoff = PageGetMaxOffsetNumber(page);
+
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&tup.t_self, blockno);
 	tup.t_tableOid = RelationGetRelid(relation);
 
 	/*
@@ -540,7 +543,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
 		htup = (HeapTupleHeader) PageGetItem(page, itemid);
 		tup.t_data = htup;
 		tup.t_len = ItemIdGetLength(itemid);
-		ItemPointerSet(&tup.t_self, blockno, offnum);
+		ItemPointerSetOffsetNumber(&tup.t_self, offnum);
 
 		prstate.htsv[offnum] = heap_prune_satisfies_vacuum(&prstate, &tup,
 														   buffer);
vacuumlazy_set_tuple_block_once.patchapplication/octet-stream; name=vacuumlazy_set_tuple_block_once.patchDownload
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index f28326bad0..175f83cf64 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -2249,6 +2249,7 @@ lazy_scan_noprune(LVRelState *vacrel,
 				missed_dead_tuples;
 	bool		hastup;
 	HeapTupleHeader tupleheader;
+	HeapTupleData tuple;
 	TransactionId NoFreezePageRelfrozenXid = vacrel->NewRelfrozenXid;
 	MultiXactId NoFreezePageRelminMxid = vacrel->NewRelminMxid;
 	OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
@@ -2262,13 +2263,16 @@ lazy_scan_noprune(LVRelState *vacrel,
 	recently_dead_tuples = 0;
 	missed_dead_tuples = 0;
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&tuple.t_self, blkno);
+	tuple.t_tableOid = RelationGetRelid(vacrel->rel);
+
 	maxoff = PageGetMaxOffsetNumber(page);
 	for (offnum = FirstOffsetNumber;
 		 offnum <= maxoff;
 		 offnum = OffsetNumberNext(offnum))
 	{
 		ItemId		itemid;
-		HeapTupleData tuple;
 
 		vacrel->offnum = offnum;
 		itemid = PageGetItemId(page, offnum);
@@ -2326,10 +2330,9 @@ lazy_scan_noprune(LVRelState *vacrel,
 			 */
 		}
 
-		ItemPointerSet(&(tuple.t_self), blkno, offnum);
 		tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
 		tuple.t_len = ItemIdGetLength(itemid);
-		tuple.t_tableOid = RelationGetRelid(vacrel->rel);
+		ItemPointerSetOffsetNumber(&tuple.t_self, offnum);
 
 		switch (HeapTupleSatisfiesVacuum(&tuple, vacrel->cutoffs.OldestXmin,
 										 buf))
@@ -3621,17 +3624,21 @@ heap_page_is_all_visible(LVRelState *vacrel, Buffer buf,
 	OffsetNumber offnum,
 				maxoff;
 	bool		all_visible = true;
+	HeapTupleData tuple;
 
 	*visibility_cutoff_xid = InvalidTransactionId;
 	*all_frozen = true;
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&tuple.t_self, blockno);
+	tuple.t_tableOid = RelationGetRelid(vacrel->rel);
+
 	maxoff = PageGetMaxOffsetNumber(page);
 	for (offnum = FirstOffsetNumber;
 		 offnum <= maxoff && all_visible;
 		 offnum = OffsetNumberNext(offnum))
 	{
 		ItemId		itemid;
-		HeapTupleData tuple;
 
 		/*
 		 * Set the offset number so that we can display it along with any
@@ -3644,8 +3651,6 @@ heap_page_is_all_visible(LVRelState *vacrel, Buffer buf,
 		if (!ItemIdIsUsed(itemid) || ItemIdIsRedirected(itemid))
 			continue;
 
-		ItemPointerSet(&(tuple.t_self), blockno, offnum);
-
 		/*
 		 * Dead line pointers can have index pointers pointing to them. So
 		 * they can't be treated as visible
@@ -3661,7 +3666,7 @@ heap_page_is_all_visible(LVRelState *vacrel, Buffer buf,
 
 		tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
 		tuple.t_len = ItemIdGetLength(itemid);
-		tuple.t_tableOid = RelationGetRelid(vacrel->rel);
+		ItemPointerSetOffsetNumber(&tuple.t_self, offnum);
 
 		switch (HeapTupleSatisfiesVacuum(&tuple, vacrel->cutoffs.OldestXmin,
 										 buf))
dbcommands_set_tuple_block_once.patchapplication/octet-stream; name=dbcommands_set_tuple_block_once.patchDownload
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 5fbbcdaabb..9ec07e52e0 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -337,6 +337,10 @@ ScanSourceDatabasePgClassPage(Page page, Buffer buf, Oid tbid, Oid dbid,
 
 	maxoff = PageGetMaxOffsetNumber(page);
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&tuple.t_self, blkno);
+	tuple.t_tableOid = RelationRelationId;
+
 	/* Loop over offsets. */
 	for (offnum = FirstOffsetNumber;
 		 offnum <= maxoff;
@@ -352,12 +356,11 @@ ScanSourceDatabasePgClassPage(Page page, Buffer buf, Oid tbid, Oid dbid,
 			continue;
 
 		Assert(ItemIdIsNormal(itemid));
-		ItemPointerSet(&(tuple.t_self), blkno, offnum);
 
 		/* Initialize a HeapTupleData structure. */
 		tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
 		tuple.t_len = ItemIdGetLength(itemid);
-		tuple.t_tableOid = RelationRelationId;
+		ItemPointerSetOffsetNumber(&tuple.t_self, offnum);
 
 		/* Skip tuples that are not visible to this snapshot. */
 		if (HeapTupleSatisfiesVisibility(&tuple, snapshot, buf))
#2Ranier Vilela
ranier.vf@gmail.com
In reply to: Ranier Vilela (#1)
2 attachment(s)
Re: Small optimization set tuple block/tableOid once

rebased heapam.c and heapam_handler.c

best regards,
Ranier Vilela

Attachments:

v1-heapam_set_tuple_block_once.patchapplication/octet-stream; name=v1-heapam_set_tuple_block_once.patchDownload
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index cedaa195cb..148a979699 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -508,13 +508,17 @@ page_collect_tuples(HeapScanDesc scan, Snapshot snapshot,
 					BlockNumber block, int lines,
 					bool all_visible, bool check_serializable)
 {
+	HeapTupleData loctup;
 	int			ntup = 0;
 	OffsetNumber lineoff;
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&loctup.t_self, block);
+	loctup.t_tableOid = RelationGetRelid(scan->rs_base.rs_rd);
+
 	for (lineoff = FirstOffsetNumber; lineoff <= lines; lineoff++)
 	{
 		ItemId		lpp = PageGetItemId(page, lineoff);
-		HeapTupleData loctup;
 		bool		valid;
 
 		if (!ItemIdIsNormal(lpp))
@@ -522,8 +526,7 @@ page_collect_tuples(HeapScanDesc scan, Snapshot snapshot,
 
 		loctup.t_data = (HeapTupleHeader) PageGetItem(page, lpp);
 		loctup.t_len = ItemIdGetLength(lpp);
-		loctup.t_tableOid = RelationGetRelid(scan->rs_base.rs_rd);
-		ItemPointerSet(&(loctup.t_self), block, lineoff);
+		ItemPointerSetOffsetNumber(&loctup.t_self, lineoff);
 
 		if (all_visible)
 			valid = true;
@@ -931,6 +934,10 @@ heapgettup(HeapScanDesc scan,
 
 		LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
 		page = heapgettup_start_page(scan, dir, &linesleft, &lineoff);
+
+		/* block is the same for all tuples, set it once outside the loop */
+		ItemPointerSetBlockNumber(&tuple->t_self, scan->rs_cblock);
+
 continue_page:
 
 		/*
@@ -950,7 +957,7 @@ continue_page:
 
 			tuple->t_data = (HeapTupleHeader) PageGetItem(page, lpp);
 			tuple->t_len = ItemIdGetLength(lpp);
-			ItemPointerSet(&(tuple->t_self), scan->rs_cblock, lineoff);
+			ItemPointerSetOffsetNumber(&tuple->t_self, lineoff);
 
 			visible = HeapTupleSatisfiesVisibility(tuple,
 												   scan->rs_base.rs_snapshot,
@@ -1766,6 +1773,10 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
 	Assert(TransactionIdIsValid(RecentXmin));
 	Assert(BufferGetBlockNumber(buffer) == blkno);
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&heapTuple->t_self, blkno);
+	heapTuple->t_tableOid = RelationGetRelid(relation);
+
 	/* Scan through possible multiple members of HOT-chain */
 	for (;;)
 	{
@@ -1800,8 +1811,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
 		 */
 		heapTuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
 		heapTuple->t_len = ItemIdGetLength(lp);
-		heapTuple->t_tableOid = RelationGetRelid(relation);
-		ItemPointerSet(&heapTuple->t_self, blkno, offnum);
+		ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
 
 		/*
 		 * Shouldn't see a HEAP_ONLY tuple at chain start.
v1-heapam_handler_set_tuple_block_once.patchapplication/octet-stream; name=v1-heapam_handler_set_tuple_block_once.patchDownload
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 24d3765aa2..5c42bce2de 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -1031,6 +1031,7 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
 							   TupleTableSlot *slot)
 {
 	HeapScanDesc hscan = (HeapScanDesc) scan;
+	HeapTuple	targtuple;
 	Page		targpage;
 	OffsetNumber maxoffset;
 	BufferHeapTupleTableSlot *hslot;
@@ -1038,14 +1039,18 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
 	Assert(TTS_IS_BUFFERTUPLE(slot));
 
 	hslot = (BufferHeapTupleTableSlot *) slot;
+	targtuple = &hslot->base.tupdata;
 	targpage = BufferGetPage(hscan->rs_cbuf);
 	maxoffset = PageGetMaxOffsetNumber(targpage);
 
+	/* block and tableOid is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&targtuple->t_self, hscan->rs_cblock);
+	targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
+
 	/* Inner loop over all tuples on the selected page */
 	for (; hscan->rs_cindex <= maxoffset; hscan->rs_cindex++)
 	{
 		ItemId		itemid;
-		HeapTuple	targtuple = &hslot->base.tupdata;
 		bool		sample_it = false;
 
 		itemid = PageGetItemId(targpage, hscan->rs_cindex);
@@ -1063,11 +1068,9 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
 			continue;
 		}
 
-		ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);
-
-		targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
 		targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
 		targtuple->t_len = ItemIdGetLength(itemid);
+		ItemPointerSetOffsetNumber(&targtuple->t_self, hscan->rs_cindex);
 
 		switch (HeapTupleSatisfiesVacuum(targtuple, OldestXmin,
 										 hscan->rs_cbuf))
@@ -2291,6 +2294,7 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
 							  TupleTableSlot *slot)
 {
 	HeapScanDesc hscan = (HeapScanDesc) scan;
+	HeapTuple	tuple = &(hscan->rs_ctup);
 	TsmRoutine *tsm = scanstate->tsmroutine;
 	BlockNumber blockno = hscan->rs_cblock;
 	bool		pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
@@ -2311,6 +2315,9 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
 		!scan->rs_snapshot->takenDuringRecovery;
 	maxoffset = PageGetMaxOffsetNumber(page);
 
+	/* block is the same for all tuples, set it once outside the loop */
+	ItemPointerSetBlockNumber(&tuple->t_self, blockno);
+
 	for (;;)
 	{
 		OffsetNumber tupoffset;
@@ -2326,7 +2333,6 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
 		{
 			ItemId		itemid;
 			bool		visible;
-			HeapTuple	tuple = &(hscan->rs_ctup);
 
 			/* Skip invalid tuple pointers. */
 			itemid = PageGetItemId(page, tupoffset);
@@ -2335,8 +2341,7 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
 
 			tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
 			tuple->t_len = ItemIdGetLength(itemid);
-			ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
-
+			ItemPointerSetOffsetNumber(&tuple->t_self, blockno);
 
 			if (all_visible)
 				visible = true;
@@ -2578,18 +2583,21 @@ BitmapHeapScanNextBlock(TableScanDesc scan,
 		 * tbmres; but we have to follow any HOT chain starting at each such
 		 * offset.
 		 */
+		ItemPointerData tid;
 		int			curslot;
 
 		/* We must have extracted the tuple offsets by now */
 		Assert(noffsets > -1);
 
+		/* block is the same for all tuples, set it once outside the loop */
+		ItemPointerSetBlockNumber(&tid, block);
+
 		for (curslot = 0; curslot < noffsets; curslot++)
 		{
 			OffsetNumber offnum = offsets[curslot];
-			ItemPointerData tid;
 			HeapTupleData heapTuple;
 
-			ItemPointerSet(&tid, block, offnum);
+			ItemPointerSetOffsetNumber(&tid, offnum);
 			if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
 									   &heapTuple, NULL, true))
 				hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
@@ -2604,11 +2612,15 @@ BitmapHeapScanNextBlock(TableScanDesc scan,
 		Page		page = BufferGetPage(buffer);
 		OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
 		OffsetNumber offnum;
+		HeapTupleData loctup;
+
+		/* block and tableOid is the same for all tuples, set it once outside the loop */
+		ItemPointerSetBlockNumber(&loctup.t_self, block);
+		loctup.t_tableOid = scan->rs_rd->rd_id;
 
 		for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
 		{
 			ItemId		lp;
-			HeapTupleData loctup;
 			bool		valid;
 
 			lp = PageGetItemId(page, offnum);
@@ -2616,8 +2628,8 @@ BitmapHeapScanNextBlock(TableScanDesc scan,
 				continue;
 			loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
 			loctup.t_len = ItemIdGetLength(lp);
-			loctup.t_tableOid = scan->rs_rd->rd_id;
-			ItemPointerSet(&loctup.t_self, block, offnum);
+			ItemPointerSetOffsetNumber(&loctup.t_self, offnum);
+
 			valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
 			if (valid)
 			{
#3David Rowley
dgrowleyml@gmail.com
In reply to: Ranier Vilela (#2)
Re: Small optimization set tuple block/tableOid once

On Thu, 3 Apr 2025 at 23:09, Ranier Vilela <ranier.vf@gmail.com> wrote:

rebased heapam.c and heapam_handler.c

This doesn't work. The tids are not being set in all cases.

patched:
select tableoid,ctid,oid from pg_class TABLESAMPLE BERNOULLI (100);
tableoid | ctid | oid
----------+---------+-------
1259 | (0,0) | 16409
1259 | (0,0) | 16403
1259 | (0,0) | 16412
1259 | (0,0) | 16407
1259 | (0,0) | 16415
1259 | (0,0) | 16418
1259 | (0,0) | 16434
...

master:
tableoid | ctid | oid
----------+---------+-------
1259 | (0,10) | 16409
1259 | (0,11) | 16403
1259 | (0,12) | 16412
1259 | (0,13) | 16407
1259 | (0,15) | 16415
1259 | (0,16) | 16418
1259 | (0,17) | 16434
...

David