Modify aset.c logic so that blocks requested from malloc get

bigger the more space is used in an allocset.  This reduces the malloc
overhead very substantially on queries that need lots of memory.
This commit is contained in:
Tom Lane 1999-05-22 23:19:37 +00:00
parent f9f90b21b2
commit d52a91a5d8
2 changed files with 122 additions and 36 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.14 1999/02/13 23:20:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.15 1999/05/22 23:19:37 tgl Exp $
*
* NOTE:
* This is a new (Feb. 05, 1999) implementation of the allocation set
@ -16,7 +16,7 @@
* many small allocations in a few bigger blocks. AllocSetFree() does
* never free() memory really. It just add's the free'd area to some
* list for later reuse by AllocSetAlloc(). All memory blocks are free()'d
* on AllocSetReset() at once, what happens when the memory context gets
* at once on AllocSetReset(), which happens when the memory context gets
* destroyed.
* Jan Wieck
*-------------------------------------------------------------------------
@ -38,8 +38,36 @@
#undef realloc
#define ALLOC_BLOCK_SIZE 8192
#define ALLOC_CHUNK_LIMIT 512
/*--------------------
* Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
* for k = 0 .. ALLOCSET_NUM_FREELISTS-2.
* The last freelist holds all larger chunks.
*
* CAUTION: ALLOC_MINBITS must be large enough so that
* 1<<ALLOC_MINBITS is at least MAXALIGN,
* or we may fail to align the smallest chunks adequately.
* 16-byte alignment is enough on all currently known machines.
*--------------------
*/
#define ALLOC_MINBITS 4 /* smallest chunk size is 16 bytes */
#define ALLOC_SMALLCHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-2+ALLOC_MINBITS))
/* Size of largest chunk that we use a fixed size for */
/*--------------------
* The first block allocated for an allocset has size ALLOC_MIN_BLOCK_SIZE.
* Each time we have to allocate another block, we double the block size
* (if possible, and without exceeding ALLOC_MAX_BLOCK_SIZE), so as to reduce
* the load on "malloc".
*
* Blocks allocated to hold oversize chunks do not follow this rule, however;
* they are just however big they need to be.
*--------------------
*/
#define ALLOC_MIN_BLOCK_SIZE 8192
#define ALLOC_MAX_BLOCK_SIZE (8 * 1024 * 1024)
#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))
@ -65,11 +93,14 @@ AllocSetFreeIndex(Size size)
{
int idx = 0;
size = (size - 1) >> 4;
while (size != 0 && idx < 7)
if (size > 0)
{
idx++;
size >>= 1;
size = (size - 1) >> ALLOC_MINBITS;
while (size != 0 && idx < ALLOCSET_NUM_FREELISTS-1)
{
idx++;
size >>= 1;
}
}
return idx;
@ -174,6 +205,7 @@ AllocSetAlloc(AllocSet set, Size size)
AllocChunk freeref = NULL;
int fidx;
Size chunk_size;
Size blksize;
AssertArg(AllocSetIsValid(set));
@ -191,7 +223,7 @@ AllocSetAlloc(AllocSet set, Size size)
}
/*
* If found, remove it from the free list, make it again
* If one is found, remove it from the free list, make it again
* a member of the alloc set and return it's data address.
*
*/
@ -207,26 +239,49 @@ AllocSetAlloc(AllocSet set, Size size)
}
/*
* If requested size exceeds smallchunk limit, allocate a separate,
* entire used block for this allocation
* Choose the actual chunk size to allocate.
*/
if (size > ALLOC_SMALLCHUNK_LIMIT)
chunk_size = MAXALIGN(size);
else
chunk_size = 1 << (fidx + ALLOC_MINBITS);
Assert(chunk_size >= size);
/*
* If there is enough room in the active allocation block,
* always allocate the chunk there.
*/
if ((block = set->blocks) != NULL)
{
Size have_free = block->endptr - block->freeptr;
if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ))
block = NULL;
}
/*
* Otherwise, if requested size exceeds smallchunk limit,
* allocate an entire separate block for this allocation
*
*/
if (size > ALLOC_CHUNK_LIMIT)
if (block == NULL && size > ALLOC_SMALLCHUNK_LIMIT)
{
Size blksize;
chunk_size = MAXALIGN(size);
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
block = (AllocBlock) malloc(blksize);
if (block == NULL)
elog(FATAL, "Memory exhausted in AllocSetAlloc()");
block->aset = set;
block->freeptr = block->endptr = ((char *)block) + ALLOC_BLOCKHDRSZ;
block->freeptr = block->endptr = ((char *)block) + blksize;
chunk = (AllocChunk) (((char *)block) + ALLOC_BLOCKHDRSZ);
chunk->aset = set;
chunk->size = chunk_size;
/*
* Try to stick the block underneath the active allocation block,
* so that we don't lose the use of the space remaining therein.
*/
if (set->blocks != NULL)
{
block->next = set->blocks->next;
@ -241,33 +296,61 @@ AllocSetAlloc(AllocSet set, Size size)
return AllocChunkGetPointer(chunk);
}
chunk_size = 16 << fidx;
if ((block = set->blocks) != NULL)
{
Size have_free = block->endptr - block->freeptr;
if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ))
block = NULL;
}
/*
* Time to create a new regular block?
*/
if (block == NULL)
{
block = (AllocBlock) malloc(ALLOC_BLOCK_SIZE);
if (set->blocks == NULL)
{
blksize = ALLOC_MIN_BLOCK_SIZE;
block = (AllocBlock) malloc(blksize);
}
else
{
/* Get size of prior block */
blksize = set->blocks->endptr - ((char *) set->blocks);
/* Special case: if very first allocation was for a large chunk,
* could have a funny-sized top block. Do something reasonable.
*/
if (blksize < ALLOC_MIN_BLOCK_SIZE)
blksize = ALLOC_MIN_BLOCK_SIZE;
/* Crank it up, but not past max */
blksize <<= 1;
if (blksize > ALLOC_MAX_BLOCK_SIZE)
blksize = ALLOC_MAX_BLOCK_SIZE;
/* Try to allocate it */
block = (AllocBlock) malloc(blksize);
/*
* We could be asking for pretty big blocks here, so cope if
* malloc fails. But give up if there's less than a meg or so
* available...
*/
while (block == NULL && blksize > 1024*1024)
{
blksize >>= 1;
block = (AllocBlock) malloc(blksize);
}
}
if (block == NULL)
elog(FATAL, "Memory exhausted in AllocSetAlloc()");
block->aset = set;
block->next = set->blocks;
block->freeptr = ((char *)block) + ALLOC_BLOCKHDRSZ;
block->endptr = ((char *)block) + ALLOC_BLOCK_SIZE;
block->endptr = ((char *)block) + blksize;
block->next = set->blocks;
set->blocks = block;
}
/*
* OK, do the allocation
*/
chunk = (AllocChunk)(block->freeptr);
chunk->aset = (void *)set;
chunk->size = chunk_size;
block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
Assert(block->freeptr <= block->endptr);
return AllocChunkGetPointer(chunk);
}
@ -325,14 +408,14 @@ AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size)
* Maybe the allocated area already is >= the new size.
*
*/
if (AllocPointerGetSize(pointer) >= size)
oldsize = AllocPointerGetSize(pointer);
if (oldsize >= size)
return pointer;
/* allocate new pointer */
newPointer = AllocSetAlloc(set, size);
/* fill new memory */
oldsize = AllocPointerGetSize(pointer);
memmove(newPointer, pointer, (oldsize < size) ? oldsize : size);
/* free old pointer */

