From c22dea89004b99af099cb31139f8aeb5bcb8840d Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
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 0135ba0e0b3..5e97ebe8969 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);
-- 
GitLab