From f2696dc29bdb46de62e8aab3cf4685fe53de9f3a Mon Sep 17 00:00:00 2001
From: Sokolov Yura <funny.falcon@postgrespro.ru>
Date: Mon, 10 Jul 2017 12:34:48 +0000
Subject: [PATCH] Make hash table for xip in XidInMVCCSnapshot

---
 src/backend/storage/ipc/procarray.c | 21 +++++++++-
 src/backend/utils/time/tqual.c      | 81 +++++++++++++++++++++++++++----------
 src/include/access/transam.h        |  5 +++
 src/include/utils/snapshot.h        |  3 ++
 4 files changed, 87 insertions(+), 23 deletions(-)

diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index a7e8cf2..2a5e3ac 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -1533,23 +1533,34 @@ GetSnapshotData(Snapshot snapshot)
 	 */
 	if (snapshot->xip == NULL)
 	{
+		uint32 cap, max;
 		/*
 		 * First call for this snapshot. Snapshot is same size whether or not
 		 * we are in recovery, see later comments.
 		 */
+		max = GetMaxSnapshotXidCount();
+		cap = 1;
+		while (cap < max * 2) cap *= 2;
 		snapshot->xip = (TransactionId *)
-			malloc(GetMaxSnapshotXidCount() * sizeof(TransactionId));
+			malloc((max + cap + 1) * sizeof(TransactionId));
 		if (snapshot->xip == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_OUT_OF_MEMORY),
 					 errmsg("out of memory")));
+		snapshot->xiphash = snapshot->xip + max;
+		snapshot->xiphash[0] = cap - 1;
 		Assert(snapshot->subxip == NULL);
+		max = GetMaxSnapshotSubxidCount();
+		cap = 1;
+		while (cap < max * 2) cap *= 2;
 		snapshot->subxip = (TransactionId *)
-			malloc(GetMaxSnapshotSubxidCount() * sizeof(TransactionId));
+			malloc((max + cap + 1) * sizeof(TransactionId));
 		if (snapshot->subxip == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_OUT_OF_MEMORY),
 					 errmsg("out of memory")));
+		snapshot->subxiphash = snapshot->subxip + max;
+		snapshot->subxiphash[0] = cap - 1;
 	}
 
 	/*
@@ -1757,6 +1768,12 @@ GetSnapshotData(Snapshot snapshot)
 	snapshot->active_count = 0;
 	snapshot->regd_count = 0;
 	snapshot->copied = false;
+	if (snapshot->xiphash != NULL)
+	{
+		/* set "unbuilt" bit */
+		snapshot->xiphash[0] |= SnapshotHashNotBuilt;
+		snapshot->subxiphash[0] |= SnapshotHashNotBuilt;
+	}
 
 	if (old_snapshot_threshold < 0)
 	{
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index f9da9e1..fdf0a1c 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -1450,6 +1450,59 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
 	return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
 }
 
+static bool
+XidInXip(TransactionId xid, TransactionId *xip, TransactionId *xiphash, uint32 xcnt)
+{
+	uint32 i = 0;
+	uint32 j, k, d, mask;
+	if (!TransactionIdIsNormal(xid))
+		return false;
+	if (xcnt < 60 || xiphash == NULL)
+	{
+		/* full scan for small snapshots and if xiphash is not allocated */
+		for (; i < xcnt; i++)
+			if (TransactionIdEquals(xid, xip[i]))
+				return true;
+		return false;
+	}
+	mask = xiphash[0];
+	xiphash += 1;
+	if ((mask & SnapshotHashNotBuilt) == 0)
+	{
+		d = xid >> 6;
+		j = k = xid & mask;
+		while (xiphash[j] != InvalidTransactionId)
+		{
+			if (TransactionIdEquals(xiphash[j], xid))
+				return true;
+			j = (k + d) & mask;
+			d = d*5 + 1;
+		}
+		return false;
+	}
+	else
+	{
+		/* build hash */
+		int found = 0;
+		mask &= ~SnapshotHashNotBuilt;
+		xiphash[-1] = mask;
+		memset(xiphash, 0, (mask + 1) * sizeof(TransactionId));
+		for (i = 0; i < xcnt; i++)
+		{
+			found += TransactionIdEquals(xip[i], xid);
+			d = xip[i] >> 6;
+			j = k = xip[i] & mask;
+			while (xiphash[j] != InvalidTransactionId)
+			{
+				j = (k + d) & mask;
+				d = d*5 + 1;
+			}
+			xiphash[j] = xip[i];
+		}
+		return found != 0;
+	}
+}
+
 /*
  * XidInMVCCSnapshot
  *		Is the given XID still-in-progress according to the snapshot?
@@ -1463,8 +1516,6 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
 static bool
 XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
 {
-	uint32		i;
-
 	/*
 	 * Make a quick range check to eliminate most XIDs without looking at the
 	 * xip arrays.  Note that this is OK even if we convert a subxact XID to
@@ -1496,13 +1547,8 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
 		if (!snapshot->suboverflowed)
 		{
 			/* we have full data, so search subxip */
