diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 6ea0a28..cd0b39c 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -51,6 +51,7 @@
 #include "access/xact.h"
 #include "access/twophase.h"
 #include "miscadmin.h"
+#include "storage/barrier.h"
 #include "storage/procarray.h"
 #include "storage/spin.h"
 #include "utils/builtins.h"
@@ -81,6 +82,9 @@ typedef struct ProcArrayStruct
 	 */
 	TransactionId lastOverflowedXid;
 
+	/* Increment on transaction commit/abort. */
+	TransactionId	xendcount;
+
 	/*
 	 * We declare pgprocnos[] as 1 entry because C wants a fixed-size array, but
 	 * actually it is maxProcs entries long.
@@ -223,7 +227,13 @@ CreateSharedProcArray(void)
 	{
 		/*
 		 * We're the first - initialize.
+		 *
+		 * Note: We need to initialize procArray->xfinish to something other
+		 * than 0, because we're going to later compare it against the
+		 * xendcount of a snapshot to see if anything's changed; and 0 in the
+		 * snapshot means it's as-yet uninitialized.
 		 */
+		procArray->xendcount = 1;
 		procArray->numProcs = 0;
 		procArray->maxProcs = PROCARRAY_MAXPROCS;
 		procArray->maxKnownAssignedXids = TOTAL_MAX_CACHED_SUBXIDS;
@@ -410,6 +420,8 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 								  latestXid))
 			ShmemVariableCache->latestCompletedXid = latestXid;
 
+		procArray->xendcount++;
+
 		LWLockRelease(ProcArrayLock);
 	}
 	else
@@ -1245,6 +1257,27 @@ GetSnapshotData(Snapshot snapshot)
 	Assert(snapshot != NULL);
 
 	/*
+	 * If no transactions have committed or aborted since the last time this
+	 * function was called on the passed-in snapshot, we can return without
+	 * doing anything.
+	 *
+	 * Memory ordering effects: It's possible that the procArray->xendcount
+	 * could be fetched by the CPU prior to entering this function, in which
+	 * case we might see an "old" value that matches instead of a "new" value
+	 * that doesn't.  But that's not much different than if this function had
+	 * been called slightly sooner in the first place.  Just to be on the safe
+	 * side, include a read barrier, so that this fetch won't be done before
+	 * all prior fetches have been completed.
+	 *
+	 * XXX: This is theoretically unsafe if 64-bit reads and writes from shared
+	 * memory aren't atomic, but in practice you'd have to be incredibly
+	 * unlucky to have a problem.
+	 */
+	pg_read_barrier();
+	if (procArray->xendcount == snapshot->xendcount)
+		return snapshot;
+
+	/*
 	 * Allocating space for maxProcs xids is usually overkill; numProcs would
 	 * be sufficient.  But it seems better to do the malloc while not holding
 	 * the lock, so we can't look at numProcs.  Likewise, we allocate much
@@ -1283,6 +1316,7 @@ GetSnapshotData(Snapshot snapshot)
 	LWLockAcquire(ProcArrayLock, LW_SHARED);
 
 	/* xmax is always latestCompletedXid + 1 */
+	snapshot->xendcount = arrayP->xendcount;
 	xmax = ShmemVariableCache->latestCompletedXid;
 	Assert(TransactionIdIsNormal(xmax));
 	TransactionIdAdvance(xmax);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index 93c02fa..d6a3a68 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -44,12 +44,13 @@ typedef struct SnapshotData
 	 * is stored as an optimization to avoid needing to search the XID arrays
 	 * for most tuples.
 	 */
+	uint64		xendcount;		/* when did we take this snapshot? */
 	TransactionId xmin;			/* all XID < xmin are visible to me */
 	TransactionId xmax;			/* all XID >= xmax are invisible to me */
 	uint32		xcnt;			/* # of xact ids in xip[] */
-	TransactionId *xip;			/* array of xact IDs in progress */
 	/* note: all ids in xip[] satisfy xmin <= xip[i] < xmax */
 	int32		subxcnt;		/* # of xact ids in subxip[] */
+	TransactionId *xip;			/* array of xact IDs in progress */
 	TransactionId *subxip;		/* array of subxact IDs in progress */
 	bool		suboverflowed;	/* has the subxip array overflowed? */
 	bool		takenDuringRecovery;	/* recovery-shaped snapshot? */
