Eliminate divide in new fast-path locking code
authorDavid Rowley <[email protected]>
Sat, 26 Apr 2025 23:53:40 +0000 (11:53 +1200)
committerDavid Rowley <[email protected]>
Sat, 26 Apr 2025 23:53:40 +0000 (11:53 +1200)
c4d5cb71d2 adjusted the fast-path locking code to allow some
configuration of the number of fast-path locking slots via the
max_locks_per_transaction GUC.  In that commit the FAST_PATH_REL_GROUP()
macro used integer division to determine the fast-path locking group slot
to use for the lock.

The divisor in this case is always a power-of-two value.  Here we swap
out the divide by a bitwise-AND, which is a significantly faster
operation to perform.

In passing, adjust the code that's setting FastPathLockGroupsPerBackend
so that it's more clear that the value being set is a power-of-two.

Also, adjust some comments in the area which contained some magic
numbers.  It seems better to justify the 1024 upper limit in the
location where the #define is made instead of where it is used.

Author: David Rowley <[email protected]>
Reviewed-by: Tomas Vondra <[email protected]>
Discussion: https://postgr.es/m/CAApHDvodr3bcnpxcs7+k-3cFwYR0tP-BYhyd2PpDhe-bCx9i=g@mail.gmail.com

src/backend/storage/lmgr/lock.c
src/backend/utils/init/postinit.c
src/include/storage/proc.h

index 002303664aa5c7bace4b87fc42acb971c2e38b97..86b06b9223f0b00489d6d8ac5ef9de2b4eb586ce 100644 (file)
@@ -210,9 +210,12 @@ int            FastPathLockGroupsPerBackend = 0;
  *
  * The selected constant (49157) is a prime not too close to 2^k, and it's
  * small enough to not cause overflows (in 64-bit).
+ *
+ * We can assume that FastPathLockGroupsPerBackend is a power-of-two per
+ * InitializeFastPathLocks().
  */
 #define FAST_PATH_REL_GROUP(rel) \
-   (((uint64) (rel) * 49157) % FastPathLockGroupsPerBackend)
+   (((uint64) (rel) * 49157) & (FastPathLockGroupsPerBackend - 1))
 
 /*
  * Given the group/slot indexes, calculate the slot index in the whole array
index 01309ef3f867ab89d5b296c752fff87402e45e67..28f09a27001b043411e27b41476b177f53d6d7f5 100644 (file)
@@ -575,13 +575,6 @@ InitializeMaxBackends(void)
  *
  * This must be called after modules have had the chance to alter GUCs in
  * shared_preload_libraries and before shared memory size is determined.
- *
- * The default max_locks_per_xact=64 means 4 groups by default.
- *
- * We allow anything between 1 and 1024 groups, with the usual power-of-2
- * logic. The 1 is the "old" size with only 16 slots, 1024 is an arbitrary
- * limit (matching max_locks_per_xact = 16k). Values over 1024 are unlikely
- * to be beneficial - there are bottlenecks we'll hit way before that.
  */
 void
 InitializeFastPathLocks(void)
@@ -589,19 +582,22 @@ InitializeFastPathLocks(void)
    /* Should be initialized only once. */
    Assert(FastPathLockGroupsPerBackend == 0);
 
-   /* we need at least one group */
-   FastPathLockGroupsPerBackend = 1;
-
-   while (FastPathLockGroupsPerBackend < FP_LOCK_GROUPS_PER_BACKEND_MAX)
-   {
-       /* stop once we exceed max_locks_per_xact */
-       if (FastPathLockSlotsPerBackend() >= max_locks_per_xact)
-           break;
-
-       FastPathLockGroupsPerBackend *= 2;
-   }
+   /*
+    * Based on the max_locks_per_transaction GUC, as that's a good indicator
+    * of the expected number of locks, figure out the value for
+    * FastPathLockGroupsPerBackend.  This must be a power-of-two.  We cap the
+    * value at FP_LOCK_GROUPS_PER_BACKEND_MAX and insist the value is at
+    * least 1.
+    *
+    * The default max_locks_per_transaction = 64 means 4 groups by default.
+    */
+   FastPathLockGroupsPerBackend =
+       Max(Min(pg_nextpower2_32(max_locks_per_xact) / FP_LOCK_SLOTS_PER_GROUP,
+               FP_LOCK_GROUPS_PER_BACKEND_MAX), 1);
 
-   Assert(FastPathLockGroupsPerBackend <= FP_LOCK_GROUPS_PER_BACKEND_MAX);
+   /* Validate we did get a power-of-two */
+   Assert(FastPathLockGroupsPerBackend ==
+          pg_nextpower2_32(FastPathLockGroupsPerBackend));
 }
 
 /*
index 86c5f998c778fa66005418746e2b4f09f1e81f6f..9f9b3fcfbf1d7bebaa48711711f994fb3fe9bdce 100644 (file)
@@ -86,6 +86,14 @@ struct XidCache
  */
 extern PGDLLIMPORT int FastPathLockGroupsPerBackend;
 
+/*
+ * Define the maximum number of fast-path locking groups per backend.
+ * This must be a power-of-two value.  The actual number of fast-path
+ * lock groups is calculated in InitializeFastPathLocks() based on
+ * max_locks_per_transaction.  1024 is an arbitrary upper limit (matching
+ * max_locks_per_transaction = 16k).  Values over 1024 are unlikely to be
+ * beneficial as there are bottlenecks we'll hit way before that.
+ */
 #define        FP_LOCK_GROUPS_PER_BACKEND_MAX  1024
 #define        FP_LOCK_SLOTS_PER_GROUP     16  /* don't change */
 #define        FastPathLockSlotsPerBackend() \