-			int32		j;
-
-			for (j = 0; j < snapshot->subxcnt; j++)
-			{
-				if (TransactionIdEquals(xid, snapshot->subxip[j]))
-					return true;
-			}
+			if (XidInXip(xid, snapshot->subxip, snapshot->subxiphash, snapshot->subxcnt))
+				return true;
 
 			/* not there, fall through to search xip[] */
 		}
@@ -1523,16 +1569,12 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
 				return false;
 		}
 
-		for (i = 0; i < snapshot->xcnt; i++)
-		{
-			if (TransactionIdEquals(xid, snapshot->xip[i]))
-				return true;
-		}
+
+		if (XidInXip(xid, snapshot->xip, snapshot->xiphash, snapshot->xcnt))
+			return true;
 	}
 	else
 	{
-		int32		j;
-
 		/*
 		 * In recovery we store all xids in the subxact array because it is by
 		 * far the bigger array, and we mostly don't know which xids are
@@ -1562,11 +1604,8 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
 		 * indeterminate xid. We don't know whether it's top level or subxact
 		 * but it doesn't matter. If it's present, the xid is visible.
 		 */
-		for (j = 0; j < snapshot->subxcnt; j++)
-		{
-			if (TransactionIdEquals(xid, snapshot->subxip[j]))
-				return true;
-		}
+		if (XidInXip(xid, snapshot->subxip, snapshot->subxiphash, snapshot->subxcnt))
+			return true;
 	}
 
 	return false;
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 86076de..b60126c 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -68,6 +68,11 @@
 	(AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
 	(int32) ((id1) - (id2)) > 0)
 
+/* compare two XIDs already known to be normal; this is a macro for speed */
+#define NormalTransactionIdCompare(id1, id2) \
+	(AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
+	(int32) ((id1) - (id2)))
+
 /* ----------
  *		Object ID (OID) zero is InvalidOid.
  *
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index 074cc81..e0a3b11 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -23,6 +23,7 @@
 typedef struct SnapshotData *Snapshot;
 
 #define InvalidSnapshot		((Snapshot) NULL)
+#define SnapshotHashNotBuilt	(0x80000000u)
 
 /*
  * We use SnapshotData structures to represent both "regular" (MVCC)
@@ -75,6 +76,7 @@ typedef struct SnapshotData
 	 * note: all ids in xip[] satisfy xmin <= xip[i] < xmax
 	 */
 	TransactionId *xip;
+	TransactionId *xiphash;
 	uint32		xcnt;			/* # of xact ids in xip[] */
 
 	/*
@@ -87,6 +89,7 @@ typedef struct SnapshotData
 	 * out any that are >= xmax
 	 */
 	TransactionId *subxip;
+	TransactionId *subxiphash;
 	int32		subxcnt;		/* # of xact ids in subxip[] */
 	bool		suboverflowed;	/* has the subxip array overflowed? */
 
-- 
1.8.3.1

