7
7
*
8
8
*
9
9
* 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 $
11
11
*
12
12
* NOTE:
13
13
* This is a new (Feb. 05, 1999) implementation of the allocation set
16
16
* many small allocations in a few bigger blocks. AllocSetFree() does
17
17
* never free() memory really. It just add's the free'd area to some
18
18
* 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
20
20
* destroyed.
21
21
* Jan Wieck
22
22
*-------------------------------------------------------------------------
38
38
#undef realloc
39
39
40
40
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
+
43
71
44
72
#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
45
73
#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))
@@ -65,11 +93,14 @@ AllocSetFreeIndex(Size size)
65
93
{
66
94
int idx = 0 ;
67
95
68
- size = (size - 1 ) >> 4 ;
69
- while (size != 0 && idx < 7 )
96
+ if (size > 0 )
70
97
{
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
+ }
73
104
}
74
105
75
106
return idx ;
@@ -174,6 +205,7 @@ AllocSetAlloc(AllocSet set, Size size)
174
205
AllocChunk freeref = NULL ;
175
206
int fidx ;
176
207
Size chunk_size ;
208
+ Size blksize ;
177
209
178
210
AssertArg (AllocSetIsValid (set ));
179
211
@@ -191,7 +223,7 @@ AllocSetAlloc(AllocSet set, Size size)
191
223
}
192
224
193
225
/*
194
- * If found, remove it from the free list, make it again
226
+ * If one is found, remove it from the free list, make it again
195
227
* a member of the alloc set and return it's data address.
196
228
*
197
229
*/
@@ -207,26 +239,49 @@ AllocSetAlloc(AllocSet set, Size size)
207
239
}
208
240
209
241
/*
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.
213
243
*/
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 )
215
256
{
216
- Size blksize ;
257
+ Size have_free = block -> endptr - block -> freeptr ;
217
258
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
+ {
219
270
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ ;
220
271
block = (AllocBlock ) malloc (blksize );
221
272
if (block == NULL )
222
273
elog (FATAL , "Memory exhausted in AllocSetAlloc()" );
223
274
block -> aset = set ;
224
- block -> freeptr = block -> endptr = ((char * )block ) + ALLOC_BLOCKHDRSZ ;
275
+ block -> freeptr = block -> endptr = ((char * )block ) + blksize ;
225
276
226
277
chunk = (AllocChunk ) (((char * )block ) + ALLOC_BLOCKHDRSZ );
227
278
chunk -> aset = set ;
228
279
chunk -> size = chunk_size ;
229
280
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
+ */
230
285
if (set -> blocks != NULL )
231
286
{
232
287
block -> next = set -> blocks -> next ;
@@ -241,33 +296,61 @@ AllocSetAlloc(AllocSet set, Size size)
241
296
return AllocChunkGetPointer (chunk );
242
297
}
243
298
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
+ */
254
302
if (block == NULL )
255
303
{
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
+
257
336
if (block == NULL )
258
337
elog (FATAL , "Memory exhausted in AllocSetAlloc()" );
259
338
block -> aset = set ;
260
- block -> next = set -> blocks ;
261
339
block -> freeptr = ((char * )block ) + ALLOC_BLOCKHDRSZ ;
262
- block -> endptr = ((char * )block ) + ALLOC_BLOCK_SIZE ;
340
+ block -> endptr = ((char * )block ) + blksize ;
341
+ block -> next = set -> blocks ;
263
342
264
343
set -> blocks = block ;
265
344
}
266
345
346
+ /*
347
+ * OK, do the allocation
348
+ */
267
349
chunk = (AllocChunk )(block -> freeptr );
268
350
chunk -> aset = (void * )set ;
269
351
chunk -> size = chunk_size ;
270
352
block -> freeptr += (chunk_size + ALLOC_CHUNKHDRSZ );
353
+ Assert (block -> freeptr <= block -> endptr );
271
354
272
355
return AllocChunkGetPointer (chunk );
273
356
}
@@ -325,14 +408,14 @@ AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size)
325
408
* Maybe the allocated area already is >= the new size.
326
409
*
327
410
*/
328
- if (AllocPointerGetSize (pointer ) >= size )
411
+ oldsize = AllocPointerGetSize (pointer );
412
+ if (oldsize >= size )
329
413
return pointer ;
330
414
331
415
/* allocate new pointer */
332
416
newPointer = AllocSetAlloc (set , size );
333
417
334
418
/* fill new memory */
335
- oldsize = AllocPointerGetSize (pointer );
336
419
memmove (newPointer , pointer , (oldsize < size ) ? oldsize : size );
337
420
338
421
/* free old pointer */
0 commit comments