Commit 4b4b680c authored by Andres Freund's avatar Andres Freund

Make backend local tracking of buffer pins memory efficient.

Since the dawn of time (aka Postgres95) multiple pins of the same
buffer by one backend have been optimized not to modify the shared
refcount more than once. This optimization has always used a NBuffer
sized array in each backend keeping track of a backend's pins.

That array (PrivateRefCount) was one of the biggest per-backend memory
allocations, depending on the shared_buffers setting. Besides the
waste of memory it also has proven to be a performance bottleneck when
assertions are enabled as we make sure that there's no remaining pins
left at the end of transactions. Also, on servers with lots of memory
and a correspondingly high shared_buffers setting the amount of random
memory accesses can also lead to poor cpu cache efficiency.

Because of these reasons a backend's buffers pins are now kept track
of in a small statically sized array that overflows into a hash table
when necessary. Benchmarks have shown neutral to positive performance
results with considerably lower memory usage.

Patch by me, review by Robert Haas.

Discussion: 20140321182231.GA17111@alap3.anarazel.de
parent c6eaa880
...@@ -37,7 +37,7 @@ typedef struct ...@@ -37,7 +37,7 @@ typedef struct
/* /*
* An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from * An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from
* being pinned by too many backends and each backend will only pin once * being pinned by too many backends and each backend will only pin once
* because of bufmgr.c's PrivateRefCount array. * because of bufmgr.c's PrivateRefCount infrastructure.
*/ */
int32 pinning_backends; int32 pinning_backends;
} BufferCachePagesRec; } BufferCachePagesRec;
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
BufferDesc *BufferDescriptors; BufferDesc *BufferDescriptors;
char *BufferBlocks; char *BufferBlocks;
int32 *PrivateRefCount;
/* /*
...@@ -50,16 +49,9 @@ int32 *PrivateRefCount; ...@@ -50,16 +49,9 @@ int32 *PrivateRefCount;
* *
* refcount -- Counts the number of processes holding pins on a buffer. * refcount -- Counts the number of processes holding pins on a buffer.
* A buffer is pinned during IO and immediately after a BufferAlloc(). * A buffer is pinned during IO and immediately after a BufferAlloc().
* Pins must be released before end of transaction. * Pins must be released before end of transaction. For efficiency the
* * shared refcount isn't increased if a individual backend pins a buffer
* PrivateRefCount -- Each buffer also has a private refcount that keeps * multiple times. Check the PrivateRefCount infrastructure in bufmgr.c.
* track of the number of times the buffer is pinned in the current
* process. This is used for two purposes: first, if we pin a
* a buffer more than once, we only need to change the shared refcount
* once, thus only lock the shared state once; second, when a transaction
* aborts, it should only unpin the buffers exactly the number of times it
* has pinned them, so that it will not blow away buffers of another
* backend.
*/ */
...@@ -129,31 +121,6 @@ InitBufferPool(void) ...@@ -129,31 +121,6 @@ InitBufferPool(void)
StrategyInitialize(!foundDescs); StrategyInitialize(!foundDescs);
} }
/*
* Initialize access to shared buffer pool
*
* This is called during backend startup (whether standalone or under the
* postmaster). It sets up for this backend's access to the already-existing
* buffer pool.
*
* NB: this is called before InitProcess(), so we do not have a PGPROC and
* cannot do LWLockAcquire; hence we can't actually access stuff in
* shared memory yet. We are only initializing local data here.
* (See also InitBufferPoolBackend, over in bufmgr.c.)
*/
void
InitBufferPoolAccess(void)
{
/*
* Allocate and zero local arrays of per-buffer info.
*/
PrivateRefCount = (int32 *) calloc(NBuffers, sizeof(int32));
if (!PrivateRefCount)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
}
/* /*
* BufferShmemSize * BufferShmemSize
* *
......
This diff is collapsed.
...@@ -55,7 +55,6 @@ extern int target_prefetch_pages; ...@@ -55,7 +55,6 @@ extern int target_prefetch_pages;
/* in buf_init.c */ /* in buf_init.c */
extern PGDLLIMPORT char *BufferBlocks; extern PGDLLIMPORT char *BufferBlocks;
extern PGDLLIMPORT int32 *PrivateRefCount;
/* in localbuf.c */ /* in localbuf.c */
extern PGDLLIMPORT int NLocBuffer; extern PGDLLIMPORT int NLocBuffer;
...@@ -101,24 +100,6 @@ extern PGDLLIMPORT int32 *LocalRefCount; ...@@ -101,24 +100,6 @@ extern PGDLLIMPORT int32 *LocalRefCount;
(bufnum) != InvalidBuffer \ (bufnum) != InvalidBuffer \
) )
/*
* BufferIsPinned
* True iff the buffer is pinned (also checks for valid buffer number).
*
* NOTE: what we check here is that *this* backend holds a pin on
* the buffer. We do not care whether some other backend does.
*/
#define BufferIsPinned(bufnum) \
( \
!BufferIsValid(bufnum) ? \
false \
: \
BufferIsLocal(bufnum) ? \
(LocalRefCount[-(bufnum) - 1] > 0) \
: \
(PrivateRefCount[(bufnum) - 1] > 0) \
)
/* /*
* BufferGetBlock * BufferGetBlock
* Returns a reference to a disk page image associated with a buffer. * Returns a reference to a disk page image associated with a buffer.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment