Specialize MemoryContextMemAllocated().

An AllocSet doubles the size of allocated blocks (up to maxBlockSize),
which means that the current block can represent half of the total
allocated space for the memory context. But the free space in the
current block may never have been touched, so don't count the
untouched memory as allocated for the purposes of
MemoryContextMemAllocated().

Discussion: https://postgr.es/m/ec63d70b668818255486a83ffadc3aec492c1f57.camel@j-davis.com
This commit is contained in:
Jeff Davis 2020-03-18 15:39:14 -07:00
parent 487e9861d0
commit e00912e11a
5 changed files with 77 additions and 28 deletions

View File

@ -132,6 +132,7 @@ typedef struct AllocSetContext
Size maxBlockSize; /* maximum block size */ Size maxBlockSize; /* maximum block size */
Size nextBlockSize; /* next block size to allocate */ Size nextBlockSize; /* next block size to allocate */
Size allocChunkLimit; /* effective chunk size limit */ Size allocChunkLimit; /* effective chunk size limit */
Size memAllocated; /* track memory allocated for this context */
AllocBlock keeper; /* keep this block over resets */ AllocBlock keeper; /* keep this block over resets */
/* freelist this context could be put in, or -1 if not a candidate: */ /* freelist this context could be put in, or -1 if not a candidate: */
int freeListIndex; /* index in context_freelists[], or -1 */ int freeListIndex; /* index in context_freelists[], or -1 */
@ -272,6 +273,7 @@ static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size);
static void AllocSetReset(MemoryContext context); static void AllocSetReset(MemoryContext context);
static void AllocSetDelete(MemoryContext context); static void AllocSetDelete(MemoryContext context);
static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer); static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
static Size AllocSetMemAllocated(MemoryContext context);
static bool AllocSetIsEmpty(MemoryContext context); static bool AllocSetIsEmpty(MemoryContext context);
static void AllocSetStats(MemoryContext context, static void AllocSetStats(MemoryContext context,
MemoryStatsPrintFunc printfunc, void *passthru, MemoryStatsPrintFunc printfunc, void *passthru,
@ -291,6 +293,7 @@ static const MemoryContextMethods AllocSetMethods = {
AllocSetReset, AllocSetReset,
AllocSetDelete, AllocSetDelete,
AllocSetGetChunkSpace, AllocSetGetChunkSpace,
AllocSetMemAllocated,
AllocSetIsEmpty, AllocSetIsEmpty,
AllocSetStats AllocSetStats
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
@ -464,8 +467,7 @@ AllocSetContextCreateInternal(MemoryContext parent,
parent, parent,
name); name);
((MemoryContext) set)->mem_allocated = set->memAllocated = set->keeper->endptr - ((char *) set);
set->keeper->endptr - ((char *) set);
return (MemoryContext) set; return (MemoryContext) set;
} }
@ -555,7 +557,7 @@ AllocSetContextCreateInternal(MemoryContext parent,
parent, parent,
name); name);
((MemoryContext) set)->mem_allocated = firstBlockSize; set->memAllocated = firstBlockSize;
return (MemoryContext) set; return (MemoryContext) set;
} }
@ -617,7 +619,7 @@ AllocSetReset(MemoryContext context)
else else
{ {
/* Normal case, release the block */ /* Normal case, release the block */
context->mem_allocated -= block->endptr - ((char*) block); set->memAllocated -= block->endptr - ((char*) block);
#ifdef CLOBBER_FREED_MEMORY #ifdef CLOBBER_FREED_MEMORY
wipe_mem(block, block->freeptr - ((char *) block)); wipe_mem(block, block->freeptr - ((char *) block));
@ -627,7 +629,7 @@ AllocSetReset(MemoryContext context)
block = next; block = next;
} }
Assert(context->mem_allocated == keepersize); Assert(set->memAllocated == keepersize);
/* Reset block size allocation sequence, too */ /* Reset block size allocation sequence, too */
set->nextBlockSize = set->initBlockSize; set->nextBlockSize = set->initBlockSize;
@ -703,7 +705,7 @@ AllocSetDelete(MemoryContext context)
AllocBlock next = block->next; AllocBlock next = block->next;
if (block != set->keeper) if (block != set->keeper)
context->mem_allocated -= block->endptr - ((char *) block); set->memAllocated -= block->endptr - ((char *) block);
#ifdef CLOBBER_FREED_MEMORY #ifdef CLOBBER_FREED_MEMORY
wipe_mem(block, block->freeptr - ((char *) block)); wipe_mem(block, block->freeptr - ((char *) block));
@ -715,7 +717,7 @@ AllocSetDelete(MemoryContext context)
block = next; block = next;
} }
Assert(context->mem_allocated == keepersize); Assert(set->memAllocated == keepersize);
/* Finally, free the context header, including the keeper block */ /* Finally, free the context header, including the keeper block */
free(set); free(set);
@ -758,7 +760,7 @@ AllocSetAlloc(MemoryContext context, Size size)
if (block == NULL) if (block == NULL)
return NULL; return NULL;
context->mem_allocated += blksize; set->memAllocated += blksize;
block->aset = set; block->aset = set;
block->freeptr = block->endptr = ((char *) block) + blksize; block->freeptr = block->endptr = ((char *) block) + blksize;
@ -955,7 +957,7 @@ AllocSetAlloc(MemoryContext context, Size size)
if (block == NULL) if (block == NULL)
return NULL; return NULL;
context->mem_allocated += blksize; set->memAllocated += blksize;
block->aset = set; block->aset = set;
block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
@ -1058,7 +1060,7 @@ AllocSetFree(MemoryContext context, void *pointer)
if (block->next) if (block->next)
block->next->prev = block->prev; block->next->prev = block->prev;
context->mem_allocated -= block->endptr - ((char*) block); set->memAllocated -= block->endptr - ((char*) block);
#ifdef CLOBBER_FREED_MEMORY #ifdef CLOBBER_FREED_MEMORY
wipe_mem(block, block->freeptr - ((char *) block)); wipe_mem(block, block->freeptr - ((char *) block));
@ -1161,8 +1163,8 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
} }
/* updated separately, not to underflow when (oldblksize > blksize) */ /* updated separately, not to underflow when (oldblksize > blksize) */
context->mem_allocated -= oldblksize; set->memAllocated -= oldblksize;
context->mem_allocated += blksize; set->memAllocated += blksize;
block->freeptr = block->endptr = ((char *) block) + blksize; block->freeptr = block->endptr = ((char *) block) + blksize;
@ -1337,6 +1339,24 @@ AllocSetGetChunkSpace(MemoryContext context, void *pointer)
return result; return result;
} }
/*
* All memory currently allocated for this context (including fragmentation
* and freed chunks).
*
* Allocation sizes double (up to maxBlockSize), so the current block may
* represent half of the total space allocated to the context. Subtract away
* the free space at the tail of the current block, which may never have been
* touched.
*/
static Size
AllocSetMemAllocated(MemoryContext context)
{
AllocSet set = (AllocSet) context;
AllocBlock currentBlock = set->blocks;
Size tailSpace = currentBlock->endptr - currentBlock->freeptr;
return set->memAllocated - tailSpace;
}
/* /*
* AllocSetIsEmpty * AllocSetIsEmpty
* Is an allocset empty of any allocated space? * Is an allocset empty of any allocated space?
@ -1538,7 +1558,7 @@ AllocSetCheck(MemoryContext context)
name, block); name, block);
} }
Assert(total_allocated == context->mem_allocated); Assert(total_allocated == set->memAllocated);
} }
#endif /* MEMORY_CONTEXT_CHECKING */ #endif /* MEMORY_CONTEXT_CHECKING */

