]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
bufmgr: Make UnlockReleaseBuffer() more efficient
authorAndres Freund <andres@anarazel.de>
Fri, 27 Mar 2026 19:27:04 +0000 (15:27 -0400)
committerAndres Freund <andres@anarazel.de>
Fri, 27 Mar 2026 19:56:29 +0000 (15:56 -0400)
Now that the buffer content lock is implemented as part of BufferDesc.state,
releasing the lock and unpinning the buffer can be implemented as a single
atomic operation.

This improves workloads that have heavy contention on a small number of
buffers substantially, I e.g., see a ~20% improvement for pipelined readonly
pgbench on an older two socket machine.

Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://postgr.es/m/5ubipyssiju5twkb7zgqwdr7q2vhpkpmuelxfpanetlk6ofnop@hvxb4g2amb2d

src/backend/storage/buffer/bufmgr.c

index 8a8c4adb9e1eead250347ede16165ef7c6ef25f4..da87c85a079f8c509d1856d465121f80c334bf7c 100644 (file)
@@ -5529,13 +5529,65 @@ ReleaseBuffer(Buffer buffer)
 /*
  * UnlockReleaseBuffer -- release the content lock and pin on a buffer
  *
- * This is just a shorthand for a common combination.
+ * This is just a, more efficient, shorthand for a common combination.
  */
 void
 UnlockReleaseBuffer(Buffer buffer)
 {
-       LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
-       ReleaseBuffer(buffer);
+       int                     mode;
+       BufferDesc *buf;
+       PrivateRefCountEntry *ref;
+       uint64          sub;
+       uint64          lockstate;
+
+       Assert(BufferIsPinned(buffer));
+
+       if (BufferIsLocal(buffer))
+       {
+               UnpinLocalBuffer(buffer);
+               return;
+       }
+
+       ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer);
+
+       buf = GetBufferDescriptor(buffer - 1);
+
+       mode = BufferLockDisownInternal(buffer, buf);
+
+       /* compute state modification for lock release */
+       sub = BufferLockReleaseSub(mode);
+
+       /* compute state modification for pin release */
+       ref = GetPrivateRefCountEntry(buffer, false);
+       Assert(ref != NULL);
+       Assert(ref->data.refcount > 0);
+       ref->data.refcount--;
+
+       /* no more backend local pins, reduce shared pin count */
+       if (likely(ref->data.refcount == 0))
+       {
+               /* See comment in UnpinBufferNoOwner() */
+               VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), BLCKSZ);
+
+               sub |= BUF_REFCOUNT_ONE;
+               ForgetPrivateRefCountEntry(ref);
+       }
+
+       /* perform the lock and pin release in one atomic op */
+       lockstate = pg_atomic_sub_fetch_u64(&buf->state, sub);
+
+       /* wake up waiters for the lock */
+       BufferLockProcessRelease(buf, mode, lockstate);
+
+       /* wake up waiter for the pin release */
+       if (lockstate & BM_PIN_COUNT_WAITER)
+               WakePinCountWaiter(buf);
+
+       /*
+        * Now okay to allow cancel/die interrupts again, which were held when the
+        * lock was acquired.
+        */
+       RESUME_INTERRUPTS();
 }
 
 /*