Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d52a91a

Browse files
committed
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.
1 parent f9f90b2 commit d52a91a

File tree

2 files changed

+122
-36
lines changed

2 files changed

+122
-36
lines changed

src/backend/utils/mmgr/aset.c

+114-31
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
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
@@ -16,7 +16,7 @@
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
*-------------------------------------------------------------------------
@@ -38,8 +38,36 @@
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
{
6694
int 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

75106
return idx;
@@ -174,6 +205,7 @@ AllocSetAlloc(AllocSet set, Size size)
174205
AllocChunk freeref = NULL;
175206
int fidx;
176207
Size chunk_size;
208+
Size blksize;
177209

178210
AssertArg(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+
* If one 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+
{
219270
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
220271
block = (AllocBlock) malloc(blksize);
221272
if (block == NULL)
222273
elog(FATAL, "Memory exhausted in AllocSetAlloc()");
223274
block->aset = set;
224-
block->freeptr = block->endptr = ((char *)block) + ALLOC_BLOCKHDRSZ;
275+
block->freeptr = block->endptr = ((char *)block) + blksize;
225276

226277
chunk = (AllocChunk) (((char *)block) + ALLOC_BLOCKHDRSZ);
227278
chunk->aset = set;
228279
chunk->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+
*/
230285
if (set->blocks != NULL)
231286
{
232287
block->next = set->blocks->next;
@@ -241,33 +296,61 @@ AllocSetAlloc(AllocSet set, Size size)
241296
return 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+
*/
254302
if (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+
257336
if (block == NULL)
258337
elog(FATAL, "Memory exhausted in AllocSetAlloc()");
259338
block->aset = set;
260-
block->next = set->blocks;
261339
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;
263342

264343
set->blocks = block;
265344
}
266345

346+
/*
347+
* OK, do the allocation
348+
*/
267349
chunk = (AllocChunk)(block->freeptr);
268350
chunk->aset = (void *)set;
269351
chunk->size = chunk_size;
270352
block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
353+
Assert(block->freeptr <= block->endptr);
271354

272355
return 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)
329413
return pointer;
330414

331415
/* allocate new pointer */
332416
newPointer = AllocSetAlloc(set, size);
333417

334418
/* fill new memory */
335-
oldsize = AllocPointerGetSize(pointer);
336419
memmove(newPointer, pointer, (oldsize < size) ? oldsize : size);
337420

338421
/* free old pointer */

src/include/utils/memutils.h

+8-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
* Copyright (c) 1994, Regents of the University of California
1717
*
18-
* $Id: memutils.h,v 1.23 1999/03/25 19:05:19 tgl Exp $
18+
* $Id: memutils.h,v 1.24 1999/05/22 23:19:36 tgl Exp $
1919
*
2020
* NOTES
2121
* some of the information in this file will be moved to
@@ -96,7 +96,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
9696
* an allocation is requested for a set, memory is allocated and a
9797
* pointer is returned. Subsequently, this memory may be freed or
9898
* reallocated. In addition, an allocation set may be reset which
99-
* will cause all allocated memory to be freed.
99+
* will cause all memory allocated within it to be freed.
100100
*
101101
* Allocations may occur in four different modes. The mode of
102102
* allocation does not affect the behavior of allocations except in
@@ -109,7 +109,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
109109
* and freed very frequently. This is a good choice when allocation
110110
* characteristics are unknown. This is the default mode.
111111
*
112-
* "Static" mode attemts to allocate space as efficiently as possible
112+
* "Static" mode attempts to allocate space as efficiently as possible
113113
* without regard to freeing memory. This mode should be chosen only
114114
* when it is known that many allocations will occur but that very
115115
* little of the allocated memory will be explicitly freed.
@@ -129,7 +129,7 @@ extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
129129
* Allocation sets are not automatically reset on a system reset.
130130
* Higher level code is responsible for cleaning up.
131131
*
132-
* There may other modes in the future.
132+
* There may be other modes in the future.
133133
*/
134134

135135
/*
@@ -176,7 +176,9 @@ typedef AllocBlockData *AllocBlock;
176176
* The prefix of each piece of memory in an AllocBlock
177177
*/
178178
typedef struct AllocChunkData {
179+
/* aset is the owning aset if allocated, or the freelist link if free */
179180
void *aset;
181+
/* size is always the chunk size */
180182
Size size;
181183
} AllocChunkData;
182184

@@ -189,7 +191,8 @@ typedef AllocChunkData *AllocChunk;
189191
typedef struct AllocSetData
190192
{
191193
struct AllocBlockData *blocks;
192-
struct AllocChunkData *freelist[8];
194+
#define ALLOCSET_NUM_FREELISTS 8
195+
struct AllocChunkData *freelist[ALLOCSET_NUM_FREELISTS];
193196
/* Note: this will change in the future to support other modes */
194197
} AllocSetData;
195198

0 commit comments

Comments
 (0)