]> granicus.if.org Git - postgresql/commitdiff
Improve memory management code to avoid inefficient behavior when a context
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 27 Dec 2006 22:30:48 +0000 (22:30 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 27 Dec 2006 22:30:48 +0000 (22:30 +0000)
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

index 0135ba0e0b34701bb8693f946c74cac43da631d5..5e97ebe89694a3d16cad56bc2020a2b61a6c5050 100644 (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);