View File

@ -15,7 +15,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: memutils.h,v 1.23 1999/03/25 19:05:19 tgl Exp $
* $Id: memutils.h,v 1.24 1999/05/22 23:19:36 tgl Exp $
*
* NOTES
* some of the information in this file will be moved to
@ -96,7 +96,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
* an allocation is requested for a set, memory is allocated and a
* pointer is returned. Subsequently, this memory may be freed or
* reallocated. In addition, an allocation set may be reset which
* will cause all allocated memory to be freed.
* will cause all memory allocated within it to be freed.
*
* Allocations may occur in four different modes. The mode of
* allocation does not affect the behavior of allocations except in
@ -109,7 +109,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
* and freed very frequently. This is a good choice when allocation
* characteristics are unknown. This is the default mode.
*
* "Static" mode attemts to allocate space as efficiently as possible
* "Static" mode attempts to allocate space as efficiently as possible
* without regard to freeing memory. This mode should be chosen only
* when it is known that many allocations will occur but that very
* little of the allocated memory will be explicitly freed.
@ -129,7 +129,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
* Allocation sets are not automatically reset on a system reset.
* Higher level code is responsible for cleaning up.
*
* There may other modes in the future.
* There may be other modes in the future.
*/
/*
@ -176,7 +176,9 @@ typedef AllocBlockData *AllocBlock;
* The prefix of each piece of memory in an AllocBlock
*/
typedef struct AllocChunkData {
/* aset is the owning aset if allocated, or the freelist link if free */
void *aset;
/* size is always the chunk size */
Size size;
} AllocChunkData;
@ -189,7 +191,8 @@ typedef AllocChunkData *AllocChunk;
typedef struct AllocSetData
{
struct AllocBlockData *blocks;
struct AllocChunkData *freelist[8];
#define ALLOCSET_NUM_FREELISTS 8
struct AllocChunkData *freelist[ALLOCSET_NUM_FREELISTS];
/* Note: this will change in the future to support other modes */
} AllocSetData;