mirror of
https://github.com/postgres/postgres.git
synced 2025-11-13 16:22:44 +03:00
bufmgr: Remove freelist, always use clock-sweep
This set of changes removes the list of available buffers and instead simply uses the clock-sweep algorithm to find and return an available buffer. This also removes the have_free_buffer() function and simply caps the pg_autoprewarm process to at most NBuffers. While on the surface this appears to be removing an optimization it is in fact eliminating code that induces overhead in the form of synchronization that is problematic for multi-core systems. The main reason for removing the freelist, however, is not the moderate improvement in scalability, but that having the freelist would require dedicated complexity in several upcoming patches. As we have not been able to find a case benefiting from the freelist... Author: Greg Burd <greg@burd.me> Reviewed-by: Tomas Vondra <tomas@vondra.me> Reviewed-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/70C6A5B5-2A20-4D0B-BC73-EB09DD62D61C@getmailspring.com
This commit is contained in:
@@ -39,14 +39,6 @@ typedef struct
|
||||
*/
|
||||
pg_atomic_uint32 nextVictimBuffer;
|
||||
|
||||
int firstFreeBuffer; /* Head of list of unused buffers */
|
||||
int lastFreeBuffer; /* Tail of list of unused buffers */
|
||||
|
||||
/*
|
||||
* NOTE: lastFreeBuffer is undefined when firstFreeBuffer is -1 (that is,
|
||||
* when the list is empty)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Statistics. These counters should be wide enough that they can't
|
||||
* overflow during a single bgwriter cycle.
|
||||
@@ -163,23 +155,6 @@ ClockSweepTick(void)
|
||||
return victim;
|
||||
}
|
||||
|
||||
/*
|
||||
* have_free_buffer -- a lockless check to see if there is a free buffer in
|
||||
* buffer pool.
|
||||
*
|
||||
* If the result is true that will become stale once free buffers are moved out
|
||||
* by other operations, so the caller who strictly want to use a free buffer
|
||||
* should not call this.
|
||||
*/
|
||||
bool
|
||||
have_free_buffer(void)
|
||||
{
|
||||
if (StrategyControl->firstFreeBuffer >= 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* StrategyGetBuffer
|
||||
*
|
||||
@@ -249,69 +224,7 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint32 *buf_state, bool *from_r
|
||||
*/
|
||||
pg_atomic_fetch_add_u32(&StrategyControl->numBufferAllocs, 1);
|
||||
|
||||
/*
|
||||
* First check, without acquiring the lock, whether there's buffers in the
|
||||
* freelist. Since we otherwise don't require the spinlock in every
|
||||
* StrategyGetBuffer() invocation, it'd be sad to acquire it here -
|
||||
* uselessly in most cases. That obviously leaves a race where a buffer is
|
||||
* put on the freelist but we don't see the store yet - but that's pretty
|
||||
* harmless, it'll just get used during the next buffer acquisition.
|
||||
*
|
||||
* If there's buffers on the freelist, acquire the spinlock to pop one
|
||||
* buffer of the freelist. Then check whether that buffer is usable and
|
||||
* repeat if not.
|
||||
*
|
||||
* Note that the freeNext fields are considered to be protected by the
|
||||
* buffer_strategy_lock not the individual buffer spinlocks, so it's OK to
|
||||
* manipulate them without holding the spinlock.
|
||||
*/
|
||||
if (StrategyControl->firstFreeBuffer >= 0)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
/* Acquire the spinlock to remove element from the freelist */
|
||||
SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
|
||||
|
||||
if (StrategyControl->firstFreeBuffer < 0)
|
||||
{
|
||||
SpinLockRelease(&StrategyControl->buffer_strategy_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
buf = GetBufferDescriptor(StrategyControl->firstFreeBuffer);
|
||||
Assert(buf->freeNext != FREENEXT_NOT_IN_LIST);
|
||||
|
||||
/* Unconditionally remove buffer from freelist */
|
||||
StrategyControl->firstFreeBuffer = buf->freeNext;
|
||||
buf->freeNext = FREENEXT_NOT_IN_LIST;
|
||||
|
||||
/*
|
||||
* Release the lock so someone else can access the freelist while
|
||||
* we check out this buffer.
|
||||
*/
|
||||
SpinLockRelease(&StrategyControl->buffer_strategy_lock);
|
||||
|
||||
/*
|
||||
* If the buffer is pinned or has a nonzero usage_count, we cannot
|
||||
* use it; discard it and retry. (This can only happen if VACUUM
|
||||
* put a valid buffer in the freelist and then someone else used
|
||||
* it before we got to it. It's probably impossible altogether as
|
||||
* of 8.3, but we'd better check anyway.)
|
||||
*/
|
||||
local_buf_state = LockBufHdr(buf);
|
||||
if (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0
|
||||
&& BUF_STATE_GET_USAGECOUNT(local_buf_state) == 0)
|
||||
{
|
||||
if (strategy != NULL)
|
||||
AddBufferToRing(strategy, buf);
|
||||
*buf_state = local_buf_state;
|
||||
return buf;
|
||||
}
|
||||
UnlockBufHdr(buf, local_buf_state);
|
||||
}
|
||||
}
|
||||
|
||||
/* Nothing on the freelist, so run the "clock-sweep" algorithm */
|
||||
/* Use the "clock sweep" algorithm to find a free buffer */
|
||||
trycounter = NBuffers;
|
||||
for (;;)
|
||||
{
|
||||
@@ -356,29 +269,6 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint32 *buf_state, bool *from_r
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* StrategyFreeBuffer: put a buffer on the freelist
|
||||
*/
|
||||
void
|
||||
StrategyFreeBuffer(BufferDesc *buf)
|
||||
{
|
||||
SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
|
||||
|
||||
/*
|
||||
* It is possible that we are told to put something in the freelist that
|
||||
* is already in it; don't screw up the list if so.
|
||||
*/
|
||||
if (buf->freeNext == FREENEXT_NOT_IN_LIST)
|
||||
{
|
||||
buf->freeNext = StrategyControl->firstFreeBuffer;
|
||||
if (buf->freeNext < 0)
|
||||
StrategyControl->lastFreeBuffer = buf->buf_id;
|
||||
StrategyControl->firstFreeBuffer = buf->buf_id;
|
||||
}
|
||||
|
||||
SpinLockRelease(&StrategyControl->buffer_strategy_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* StrategySyncStart -- tell BgBufferSync where to start syncing
|
||||
*
|
||||
@@ -504,13 +394,6 @@ StrategyInitialize(bool init)
|
||||
|
||||
SpinLockInit(&StrategyControl->buffer_strategy_lock);
|
||||
|
||||
/*
|
||||
* Grab the whole linked list of free buffers for our strategy. We
|
||||
* assume it was previously set up by BufferManagerShmemInit().
|
||||
*/
|
||||
StrategyControl->firstFreeBuffer = 0;
|
||||
StrategyControl->lastFreeBuffer = NBuffers - 1;
|
||||
|
||||
/* Initialize the clock-sweep pointer */
|
||||
pg_atomic_init_u32(&StrategyControl->nextVictimBuffer, 0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user