77 *
88 *
99 * IDENTIFICATION
10- * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.20 1999/07/17 20:18:13 momjian Exp $
10+ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.21 1999/08/24 20:11:17 tgl Exp $
1111 *
1212 * NOTE:
1313 *This is a new (Feb. 05, 1999) implementation of the allocation set
1414 *routines. AllocSet...() does not use OrderedSet...() any more.
1515 *Instead it manages allocations in a block pool by itself, combining
16- *many small allocations in a few bigger blocks. AllocSetFree()does
17- *never free() memory really. It just add's the free'd area to some
16+ *many small allocations in a few bigger blocks. AllocSetFree()normally
17+ *doesn't free() memory really. It just add's the free'd area to some
1818 *list for later reuse by AllocSetAlloc(). All memory blocks are free()'d
1919 *at once on AllocSetReset(), which happens when the memory context gets
2020 *destroyed.
2121 *Jan Wieck
22+ *
23+ *Performance improvement from Tom Lane, 8/99: for extremely large request
24+ *sizes, we do want to be able to give the memory back to free() as soon
25+ *as it is pfree()'d. Otherwise we risk tying up a lot of memory in
26+ *freelist entries that might never be usable. This is specially needed
27+ *when the caller is repeatedly repalloc()'ing a block bigger and bigger;
28+ *the previous instances of the block were guaranteed to be wasted until
29+ *AllocSetReset() under the old way.
2230 *-------------------------------------------------------------------------
2331 */
2432#include "postgres.h"
3442/*--------------------
3543 * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
3644 * for k = 0 .. ALLOCSET_NUM_FREELISTS-2.
37- * The last freelist holds all larger chunks.
45+ * The last freelist holds all larger free chunks. Those chunks come in
46+ * varying sizes depending on the request size, whereas smaller chunks are
47+ * coerced to powers of 2 to improve their "recyclability".
3848 *
3949 * CAUTION: ALLOC_MINBITS must be large enough so that
4050 * 1<<ALLOC_MINBITS is at least MAXALIGN,
5161 * The first block allocated for an allocset has size ALLOC_MIN_BLOCK_SIZE.
5262 * Each time we have to allocate another block, we double the block size
5363 * (if possible, and without exceeding ALLOC_MAX_BLOCK_SIZE), so as to reduce
54- * the load on" malloc" .
64+ * thebookkeeping load on malloc() .
5565 *
5666 * Blocks allocated to hold oversize chunks do not follow this rule, however;
57- * they are just however big they need to be.
67+ * they are just however big they need to be to hold that single chunk.
68+ * AllocSetAlloc has some freedom about whether to consider a chunk larger
69+ * than ALLOC_SMALLCHUNK_LIMIT to be "oversize". We require all chunks
70+ * >= ALLOC_BIGCHUNK_LIMIT to be allocated as single-chunk blocks; those
71+ * chunks are treated specially by AllocSetFree and AllocSetRealloc. For
72+ * request sizes between ALLOC_SMALLCHUNK_LIMIT and ALLOC_BIGCHUNK_LIMIT,
73+ * AllocSetAlloc has discretion whether to put the request into an existing
74+ * block or make a single-chunk block.
75+ *
76+ * We must have ALLOC_MIN_BLOCK_SIZE > ALLOC_SMALLCHUNK_LIMIT and
77+ * ALLOC_BIGCHUNK_LIMIT > ALLOC_SMALLCHUNK_LIMIT.
5878 *--------------------
5979 */
6080
61- #define ALLOC_MIN_BLOCK_SIZE 8192
81+ #define ALLOC_MIN_BLOCK_SIZE (8 * 1024)
6282#define ALLOC_MAX_BLOCK_SIZE (8 * 1024 * 1024)
6383
84+ #define ALLOC_BIGCHUNK_LIMIT (64 * 1024)
85+ /* Chunks >= ALLOC_BIGCHUNK_LIMIT are immediately free()d by pfree() */
6486
6587#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
6688#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))
@@ -104,13 +126,6 @@ AllocSetFreeIndex(Size size)
104126 * Public routines
105127 */
106128
107- /*
108- *AllocPointerIsValid(pointer)
109- *AllocSetIsValid(set)
110- *
111- *.. are now macros in aset.h -cim 4/27/91
112- */
113-
114129/*
115130 * AllocSetInit
116131 *Initializes given allocation set.
@@ -141,7 +156,7 @@ AllocSetInit(AllocSet set, AllocMode mode, Size limit)
141156
142157/*
143158 * AllocSetReset
144- *Frees memory which is allocated in the given set.
159+ *Freesall memory which is allocated in the given set.
145160 *
146161 * Exceptions:
147162 *BadArg if set is invalid.
@@ -195,7 +210,7 @@ AllocSetAlloc(AllocSet set, Size size)
195210{
196211AllocBlock block ;
197212AllocChunk chunk ;
198- AllocChunk freeref = NULL ;
213+ AllocChunk priorfree = NULL ;
199214int fidx ;
200215Size chunk_size ;
201216Size blksize ;
@@ -212,7 +227,7 @@ AllocSetAlloc(AllocSet set, Size size)
212227{
213228if (chunk -> size >=size )
214229break ;
215- freeref = chunk ;
230+ priorfree = chunk ;
216231}
217232
218233/*
@@ -222,10 +237,10 @@ AllocSetAlloc(AllocSet set, Size size)
222237 */
223238if (chunk != NULL )
224239{
225- if (freeref == NULL )
240+ if (priorfree == NULL )
226241set -> freelist [fidx ]= (AllocChunk )chunk -> aset ;
227242else
228- freeref -> aset = chunk -> aset ;
243+ priorfree -> aset = chunk -> aset ;
229244
230245chunk -> aset = (void * )set ;
231246return AllocChunkGetPointer (chunk );
@@ -241,22 +256,23 @@ AllocSetAlloc(AllocSet set, Size size)
241256Assert (chunk_size >=size );
242257
243258/*
244- * If there is enough room in the active allocation block, always
245- * allocate the chunk there.
259+ * If there is enough room in the active allocation block, *and*
260+ * the chunk is less than ALLOC_BIGCHUNK_LIMIT, put the chunk
261+ * into the active allocation block.
246262 */
247-
248263if ((block = set -> blocks )!= NULL )
249264{
250265Size have_free = block -> endptr - block -> freeptr ;
251266
252- if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ ))
267+ if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ )||
268+ chunk_size >=ALLOC_BIGCHUNK_LIMIT )
253269block = NULL ;
254270}
255271
256272/*
257273 * Otherwise, if requested size exceeds smallchunk limit, allocate an
258- * entire separate block for this allocation
259- *
274+ * entire separate block for this allocation. In particular, we will
275+ * always take this path if the requested size exceeds bigchunk limit.
260276 */
261277if (block == NULL && size > ALLOC_SMALLCHUNK_LIMIT )
262278{
@@ -290,7 +306,7 @@ AllocSetAlloc(AllocSet set, Size size)
290306}
291307
292308/*
293- * Time to create a new regular block?
309+ * Time to create a new regular(multi-chunk) block?
294310 */
295311if (block == NULL )
296312{
@@ -364,18 +380,49 @@ AllocSetAlloc(AllocSet set, Size size)
364380void
365381AllocSetFree (AllocSet set ,AllocPointer pointer )
366382{
367- int fidx ;
368383AllocChunk chunk ;
369384
370385/* AssertArg(AllocSetIsValid(set)); */
371386/* AssertArg(AllocPointerIsValid(pointer)); */
372387AssertArg (AllocSetContains (set ,pointer ));
373388
374389chunk = AllocPointerGetChunk (pointer );
375- fidx = AllocSetFreeIndex (chunk -> size );
376390
377- chunk -> aset = (void * )set -> freelist [fidx ];
378- set -> freelist [fidx ]= chunk ;
391+ if (chunk -> size >=ALLOC_BIGCHUNK_LIMIT )
392+ {
393+ /* Big chunks are certain to have been allocated as single-chunk
394+ * blocks. Find the containing block and return it to malloc().
395+ */
396+ AllocBlock block = set -> blocks ;
397+ AllocBlock prevblock = NULL ;
398+
399+ while (block != NULL )
400+ {
401+ if (chunk == (AllocChunk ) (((char * )block )+ ALLOC_BLOCKHDRSZ ))
402+ break ;
403+ prevblock = block ;
404+ block = block -> next ;
405+ }
406+ if (block == NULL )
407+ elog (ERROR ,"AllocSetFree: cannot find block containing chunk" );
408+ /* let's just make sure chunk is the only one in the block */
409+ Assert (block -> freeptr == ((char * )block )+
410+ (chunk -> size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ ));
411+ /* OK, remove block from aset's list and free it */
412+ if (prevblock == NULL )
413+ set -> blocks = block -> next ;
414+ else
415+ prevblock -> next = block -> next ;
416+ free (block );
417+ }
418+ else
419+ {
420+ /* Normal case, put the chunk into appropriate freelist */
421+ int fidx = AllocSetFreeIndex (chunk -> size );
422+
423+ chunk -> aset = (void * )set -> freelist [fidx ];
424+ set -> freelist [fidx ]= chunk ;
425+ }
379426}
380427
381428/*
@@ -393,7 +440,6 @@ AllocSetFree(AllocSet set, AllocPointer pointer)
393440AllocPointer
394441AllocSetRealloc (AllocSet set ,AllocPointer pointer ,Size size )
395442{
396- AllocPointer newPointer ;
397443Size oldsize ;
398444
399445/* AssertArg(AllocSetIsValid(set)); */
@@ -402,23 +448,70 @@ AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size)
402448
403449/*
404450 * Chunk sizes are aligned to power of 2 on AllocSetAlloc(). Maybe the
405- * allocated area already is >= the new size.
406- *
451+ * allocated area already is >= the new size. (In particular, we
452+ * always fall out here if the requested size is a decrease.)
407453 */
408454oldsize = AllocPointerGetSize (pointer );
409455if (oldsize >=size )
410456return pointer ;
411457
412- /* allocate new pointer */
413- newPointer = AllocSetAlloc (set ,size );
458+ if (oldsize >=ALLOC_BIGCHUNK_LIMIT )
459+ {
460+ /*
461+ * If the chunk is already >= bigchunk limit, then it must have been
462+ * allocated as a single-chunk block. Find the containing block and
463+ * use realloc() to make it bigger with minimum space wastage.
464+ */
465+ AllocChunk chunk = AllocPointerGetChunk (pointer );
466+ AllocBlock block = set -> blocks ;
467+ AllocBlock prevblock = NULL ;
468+ Size blksize ;
414469
415- /* fill new memory */
416- memmove (newPointer ,pointer , (oldsize < size ) ?oldsize :size );
470+ while (block != NULL )
471+ {
472+ if (chunk == (AllocChunk ) (((char * )block )+ ALLOC_BLOCKHDRSZ ))
473+ break ;
474+ prevblock = block ;
475+ block = block -> next ;
476+ }
477+ if (block == NULL )
478+ elog (ERROR ,"AllocSetRealloc: cannot find block containing chunk" );
479+ /* let's just make sure chunk is the only one in the block */
480+ Assert (block -> freeptr == ((char * )block )+
481+ (chunk -> size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ ));
482+
483+ /* Do the realloc */
484+ size = MAXALIGN (size );
485+ blksize = size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ ;
486+ block = (AllocBlock )realloc (block ,blksize );
487+ if (block == NULL )
488+ elog (FATAL ,"Memory exhausted in AllocSetReAlloc()" );
489+ block -> freeptr = block -> endptr = ((char * )block )+ blksize ;
417490
418- /* free old pointer */
419- AllocSetFree (set ,pointer );
491+ /* Update pointers since block has likely been moved */
492+ chunk = (AllocChunk ) (((char * )block )+ ALLOC_BLOCKHDRSZ );
493+ if (prevblock == NULL )
494+ set -> blocks = block ;
495+ else
496+ prevblock -> next = block ;
497+ chunk -> size = size ;
498+ return AllocChunkGetPointer (chunk );
499+ }
500+ else
501+ {
502+ /* Normal small-chunk case: just do it by brute force. */
503+
504+ /* allocate new chunk */
505+ AllocPointer newPointer = AllocSetAlloc (set ,size );
506+
507+ /* transfer existing data (certain to fit) */
508+ memcpy (newPointer ,pointer ,oldsize );
420509
421- return newPointer ;
510+ /* free old chunk */
511+ AllocSetFree (set ,pointer );
512+
513+ return newPointer ;
514+ }
422515}
423516
424517/*