🌐 AI搜索 & 代理 主页
Skip to content

Commit 30df619

Browse files
committed
bufmgr: Add one-entry cache for private refcount
The private refcount entry for a buffer is often looked up repeatedly for the same buffer, e.g. to pin and then unpin a buffer. Benchmarking shows that it's worthwhile to have a one-entry cache for that case. With that cache in place, it's worth splitting GetPrivateRefCountEntry() into a small inline portion (for the cache hit case) and an out-of-line helper for the rest. This is helpful for some workloads today, but becomes more important in an upcoming patch that will utilize the private refcount infrastructure to also store whether the buffer is currently locked, as that increases the rate of lookups substantially. Reviewed-by: Melanie Plageman <melanieplageman@gmail.com> Discussion: https://postgr.es/m/6rgb2nvhyvnszz4ul3wfzlf5rheb2kkwrglthnna7qhe24onwr@vw27225tkyar
1 parent edbaaea commit 30df619

File tree

1 file changed

+55
-11
lines changed

1 file changed

+55
-11
lines changed

src/backend/storage/buffer/bufmgr.c

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ static HTAB *PrivateRefCountHash = NULL;
241241
static int32 PrivateRefCountOverflowed = 0;
242242
static uint32 PrivateRefCountClock = 0;
243243
static int ReservedRefCountSlot = -1;
244+
static int PrivateRefCountEntryLast = -1;
244245

245246
static uint32 MaxProportionalPins;
246247

@@ -374,28 +375,27 @@ NewPrivateRefCountEntry(Buffer buffer)
374375
res->buffer = buffer;
375376
res->data.refcount = 0;
376377

378+
/* update cache for the next lookup */
379+
PrivateRefCountEntryLast = ReservedRefCountSlot;
380+
377381
ReservedRefCountSlot = -1;
378382

379383
return res;
380384
}
381385

382386
/*
383-
* Return the PrivateRefCount entry for the passed buffer.
384-
*
385-
* Returns NULL if a buffer doesn't have a refcount entry. Otherwise, if
386-
* do_move is true, and the entry resides in the hashtable the entry is
387-
* optimized for frequent access by moving it to the array.
387+
* Slow-path for GetPrivateRefCountEntry(). This is big enough to not be worth
388+
* inlining. This particularly seems to be true if the compiler is capable of
389+
* auto-vectorizing the code, as that imposes additional stack-alignment
390+
* requirements etc.
388391
*/
389-
static inline PrivateRefCountEntry *
390-
GetPrivateRefCountEntry(Buffer buffer, bool do_move)
392+
static pg_noinline PrivateRefCountEntry *
393+
GetPrivateRefCountEntrySlow(Buffer buffer, bool do_move)
391394
{
392395
PrivateRefCountEntry *res;
393396
int match = -1;
394397
int i;
395398

396-
Assert(BufferIsValid(buffer));
397-
Assert(!BufferIsLocal(buffer));
398-
399399
/*
400400
* First search for references in the array, that'll be sufficient in the
401401
* majority of cases.
@@ -409,8 +409,13 @@ GetPrivateRefCountEntry(Buffer buffer, bool do_move)
409409
}
410410
}
411411

412-
if (match != -1)
412+
if (likely(match != -1))
413+
{
414+
/* update cache for the next lookup */
415+
PrivateRefCountEntryLast = match;
416+
413417
return &PrivateRefCountArray[match];
418+
}
414419

415420
/*
416421
* By here we know that the buffer, if already pinned, isn't residing in
@@ -450,6 +455,8 @@ GetPrivateRefCountEntry(Buffer buffer, bool do_move)
450455
free->buffer = buffer;
451456
free->data = res->data;
452457
PrivateRefCountArrayKeys[ReservedRefCountSlot] = buffer;
458+
/* update cache for the next lookup */
459+
PrivateRefCountEntryLast = match;
453460

454461
ReservedRefCountSlot = -1;
455462

@@ -464,6 +471,43 @@ GetPrivateRefCountEntry(Buffer buffer, bool do_move)
464471
}
465472
}
466473

474+
/*
475+
* Return the PrivateRefCount entry for the passed buffer.
476+
*
477+
* Returns NULL if a buffer doesn't have a refcount entry. Otherwise, if
478+
* do_move is true, and the entry resides in the hashtable the entry is
479+
* optimized for frequent access by moving it to the array.
480+
*/
481+
static inline PrivateRefCountEntry *
482+
GetPrivateRefCountEntry(Buffer buffer, bool do_move)
483+
{
484+
Assert(BufferIsValid(buffer));
485+
Assert(!BufferIsLocal(buffer));
486+
487+
/*
488+
* It's very common to look up the same buffer repeatedly. To make that
489+
* fast, we have a one-entry cache.
490+
*
491+
* In contrast to the loop in GetPrivateRefCountEntrySlow(), here it
492+
* faster to check PrivateRefCountArray[].buffer, as in the case of a hit
493+
* fewer addresses are computed and fewer cachelines are accessed. Whereas
494+
* in GetPrivateRefCountEntrySlow()'s case, checking
495+
* PrivateRefCountArrayKeys saves a lot of memory accesses.
496+
*/
497+
if (likely(PrivateRefCountEntryLast != -1) &&
498+
likely(PrivateRefCountArray[PrivateRefCountEntryLast].buffer == buffer))
499+
{
500+
return &PrivateRefCountArray[PrivateRefCountEntryLast];
501+
}
502+
503+
/*
504+
* The code for the cached lookup is small enough to be worth inlining
505+
* into the caller. In the miss case however, that empirically doesn't
506+
* seem worth it.
507+
*/
508+
return GetPrivateRefCountEntrySlow(buffer, do_move);
509+
}
510+
467511
/*
468512
* Returns how many times the passed buffer is pinned by this backend.
469513
*

0 commit comments

Comments
 (0)