View File

@ -61,6 +61,7 @@ typedef struct GenerationContext
/* Generational context parameters */ /* Generational context parameters */
Size blockSize; /* standard block size */ Size blockSize; /* standard block size */
Size memAllocated; /* track memory allocated for this context */
GenerationBlock *block; /* current (most recently allocated) block */ GenerationBlock *block; /* current (most recently allocated) block */
dlist_head blocks; /* list of blocks */ dlist_head blocks; /* list of blocks */
@ -152,6 +153,7 @@ static void *GenerationRealloc(MemoryContext context, void *pointer, Size size);
static void GenerationReset(MemoryContext context); static void GenerationReset(MemoryContext context);
static void GenerationDelete(MemoryContext context); static void GenerationDelete(MemoryContext context);
static Size GenerationGetChunkSpace(MemoryContext context, void *pointer); static Size GenerationGetChunkSpace(MemoryContext context, void *pointer);
static Size GenerationMemAllocated(MemoryContext context);
static bool GenerationIsEmpty(MemoryContext context); static bool GenerationIsEmpty(MemoryContext context);
static void GenerationStats(MemoryContext context, static void GenerationStats(MemoryContext context,
MemoryStatsPrintFunc printfunc, void *passthru, MemoryStatsPrintFunc printfunc, void *passthru,
@ -171,6 +173,7 @@ static const MemoryContextMethods GenerationMethods = {
GenerationReset, GenerationReset,
GenerationDelete, GenerationDelete,
GenerationGetChunkSpace, GenerationGetChunkSpace,
GenerationMemAllocated,
GenerationIsEmpty, GenerationIsEmpty,
GenerationStats GenerationStats
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
@ -258,6 +261,7 @@ GenerationContextCreate(MemoryContext parent,
/* Fill in GenerationContext-specific header fields */ /* Fill in GenerationContext-specific header fields */
set->blockSize = blockSize; set->blockSize = blockSize;
set->memAllocated = 0;
set->block = NULL; set->block = NULL;
dlist_init(&set->blocks); dlist_init(&set->blocks);
@ -297,7 +301,7 @@ GenerationReset(MemoryContext context)
dlist_delete(miter.cur); dlist_delete(miter.cur);
context->mem_allocated -= block->blksize; set->memAllocated -= block->blksize;
#ifdef CLOBBER_FREED_MEMORY #ifdef CLOBBER_FREED_MEMORY
wipe_mem(block, block->blksize); wipe_mem(block, block->blksize);
@ -354,7 +358,7 @@ GenerationAlloc(MemoryContext context, Size size)
if (block == NULL) if (block == NULL)
return NULL; return NULL;
context->mem_allocated += blksize; set->memAllocated += blksize;
/* block with a single (used) chunk */ /* block with a single (used) chunk */
block->blksize = blksize; block->blksize = blksize;
@ -411,7 +415,7 @@ GenerationAlloc(MemoryContext context, Size size)
if (block == NULL) if (block == NULL)
return NULL; return NULL;
context->mem_allocated += blksize; set->memAllocated += blksize;
block->blksize = blksize; block->blksize = blksize;
block->nchunks = 0; block->nchunks = 0;
@ -528,7 +532,7 @@ GenerationFree(MemoryContext context, void *pointer)
if (set->block == block) if (set->block == block)
set->block = NULL; set->block = NULL;
context->mem_allocated -= block->blksize; set->memAllocated -= block->blksize;
free(block); free(block);
} }
@ -666,6 +670,17 @@ GenerationGetChunkSpace(MemoryContext context, void *pointer)
return result; return result;
} }
/*
* All memory currently allocated for this context (including fragmentation
* and freed chunks).
*/
static Size
GenerationMemAllocated(MemoryContext context)
{
GenerationContext *set = (GenerationContext *) context;
return set->memAllocated;
}
/* /*
* GenerationIsEmpty * GenerationIsEmpty
* Is a GenerationContext empty of any allocated space? * Is a GenerationContext empty of any allocated space?
@ -844,7 +859,7 @@ GenerationCheck(MemoryContext context)
name, nfree, block, block->nfree); name, nfree, block, block->nfree);
} }
Assert(total_allocated == context->mem_allocated); Assert(total_allocated == gen->memAllocated);
} }
#endif /* MEMORY_CONTEXT_CHECKING */ #endif /* MEMORY_CONTEXT_CHECKING */

