77 *
88 *
99 * IDENTIFICATION
10- * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.14 1999/02/13 23:20:09 momjian Exp $
10+ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.15 1999/05/22 23:19:37 tgl Exp $
1111 *
1212 * NOTE:
1313 *This is a new (Feb. 05, 1999) implementation of the allocation set
1616 *many small allocations in a few bigger blocks. AllocSetFree() does
1717 *never 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
19- *on AllocSetReset() at once, what happens when the memory context gets
19+ *at once on AllocSetReset(), which happens when the memory context gets
2020 *destroyed.
2121 *Jan Wieck
2222 *-------------------------------------------------------------------------
3838#undef realloc
3939
4040
41- #define ALLOC_BLOCK_SIZE 8192
42- #define ALLOC_CHUNK_LIMIT 512
41+ /*--------------------
42+ * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
43+ * for k = 0 .. ALLOCSET_NUM_FREELISTS-2.
44+ * The last freelist holds all larger chunks.
45+ *
46+ * CAUTION: ALLOC_MINBITS must be large enough so that
47+ * 1<<ALLOC_MINBITS is at least MAXALIGN,
48+ * or we may fail to align the smallest chunks adequately.
49+ * 16-byte alignment is enough on all currently known machines.
50+ *--------------------
51+ */
52+
53+ #define ALLOC_MINBITS 4/* smallest chunk size is 16 bytes */
54+ #define ALLOC_SMALLCHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-2+ALLOC_MINBITS))
55+ /* Size of largest chunk that we use a fixed size for */
56+
57+ /*--------------------
58+ * The first block allocated for an allocset has size ALLOC_MIN_BLOCK_SIZE.
59+ * Each time we have to allocate another block, we double the block size
60+ * (if possible, and without exceeding ALLOC_MAX_BLOCK_SIZE), so as to reduce
61+ * the load on "malloc".
62+ *
63+ * Blocks allocated to hold oversize chunks do not follow this rule, however;
64+ * they are just however big they need to be.
65+ *--------------------
66+ */
67+
68+ #define ALLOC_MIN_BLOCK_SIZE 8192
69+ #define ALLOC_MAX_BLOCK_SIZE (8 * 1024 * 1024)
70+
4371
4472#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
4573#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))
@@ -65,11 +93,14 @@ AllocSetFreeIndex(Size size)
6593{
6694int idx = 0 ;
6795
68- size = (size - 1 ) >>4 ;
69- while (size != 0 && idx < 7 )
96+ if (size > 0 )
7097{
71- idx ++ ;
72- size >>=1 ;
98+ size = (size - 1 ) >>ALLOC_MINBITS ;
99+ while (size != 0 && idx < ALLOCSET_NUM_FREELISTS - 1 )
100+ {
101+ idx ++ ;
102+ size >>=1 ;
103+ }
73104}
74105
75106return idx ;
@@ -174,6 +205,7 @@ AllocSetAlloc(AllocSet set, Size size)
174205AllocChunk freeref = NULL ;
175206int fidx ;
176207Size chunk_size ;
208+ Size blksize ;
177209
178210AssertArg (AllocSetIsValid (set ));
179211
@@ -191,7 +223,7 @@ AllocSetAlloc(AllocSet set, Size size)
191223}
192224
193225/*
194- * If found, remove it from the free list, make it again
226+ * Ifone is found, remove it from the free list, make it again
195227 * a member of the alloc set and return it's data address.
196228 *
197229 */
@@ -207,26 +239,49 @@ AllocSetAlloc(AllocSet set, Size size)
207239}
208240
209241/*
210- * If requested size exceeds smallchunk limit, allocate a separate,
211- * entire used block for this allocation
212- *
242+ * Choose the actual chunk size to allocate.
213243 */
214- if (size > ALLOC_CHUNK_LIMIT )
244+ if (size > ALLOC_SMALLCHUNK_LIMIT )
245+ chunk_size = MAXALIGN (size );
246+ else
247+ chunk_size = 1 << (fidx + ALLOC_MINBITS );
248+ Assert (chunk_size >=size );
249+
250+ /*
251+ * If there is enough room in the active allocation block,
252+ * always allocate the chunk there.
253+ */
254+
255+ if ((block = set -> blocks )!= NULL )
215256{
216- Size blksize ;
257+ Size have_free = block -> endptr - block -> freeptr ;
217258
218- chunk_size = MAXALIGN (size );
259+ if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ ))
260+ block = NULL ;
261+ }
262+
263+ /*
264+ * Otherwise, if requested size exceeds smallchunk limit,
265+ * allocate an entire separate block for this allocation
266+ *
267+ */
268+ if (block == NULL && size > ALLOC_SMALLCHUNK_LIMIT )
269+ {
219270blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ ;
220271block = (AllocBlock )malloc (blksize );
221272if (block == NULL )
222273elog (FATAL ,"Memory exhausted in AllocSetAlloc()" );
223274block -> aset = set ;
224- block -> freeptr = block -> endptr = ((char * )block )+ ALLOC_BLOCKHDRSZ ;
275+ block -> freeptr = block -> endptr = ((char * )block )+ blksize ;
225276
226277chunk = (AllocChunk ) (((char * )block )+ ALLOC_BLOCKHDRSZ );
227278chunk -> aset = set ;
228279chunk -> size = chunk_size ;
229280
281+ /*
282+ * Try to stick the block underneath the active allocation block,
283+ * so that we don't lose the use of the space remaining therein.
284+ */
230285if (set -> blocks != NULL )
231286{
232287block -> next = set -> blocks -> next ;
@@ -241,33 +296,61 @@ AllocSetAlloc(AllocSet set, Size size)
241296return AllocChunkGetPointer (chunk );
242297}
243298
244- chunk_size = 16 <<fidx ;
245-
246- if ((block = set -> blocks )!= NULL )
247- {
248- Size have_free = block -> endptr - block -> freeptr ;
249-
250- if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ ))
251- block = NULL ;
252- }
253-
299+ /*
300+ * Time to create a new regular block?
301+ */
254302if (block == NULL )
255303{
256- block = (AllocBlock )malloc (ALLOC_BLOCK_SIZE );
304+ if (set -> blocks == NULL )
305+ {
306+ blksize = ALLOC_MIN_BLOCK_SIZE ;
307+ block = (AllocBlock )malloc (blksize );
308+ }
309+ else
310+ {
311+ /* Get size of prior block */
312+ blksize = set -> blocks -> endptr - ((char * )set -> blocks );
313+ /* Special case: if very first allocation was for a large chunk,
314+ * could have a funny-sized top block. Do something reasonable.
315+ */
316+ if (blksize < ALLOC_MIN_BLOCK_SIZE )
317+ blksize = ALLOC_MIN_BLOCK_SIZE ;
318+ /* Crank it up, but not past max */
319+ blksize <<=1 ;
320+ if (blksize > ALLOC_MAX_BLOCK_SIZE )
321+ blksize = ALLOC_MAX_BLOCK_SIZE ;
322+ /* Try to allocate it */
323+ block = (AllocBlock )malloc (blksize );
324+ /*
325+ * We could be asking for pretty big blocks here, so cope if
326+ * malloc fails. But give up if there's less than a meg or so
327+ * available...
328+ */
329+ while (block == NULL && blksize > 1024 * 1024 )
330+ {
331+ blksize >>=1 ;
332+ block = (AllocBlock )malloc (blksize );
333+ }
334+ }
335+
257336if (block == NULL )
258337elog (FATAL ,"Memory exhausted in AllocSetAlloc()" );
259338block -> aset = set ;
260- block -> next = set -> blocks ;
261339block -> freeptr = ((char * )block )+ ALLOC_BLOCKHDRSZ ;
262- block -> endptr = ((char * )block )+ ALLOC_BLOCK_SIZE ;
340+ block -> endptr = ((char * )block )+ blksize ;
341+ block -> next = set -> blocks ;
263342
264343set -> blocks = block ;
265344}
266345
346+ /*
347+ * OK, do the allocation
348+ */
267349chunk = (AllocChunk )(block -> freeptr );
268350chunk -> aset = (void * )set ;
269351chunk -> size = chunk_size ;
270352block -> freeptr += (chunk_size + ALLOC_CHUNKHDRSZ );
353+ Assert (block -> freeptr <=block -> endptr );
271354
272355return AllocChunkGetPointer (chunk );
273356}
@@ -325,14 +408,14 @@ AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size)
325408 * Maybe the allocated area already is >= the new size.
326409 *
327410 */
328- if (AllocPointerGetSize (pointer ) >=size )
411+ oldsize = AllocPointerGetSize (pointer );
412+ if (oldsize >=size )
329413return pointer ;
330414
331415/* allocate new pointer */
332416newPointer = AllocSetAlloc (set ,size );
333417
334418/* fill new memory */
335- oldsize = AllocPointerGetSize (pointer );
336419memmove (newPointer ,pointer , (oldsize < size ) ?oldsize :size );
337420
338421/* free old pointer */