From ea4ee0a1f317874053c303777a80636249cc36e1 Mon Sep 17 00:00:00 2001
From: "Min, Baohong" <baohong.min@intel.com>
Date: Thu, 23 Jul 2026 09:40:08 +0800
Subject: [PATCH 1/1] Reduce LWLockWaitListLock() cache-line contention with
 adaptive spin reads

Under high lock contention (100+ CPUs, 500+ clients), backends
spinning in LWLockWaitListLock() re-read lock->state on every iteration,
causing heavy cache-line bouncing that limits throughput.

Re-read lock->state only periodically, with the interval adapting to the
contention level. This optimization significantly improves performance
under high contention while leaving low-contention cases (few cores and
clients) unaffected.

Co-authored-by: Min, Baohong <baohong.min@intel.com>
Co-authored-by: Jin, Jun <jun.i.jin@intel.com>
Co-authored-by: Kim, Andrew <andrew.kim@intel.com>
---
 src/backend/storage/lmgr/lwlock.c | 25 ++++++++++++++++++++++++-
 src/backend/storage/lmgr/s_lock.c |  2 +-
 2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index b1ad396ba79..4fb61ec7fcd 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -166,6 +166,14 @@ typedef struct LWLockHandle
 static int	num_held_lwlocks = 0;
 static LWLockHandle held_lwlocks[MAX_SIMUL_LWLOCKS];
 
+/*
+ * Adaptive spin delay for LWLockWaitListLock: controls how often we
+ * re-read the lock state while spinning. This reduces cache line contention
+ * when multiple backends are waiting on the same lock.
+ */
+#define SPINS_PER_LOCK_READ_THRESHOLD  5
+static int spins_per_lock_read = 1;
+
 /* Maximum number of LWLock tranches that can be assigned by extensions */
 #define MAX_USER_DEFINED_TRANCHES 256
 
@@ -857,13 +865,28 @@ LWLockWaitListLock(LWLock *lock)
 		{
 			SpinDelayStatus delayStatus;
 
+			int spins_since_read = 0;
+			int lock_read_count = 0;
+
 			init_local_spin_delay(&delayStatus);
 
 			while (old_state & LW_FLAG_LOCKED)
 			{
 				perform_spin_delay(&delayStatus);
-				old_state = pg_atomic_read_u32(&lock->state);
+				/* Only read lock state periodically to reduce cache contention */
+				if (++spins_since_read >= spins_per_lock_read)
+				{
+					old_state = pg_atomic_read_u32(&lock->state);
+					++lock_read_count;
+					spins_since_read = 0;
+				}
 			}
+			/* Adaptively adjust read interval based on wait duration */
+			if (lock_read_count > SPINS_PER_LOCK_READ_THRESHOLD)
+				spins_per_lock_read = Min(spins_per_lock_read + 5, 256);
+			else
+				spins_per_lock_read = Max(spins_per_lock_read - 1, 1);
+
 #ifdef LWLOCK_STATS
 			delays += delayStatus.delays;
 #endif
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 6df568eccb3..3bb1205c349 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -57,7 +57,7 @@
 #define MIN_SPINS_PER_DELAY 10
 #define MAX_SPINS_PER_DELAY 1000
 #define NUM_DELAYS			1000
-#define MIN_DELAY_USEC		1000L
+#define MIN_DELAY_USEC		100L
 #define MAX_DELAY_USEC		1000000L
 
 #ifdef S_LOCK_TEST
-- 
2.43.0

