Improve memory management code to avoid inefficient behavior when a context

has a small maxBlockSize: the maximum request size that we will treat as a
"chunk" needs to be limited to fit in maxBlockSize.  Otherwise we will round
up the request size to the next power of 2, wasting space, which is a bit
pointless if we aren't going to make the blocks big enough to fit additional
stuff in them.  The example motivating this is local buffer management, which
makes repeated allocations of 8K (one BLCKSZ buffer) in TopMemoryContext,
which has maxBlockSize = 8K because for the most part allocations there are
small.  This leads to each local buffer actually eating 16K of space, which
adds up when there are thousands of them.  I intend to change localbuf.c to
aggregate its requests, which will prevent this particular misbehavior, but
it seems likely that similar scenarios could arise elsewhere, so fixing the
core problem seems wise as well.
This commit is contained in:
Tom Lane 2006-12-27 22:30:48 +00:00
parent 6bb7b467c0
commit c22dea8900
1 changed files with 21 additions and 6 deletions

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.69 2006/11/08 19:27:24 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.70 2006/12/27 22:30:48 tgl Exp $
*
* NOTE:
* This is a new (Feb. 05, 1999) implementation of the allocation set
@ -141,6 +141,7 @@ typedef struct AllocSetContext
Size initBlockSize; /* initial block size */
Size maxBlockSize; /* maximum block size */
Size nextBlockSize; /* next block size to allocate */
Size allocChunkLimit; /* effective chunk size limit */
AllocBlock keeper; /* if not NULL, keep this block over resets */
} AllocSetContext;
@ -328,6 +329,20 @@ AllocSetContextCreate(MemoryContext parent,
context->maxBlockSize = maxBlockSize;
context->nextBlockSize = initBlockSize;
/*
* Compute the allocation chunk size limit for this context. It can't
* be more than ALLOC_CHUNK_LIMIT because of the fixed number of
* freelists. If maxBlockSize is small then requests exceeding the
* maxBlockSize should be treated as large chunks, too. We have to
* have allocChunkLimit a power of two, because the requested and
* actually-allocated sizes of any chunk must be on the same side of
* the limit, else we get confused about whether the chunk is "big".
*/
context->allocChunkLimit = ALLOC_CHUNK_LIMIT;
while (context->allocChunkLimit >
(Size) (maxBlockSize - ALLOC_BLOCKHDRSZ - ALLOC_CHUNKHDRSZ))
context->allocChunkLimit >>= 1;
/*
* Grab always-allocated space, if requested
*/
@ -512,7 +527,7 @@ AllocSetAlloc(MemoryContext context, Size size)
* If requested size exceeds maximum for chunks, allocate an entire block
* for this request.
*/
if (size > ALLOC_CHUNK_LIMIT)
if (size > set->allocChunkLimit)
{
chunk_size = MAXALIGN(size);
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
@ -775,7 +790,7 @@ AllocSetFree(MemoryContext context, void *pointer)
set->header.name, chunk);
#endif
if (chunk->size > ALLOC_CHUNK_LIMIT)
if (chunk->size > set->allocChunkLimit)
{
/*
* Big chunks are certain to have been allocated as single-chunk
@ -868,7 +883,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
return pointer;
}
if (oldsize > ALLOC_CHUNK_LIMIT)
if (oldsize > set->allocChunkLimit)
{
/*
* The chunk must have been allocated as a single-chunk block. Find
@ -943,7 +958,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
*/
AllocPointer newPointer;
if (size <= ALLOC_CHUNK_LIMIT)
if (size <= set->allocChunkLimit)
{
AllocBlock block = set->blocks;
char *chunk_end;
@ -1122,7 +1137,7 @@ AllocSetCheck(MemoryContext context)
name, (unsigned long) chsize, chunk, block);
/* single-chunk block? */
if (chsize > ALLOC_CHUNK_LIMIT &&
if (chsize > set->allocChunkLimit &&
chsize + ALLOC_CHUNKHDRSZ != blk_used)
elog(WARNING, "problem in alloc set %s: bad single-chunk %p in block %p",
name, chunk, block);