From c22dea89004b99af099cb31139f8aeb5bcb8840d Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 27 Dec 2006 22:30:48 +0000 Subject: [PATCH] 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. --- src/backend/utils/mmgr/aset.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 0135ba0e0b..5e97ebe896 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -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);