View File

@ -469,7 +469,7 @@ MemoryContextIsEmpty(MemoryContext context)
Size Size
MemoryContextMemAllocated(MemoryContext context, bool recurse) MemoryContextMemAllocated(MemoryContext context, bool recurse)
{ {
Size total = context->mem_allocated; Size total = context->methods->mem_allocated(context);
AssertArg(MemoryContextIsValid(context)); AssertArg(MemoryContextIsValid(context));
@ -760,7 +760,6 @@ MemoryContextCreate(MemoryContext node,
node->methods = methods; node->methods = methods;
node->parent = parent; node->parent = parent;
node->firstchild = NULL; node->firstchild = NULL;
node->mem_allocated = 0;
node->prevchild = NULL; node->prevchild = NULL;
node->name = name; node->name = name;
node->ident = NULL; node->ident = NULL;

View File

@ -67,6 +67,7 @@ typedef struct SlabContext
Size fullChunkSize; /* chunk size including header and alignment */ Size fullChunkSize; /* chunk size including header and alignment */
Size blockSize; /* block size */ Size blockSize; /* block size */
Size headerSize; /* allocated size of context header */ Size headerSize; /* allocated size of context header */
Size memAllocated; /* track memory allocated for this context */
int chunksPerBlock; /* number of chunks per block */ int chunksPerBlock; /* number of chunks per block */
int minFreeChunks; /* min number of free chunks in any block */ int minFreeChunks; /* min number of free chunks in any block */
int nblocks; /* number of blocks allocated */ int nblocks; /* number of blocks allocated */
@ -132,6 +133,7 @@ static void *SlabRealloc(MemoryContext context, void *pointer, Size size);
static void SlabReset(MemoryContext context); static void SlabReset(MemoryContext context);
static void SlabDelete(MemoryContext context); static void SlabDelete(MemoryContext context);
static Size SlabGetChunkSpace(MemoryContext context, void *pointer); static Size SlabGetChunkSpace(MemoryContext context, void *pointer);
static Size SlabMemAllocated(MemoryContext context);
static bool SlabIsEmpty(MemoryContext context); static bool SlabIsEmpty(MemoryContext context);
static void SlabStats(MemoryContext context, static void SlabStats(MemoryContext context,
MemoryStatsPrintFunc printfunc, void *passthru, MemoryStatsPrintFunc printfunc, void *passthru,
@ -150,6 +152,7 @@ static const MemoryContextMethods SlabMethods = {
SlabReset, SlabReset,
SlabDelete, SlabDelete,
SlabGetChunkSpace, SlabGetChunkSpace,
SlabMemAllocated,
SlabIsEmpty, SlabIsEmpty,
SlabStats SlabStats
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
@ -262,6 +265,7 @@ SlabContextCreate(MemoryContext parent,
slab->fullChunkSize = fullChunkSize; slab->fullChunkSize = fullChunkSize;
slab->blockSize = blockSize; slab->blockSize = blockSize;
slab->headerSize = headerSize; slab->headerSize = headerSize;
slab->memAllocated = 0;
slab->chunksPerBlock = chunksPerBlock; slab->chunksPerBlock = chunksPerBlock;
slab->minFreeChunks = 0; slab->minFreeChunks = 0;
slab->nblocks = 0; slab->nblocks = 0;
@ -286,6 +290,17 @@ SlabContextCreate(MemoryContext parent,
return (MemoryContext) slab; return (MemoryContext) slab;
} }
/*
* All memory currently allocated for this context (including fragmentation
* and freed chunks).
*/
static Size
SlabMemAllocated(MemoryContext context)
{
SlabContext *slab = (SlabContext *) context;
return slab->memAllocated;
}
/* /*
* SlabReset * SlabReset
* Frees all memory which is allocated in the given set. * Frees all memory which is allocated in the given set.
@ -322,14 +337,14 @@ SlabReset(MemoryContext context)
#endif #endif
free(block); free(block);
slab->nblocks--; slab->nblocks--;
context->mem_allocated -= slab->blockSize; slab->memAllocated -= slab->blockSize;
} }
} }
slab->minFreeChunks = 0; slab->minFreeChunks = 0;
Assert(slab->nblocks == 0); Assert(slab->nblocks == 0);
Assert(context->mem_allocated == 0); Assert(slab->memAllocated == 0);
} }
/* /*
@ -407,7 +422,7 @@ SlabAlloc(MemoryContext context, Size size)
slab->minFreeChunks = slab->chunksPerBlock; slab->minFreeChunks = slab->chunksPerBlock;
slab->nblocks += 1; slab->nblocks += 1;
context->mem_allocated += slab->blockSize; slab->memAllocated += slab->blockSize;
} }
/* grab the block from the freelist (even the new block is there) */ /* grab the block from the freelist (even the new block is there) */
@ -501,7 +516,7 @@ SlabAlloc(MemoryContext context, Size size)
SlabAllocInfo(slab, chunk); SlabAllocInfo(slab, chunk);
Assert(slab->nblocks * slab->blockSize == context->mem_allocated); Assert(slab->nblocks * slab->blockSize == slab->memAllocated);
return SlabChunkGetPointer(chunk); return SlabChunkGetPointer(chunk);
} }
@ -578,13 +593,13 @@ SlabFree(MemoryContext context, void *pointer)
{ {
free(block); free(block);
slab->nblocks--; slab->nblocks--;
context->mem_allocated -= slab->blockSize; slab->memAllocated -= slab->blockSize;
} }
else else
dlist_push_head(&slab->freelist[block->nfree], &block->node); dlist_push_head(&slab->freelist[block->nfree], &block->node);
Assert(slab->nblocks >= 0); Assert(slab->nblocks >= 0);
Assert(slab->nblocks * slab->blockSize == context->mem_allocated); Assert(slab->nblocks * slab->blockSize == slab->memAllocated);
} }
/* /*
@ -804,7 +819,7 @@ SlabCheck(MemoryContext context)
} }
} }
Assert(slab->nblocks * slab->blockSize == context->mem_allocated); Assert(slab->nblocks * slab->blockSize == slab->memAllocated);
} }
#endif /* MEMORY_CONTEXT_CHECKING */ #endif /* MEMORY_CONTEXT_CHECKING */

View File

@ -63,6 +63,7 @@ typedef struct MemoryContextMethods
void (*reset) (MemoryContext context); void (*reset) (MemoryContext context);
void (*delete_context) (MemoryContext context); void (*delete_context) (MemoryContext context);
Size (*get_chunk_space) (MemoryContext context, void *pointer); Size (*get_chunk_space) (MemoryContext context, void *pointer);
Size (*mem_allocated) (MemoryContext context);
bool (*is_empty) (MemoryContext context); bool (*is_empty) (MemoryContext context);
void (*stats) (MemoryContext context, void (*stats) (MemoryContext context,
MemoryStatsPrintFunc printfunc, void *passthru, MemoryStatsPrintFunc printfunc, void *passthru,
@ -79,7 +80,6 @@ typedef struct MemoryContextData
/* these two fields are placed here to minimize alignment wastage: */ /* these two fields are placed here to minimize alignment wastage: */
bool isReset; /* T = no space alloced since last reset */ bool isReset; /* T = no space alloced since last reset */
bool allowInCritSection; /* allow palloc in critical section */ bool allowInCritSection; /* allow palloc in critical section */
Size mem_allocated; /* track memory allocated for this context */
const MemoryContextMethods *methods; /* virtual function table */ const MemoryContextMethods *methods; /* virtual function table */
MemoryContext parent; /* NULL if no parent (toplevel context) */ MemoryContext parent; /* NULL if no parent (toplevel context) */
MemoryContext firstchild; /* head of linked list of children */ MemoryContext firstchild; /* head of linked list of children */