From 9c452649e7db8f8be87dcaf3da4bdee8fec588df Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Wed, 22 Jan 2020 18:00:38 -0800
Subject: [PATCH 1/2] Avoid pipeline stall in _bt_compare().

Author: Peter Geoghegan
Reviewed-By: Andres Freund, Floris Van Nee
Discussion: https://postgr.es/m/CAH2-Wzngz5MrkiTaZNny0GzfTxNQE+QWPPaO-C390BgruhgjEw@mail.gmail.com
---
 src/backend/access/nbtree/nbtsearch.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 7aaa8c17b0..98642cbccb 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -655,6 +655,7 @@ _bt_compare(Relation rel,
 	BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
 	IndexTuple	itup;
 	ItemPointer heapTid;
+	int			ski;
 	ScanKey		scankey;
 	int			ncmpkey;
 	int			ntupatts;
@@ -687,10 +688,12 @@ _bt_compare(Relation rel,
 	 */
 
 	ncmpkey = Min(ntupatts, key->keysz);
+	Assert(ntupatts >= 1);
 	Assert(key->heapkeyspace || ncmpkey == key->keysz);
 	Assert(!BTreeTupleIsPosting(itup) || key->allequalimage);
+	ski = 1;
 	scankey = key->scankeys;
-	for (int i = 1; i <= ncmpkey; i++)
+	for (;;)
 	{
 		Datum		datum;
 		bool		isNull;
@@ -736,7 +739,18 @@ _bt_compare(Relation rel,
 		if (result != 0)
 			return result;
 
+		/*
+		 * The loop is deliberately structured in a way that enables the
+		 * compiler to assume that the first iteration always runs.  Testing
+		 * has shown that this avoids a pipeline stall with certain
+		 * memory-bound workloads.  We delay this test, since it depends on
+		 * whether or not caller's tuple is a pivot tuple.  Typically, most
+		 * calls here never reach this far.
+		 */
+		ski++;
 		scankey++;
+		if (ski > ncmpkey)
+			break;
 	}
 
 	/*
-- 
2.25.0

