Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2b67dc5

Browse files
committed
Alter AllocSet routines so that requests larger than
ALLOC_BIGCHUNK_LIMIT are always allocated as separate malloc() blocks,and are free()d immediately upon pfree(). Also, if such a chunk is enlargedwith repalloc(), translate the operation into a realloc() so as tominimize memory usage. Of course, these large chunks still get freedautomatically if the alloc set is reset.I have set ALLOC_BIGCHUNK_LIMIT at 64K for now, but perhaps anothersize would be better?
1 parent1b81fd7 commit2b67dc5

File tree

2 files changed

+171
-69
lines changed

2 files changed

+171
-69
lines changed

‎src/backend/utils/mmgr/aset.c

Lines changed: 133 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,26 @@
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"
@@ -34,7 +42,9 @@
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,
@@ -51,16 +61,28 @@
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+
* thebookkeepingload 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-
#defineALLOC_MIN_BLOCK_SIZE8192
81+
#defineALLOC_MIN_BLOCK_SIZE(8 * 1024)
6282
#defineALLOC_MAX_BLOCK_SIZE(8 * 1024 * 1024)
6383

84+
#defineALLOC_BIGCHUNK_LIMIT(64 * 1024)
85+
/* Chunks >= ALLOC_BIGCHUNK_LIMIT are immediately free()d by pfree() */
6486

6587
#defineALLOC_BLOCKHDRSZMAXALIGN(sizeof(AllocBlockData))
6688
#defineALLOC_CHUNKHDRSZMAXALIGN(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+
*Freesallmemory 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
{
196211
AllocBlockblock;
197212
AllocChunkchunk;
198-
AllocChunkfreeref=NULL;
213+
AllocChunkpriorfree=NULL;
199214
intfidx;
200215
Sizechunk_size;
201216
Sizeblksize;
@@ -212,7 +227,7 @@ AllocSetAlloc(AllocSet set, Size size)
212227
{
213228
if (chunk->size >=size)
214229
break;
215-
freeref=chunk;
230+
priorfree=chunk;
216231
}
217232

218233
/*
@@ -222,10 +237,10 @@ AllocSetAlloc(AllocSet set, Size size)
222237
*/
223238
if (chunk!=NULL)
224239
{
225-
if (freeref==NULL)
240+
if (priorfree==NULL)
226241
set->freelist[fidx]= (AllocChunk)chunk->aset;
227242
else
228-
freeref->aset=chunk->aset;
243+
priorfree->aset=chunk->aset;
229244

230245
chunk->aset= (void*)set;
231246
returnAllocChunkGetPointer(chunk);
@@ -241,22 +256,23 @@ AllocSetAlloc(AllocSet set, Size size)
241256
Assert(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-
248263
if ((block=set->blocks)!=NULL)
249264
{
250265
Sizehave_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)
253269
block=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
*/
261277
if (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
*/
295311
if (block==NULL)
296312
{
@@ -364,18 +380,49 @@ AllocSetAlloc(AllocSet set, Size size)
364380
void
365381
AllocSetFree(AllocSetset,AllocPointerpointer)
366382
{
367-
intfidx;
368383
AllocChunkchunk;
369384

370385
/* AssertArg(AllocSetIsValid(set)); */
371386
/* AssertArg(AllocPointerIsValid(pointer)); */
372387
AssertArg(AllocSetContains(set,pointer));
373388

374389
chunk=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+
AllocBlockblock=set->blocks;
397+
AllocBlockprevblock=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+
intfidx=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)
393440
AllocPointer
394441
AllocSetRealloc(AllocSetset,AllocPointerpointer,Sizesize)
395442
{
396-
AllocPointernewPointer;
397443
Sizeoldsize;
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
*/
408454
oldsize=AllocPointerGetSize(pointer);
409455
if (oldsize >=size)
410456
returnpointer;
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+
AllocChunkchunk=AllocPointerGetChunk(pointer);
466+
AllocBlockblock=set->blocks;
467+
AllocBlockprevblock=NULL;
468+
Sizeblksize;
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+
returnAllocChunkGetPointer(chunk);
499+
}
500+
else
501+
{
502+
/* Normal small-chunk case: just do it by brute force. */
503+
504+
/* allocate new chunk */
505+
AllocPointernewPointer=AllocSetAlloc(set,size);
506+
507+
/* transfer existing data (certain to fit) */
508+
memcpy(newPointer,pointer,oldsize);
420509

421-
returnnewPointer;
510+
/* free old chunk */
511+
AllocSetFree(set,pointer);
512+
513+
returnnewPointer;
514+
}
422515
}
423516

424517
/*

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp