Commit d52a91a5 authored by Tom Lane's avatar Tom Lane

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.
parent f9f90b21
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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: * NOTE:
* This is a new (Feb. 05, 1999) implementation of the allocation set * This is a new (Feb. 05, 1999) implementation of the allocation set
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* many small allocations in a few bigger blocks. AllocSetFree() does * many small allocations in a few bigger blocks. AllocSetFree() does
* never free() memory really. It just add's the free'd area to some * 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 * 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. * destroyed.
* Jan Wieck * Jan Wieck
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
...@@ -38,8 +38,36 @@ ...@@ -38,8 +38,36 @@
#undef realloc #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_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData)) #define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))
...@@ -65,12 +93,15 @@ AllocSetFreeIndex(Size size) ...@@ -65,12 +93,15 @@ AllocSetFreeIndex(Size size)
{ {
int idx = 0; int idx = 0;
size = (size - 1) >> 4; if (size > 0)
while (size != 0 && idx < 7) {
size = (size - 1) >> ALLOC_MINBITS;
while (size != 0 && idx < ALLOCSET_NUM_FREELISTS-1)
{ {
idx++; idx++;
size >>= 1; size >>= 1;
} }
}
return idx; return idx;
} }
...@@ -174,6 +205,7 @@ AllocSetAlloc(AllocSet set, Size size) ...@@ -174,6 +205,7 @@ AllocSetAlloc(AllocSet set, Size size)
AllocChunk freeref = NULL; AllocChunk freeref = NULL;
int fidx; int fidx;
Size chunk_size; Size chunk_size;
Size blksize;
AssertArg(AllocSetIsValid(set)); AssertArg(AllocSetIsValid(set));
...@@ -191,7 +223,7 @@ AllocSetAlloc(AllocSet set, Size size) ...@@ -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. * a member of the alloc set and return it's data address.
* *
*/ */
...@@ -207,26 +239,49 @@ AllocSetAlloc(AllocSet set, Size size) ...@@ -207,26 +239,49 @@ AllocSetAlloc(AllocSet set, Size size)
} }
/* /*
* If requested size exceeds smallchunk limit, allocate a separate, * Choose the actual chunk size to allocate.
* entire used block for this allocation
*
*/ */
if (size > ALLOC_CHUNK_LIMIT) 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 blksize; Size have_free = block->endptr - block->freeptr;
chunk_size = MAXALIGN(size); 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 (block == NULL && size > ALLOC_SMALLCHUNK_LIMIT)
{
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
block = (AllocBlock) malloc(blksize); block = (AllocBlock) malloc(blksize);
if (block == NULL) if (block == NULL)
elog(FATAL, "Memory exhausted in AllocSetAlloc()"); elog(FATAL, "Memory exhausted in AllocSetAlloc()");
block->aset = set; 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 = (AllocChunk) (((char *)block) + ALLOC_BLOCKHDRSZ);
chunk->aset = set; chunk->aset = set;
chunk->size = chunk_size; 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) if (set->blocks != NULL)
{ {
block->next = set->blocks->next; block->next = set->blocks->next;
...@@ -241,33 +296,61 @@ AllocSetAlloc(AllocSet set, Size size) ...@@ -241,33 +296,61 @@ AllocSetAlloc(AllocSet set, Size size)
return AllocChunkGetPointer(chunk); return AllocChunkGetPointer(chunk);
} }
chunk_size = 16 << fidx; /*
* Time to create a new regular block?
if ((block = set->blocks) != NULL) */
if (block == NULL)
{ {
Size have_free = block->endptr - block->freeptr; if (set->blocks == NULL)
{
if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ)) blksize = ALLOC_MIN_BLOCK_SIZE;
block = NULL; block = (AllocBlock) malloc(blksize);
} }
else
if (block == NULL)
{ {
block = (AllocBlock) malloc(ALLOC_BLOCK_SIZE); /* 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) if (block == NULL)
elog(FATAL, "Memory exhausted in AllocSetAlloc()"); elog(FATAL, "Memory exhausted in AllocSetAlloc()");
block->aset = set; block->aset = set;
block->next = set->blocks;
block->freeptr = ((char *)block) + ALLOC_BLOCKHDRSZ; 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; set->blocks = block;
} }
/*
* OK, do the allocation
*/
chunk = (AllocChunk)(block->freeptr); chunk = (AllocChunk)(block->freeptr);
chunk->aset = (void *)set; chunk->aset = (void *)set;
chunk->size = chunk_size; chunk->size = chunk_size;
block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ); block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
Assert(block->freeptr <= block->endptr);
return AllocChunkGetPointer(chunk); return AllocChunkGetPointer(chunk);
} }
...@@ -325,14 +408,14 @@ AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size) ...@@ -325,14 +408,14 @@ AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size)
* Maybe the allocated area already is >= the new size. * Maybe the allocated area already is >= the new size.
* *
*/ */
if (AllocPointerGetSize(pointer) >= size) oldsize = AllocPointerGetSize(pointer);
if (oldsize >= size)
return pointer; return pointer;
/* allocate new pointer */ /* allocate new pointer */
newPointer = AllocSetAlloc(set, size); newPointer = AllocSetAlloc(set, size);
/* fill new memory */ /* fill new memory */
oldsize = AllocPointerGetSize(pointer);
memmove(newPointer, pointer, (oldsize < size) ? oldsize : size); memmove(newPointer, pointer, (oldsize < size) ? oldsize : size);
/* free old pointer */ /* free old pointer */
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* some of the information in this file will be moved to * some of the information in this file will be moved to
...@@ -96,7 +96,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set); ...@@ -96,7 +96,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
* an allocation is requested for a set, memory is allocated and a * an allocation is requested for a set, memory is allocated and a
* pointer is returned. Subsequently, this memory may be freed or * pointer is returned. Subsequently, this memory may be freed or
* reallocated. In addition, an allocation set may be reset which * 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 * Allocations may occur in four different modes. The mode of
* allocation does not affect the behavior of allocations except in * allocation does not affect the behavior of allocations except in
...@@ -109,7 +109,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set); ...@@ -109,7 +109,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
* and freed very frequently. This is a good choice when allocation * and freed very frequently. This is a good choice when allocation
* characteristics are unknown. This is the default mode. * 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 * without regard to freeing memory. This mode should be chosen only
* when it is known that many allocations will occur but that very * when it is known that many allocations will occur but that very
* little of the allocated memory will be explicitly freed. * little of the allocated memory will be explicitly freed.
...@@ -129,7 +129,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set); ...@@ -129,7 +129,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
* Allocation sets are not automatically reset on a system reset. * Allocation sets are not automatically reset on a system reset.
* Higher level code is responsible for cleaning up. * 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; ...@@ -176,7 +176,9 @@ typedef AllocBlockData *AllocBlock;
* The prefix of each piece of memory in an AllocBlock * The prefix of each piece of memory in an AllocBlock
*/ */
typedef struct AllocChunkData { typedef struct AllocChunkData {
/* aset is the owning aset if allocated, or the freelist link if free */
void *aset; void *aset;
/* size is always the chunk size */
Size size; Size size;
} AllocChunkData; } AllocChunkData;
...@@ -189,7 +191,8 @@ typedef AllocChunkData *AllocChunk; ...@@ -189,7 +191,8 @@ typedef AllocChunkData *AllocChunk;
typedef struct AllocSetData typedef struct AllocSetData
{ {
struct AllocBlockData *blocks; 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 */ /* Note: this will change in the future to support other modes */
} AllocSetData; } AllocSetData;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment