Specialize MemoryContextMemAllocated().
authorJeff Davis <[email protected]>
Wed, 18 Mar 2020 22:39:14 +0000 (15:39 -0700)
committerJeff Davis <[email protected]>
Wed, 18 Mar 2020 22:39:14 +0000 (15:39 -0700)
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[email protected]

src/backend/utils/mmgr/aset.c
src/backend/utils/mmgr/generation.c
src/backend/utils/mmgr/mcxt.c
src/backend/utils/mmgr/slab.c
src/include/nodes/memnodes.h

index c0623f106d2b8e640de9d4ce28f1b77438213c1b..ccf78ffe0cb588ce4ba5aec0b8840c1f6378c1a4 100644 (file)
@@ -132,6 +132,7 @@ typedef struct AllocSetContext
    Size        maxBlockSize;   /* maximum block size */
    Size        nextBlockSize;  /* next block size to allocate */
    Size        allocChunkLimit;    /* effective chunk size limit */
+   Size        memAllocated;   /* track memory allocated for this context */
    AllocBlock  keeper;         /* keep this block over resets */
    /* freelist this context could be put in, or -1 if not a candidate: */
    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 AllocSetDelete(MemoryContext context);
 static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
+static Size AllocSetMemAllocated(MemoryContext context);
 static bool AllocSetIsEmpty(MemoryContext context);
 static void AllocSetStats(MemoryContext context,
                          MemoryStatsPrintFunc printfunc, void *passthru,
@@ -291,6 +293,7 @@ static const MemoryContextMethods AllocSetMethods = {
    AllocSetReset,
    AllocSetDelete,
    AllocSetGetChunkSpace,
+   AllocSetMemAllocated,
    AllocSetIsEmpty,
    AllocSetStats
 #ifdef MEMORY_CONTEXT_CHECKING
@@ -464,8 +467,7 @@ AllocSetContextCreateInternal(MemoryContext parent,
                                parent,
                                name);
 
-           ((MemoryContext) set)->mem_allocated =
-               set->keeper->endptr - ((char *) set);
+           set->memAllocated = set->keeper->endptr - ((char *) set);
 
            return (MemoryContext) set;
        }
@@ -555,7 +557,7 @@ AllocSetContextCreateInternal(MemoryContext parent,
                        parent,
                        name);
 
-   ((MemoryContext) set)->mem_allocated = firstBlockSize;
+   set->memAllocated = firstBlockSize;
 
    return (MemoryContext) set;
 }
@@ -617,7 +619,7 @@ AllocSetReset(MemoryContext context)
        else
        {
            /* Normal case, release the block */
-           context->mem_allocated -= block->endptr - ((char*) block);
+           set->memAllocated -= block->endptr - ((char*) block);
 
 #ifdef CLOBBER_FREED_MEMORY
            wipe_mem(block, block->freeptr - ((char *) block));
@@ -627,7 +629,7 @@ AllocSetReset(MemoryContext context)
        block = next;
    }
 
-   Assert(context->mem_allocated == keepersize);
+   Assert(set->memAllocated == keepersize);
 
    /* Reset block size allocation sequence, too */
    set->nextBlockSize = set->initBlockSize;
@@ -703,7 +705,7 @@ AllocSetDelete(MemoryContext context)
        AllocBlock  next = block->next;
 
        if (block != set->keeper)
-           context->mem_allocated -= block->endptr - ((char *) block);
+           set->memAllocated -= block->endptr - ((char *) block);
 
 #ifdef CLOBBER_FREED_MEMORY
        wipe_mem(block, block->freeptr - ((char *) block));
@@ -715,7 +717,7 @@ AllocSetDelete(MemoryContext context)
        block = next;
    }
 
-   Assert(context->mem_allocated == keepersize);
+   Assert(set->memAllocated == keepersize);
 
    /* Finally, free the context header, including the keeper block */
    free(set);
@@ -758,7 +760,7 @@ AllocSetAlloc(MemoryContext context, Size size)
        if (block == NULL)
            return NULL;
 
-       context->mem_allocated += blksize;
+       set->memAllocated += blksize;
 
        block->aset = set;
        block->freeptr = block->endptr = ((char *) block) + blksize;
@@ -955,7 +957,7 @@ AllocSetAlloc(MemoryContext context, Size size)
        if (block == NULL)
            return NULL;
 
-       context->mem_allocated += blksize;
+       set->memAllocated += blksize;
 
        block->aset = set;
        block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
@@ -1058,7 +1060,7 @@ AllocSetFree(MemoryContext context, void *pointer)
        if (block->next)
            block->next->prev = block->prev;
 
-       context->mem_allocated -= block->endptr - ((char*) block);
+       set->memAllocated -= block->endptr - ((char*) block);
 
 #ifdef CLOBBER_FREED_MEMORY
        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) */
-       context->mem_allocated -= oldblksize;
-       context->mem_allocated += blksize;
+       set->memAllocated -= oldblksize;
+       set->memAllocated += blksize;
 
        block->freeptr = block->endptr = ((char *) block) + blksize;
 
@@ -1337,6 +1339,24 @@ AllocSetGetChunkSpace(MemoryContext context, void *pointer)
    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
  *     Is an allocset empty of any allocated space?
@@ -1538,7 +1558,7 @@ AllocSetCheck(MemoryContext context)
                 name, block);
    }
 
-   Assert(total_allocated == context->mem_allocated);
+   Assert(total_allocated == set->memAllocated);
 }
 
 #endif                         /* MEMORY_CONTEXT_CHECKING */
index 56651d0693132dd15e3d95e8fbea93fb427ea5a2..f0ef540a7c5f1dae2c74a804d35d9b49f908ac40 100644 (file)
@@ -61,6 +61,7 @@ typedef struct GenerationContext
 
    /* Generational context parameters */
    Size        blockSize;      /* standard block size */
+   Size        memAllocated;   /* track memory allocated for this context */
 
    GenerationBlock *block;     /* current (most recently allocated) block */
    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 GenerationDelete(MemoryContext context);
 static Size GenerationGetChunkSpace(MemoryContext context, void *pointer);
+static Size GenerationMemAllocated(MemoryContext context);
 static bool GenerationIsEmpty(MemoryContext context);
 static void GenerationStats(MemoryContext context,
                            MemoryStatsPrintFunc printfunc, void *passthru,
@@ -171,6 +173,7 @@ static const MemoryContextMethods GenerationMethods = {
    GenerationReset,
    GenerationDelete,
    GenerationGetChunkSpace,
+   GenerationMemAllocated,
    GenerationIsEmpty,
    GenerationStats
 #ifdef MEMORY_CONTEXT_CHECKING
@@ -258,6 +261,7 @@ GenerationContextCreate(MemoryContext parent,
 
    /* Fill in GenerationContext-specific header fields */
    set->blockSize = blockSize;
+   set->memAllocated = 0;
    set->block = NULL;
    dlist_init(&set->blocks);
 
@@ -297,7 +301,7 @@ GenerationReset(MemoryContext context)
 
        dlist_delete(miter.cur);
 
-       context->mem_allocated -= block->blksize;
+       set->memAllocated -= block->blksize;
 
 #ifdef CLOBBER_FREED_MEMORY
        wipe_mem(block, block->blksize);
@@ -354,7 +358,7 @@ GenerationAlloc(MemoryContext context, Size size)
        if (block == NULL)
            return NULL;
 
-       context->mem_allocated += blksize;
+       set->memAllocated += blksize;
 
        /* block with a single (used) chunk */
        block->blksize = blksize;
@@ -411,7 +415,7 @@ GenerationAlloc(MemoryContext context, Size size)
        if (block == NULL)
            return NULL;
 
-       context->mem_allocated += blksize;
+       set->memAllocated += blksize;
 
        block->blksize = blksize;
        block->nchunks = 0;
@@ -528,7 +532,7 @@ GenerationFree(MemoryContext context, void *pointer)
    if (set->block == block)
        set->block = NULL;
 
-   context->mem_allocated -= block->blksize;
+   set->memAllocated -= block->blksize;
    free(block);
 }
 
@@ -666,6 +670,17 @@ GenerationGetChunkSpace(MemoryContext context, void *pointer)
    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
  *     Is a GenerationContext empty of any allocated space?
@@ -844,7 +859,7 @@ GenerationCheck(MemoryContext context)
                 name, nfree, block, block->nfree);
    }
 
-   Assert(total_allocated == context->mem_allocated);
+   Assert(total_allocated == gen->memAllocated);
 }
 
 #endif                         /* MEMORY_CONTEXT_CHECKING */
index 9e24fec72d6c13448e2511ffdea48fbeea42ac25..e32e279c340e098abdd609c549ed74deef2e65f7 100644 (file)
@@ -469,7 +469,7 @@ MemoryContextIsEmpty(MemoryContext context)
 Size
 MemoryContextMemAllocated(MemoryContext context, bool recurse)
 {
-   Size    total = context->mem_allocated;
+   Size    total = context->methods->mem_allocated(context);
 
    AssertArg(MemoryContextIsValid(context));
 
@@ -760,7 +760,6 @@ MemoryContextCreate(MemoryContext node,
    node->methods = methods;
    node->parent = parent;
    node->firstchild = NULL;
-   node->mem_allocated = 0;
    node->prevchild = NULL;
    node->name = name;
    node->ident = NULL;
index c928476c479e34275ebde76952ac3fbf9d514157..63750fbc81f7639226347f59b364bf9e9b56ea4a 100644 (file)
@@ -67,6 +67,7 @@ typedef struct SlabContext
    Size        fullChunkSize;  /* chunk size including header and alignment */
    Size        blockSize;      /* block size */
    Size        headerSize;     /* allocated size of context header */
+   Size        memAllocated;   /* track memory allocated for this context */
    int         chunksPerBlock; /* number of chunks per block */
    int         minFreeChunks;  /* min number of free chunks in any block */
    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 SlabDelete(MemoryContext context);
 static Size SlabGetChunkSpace(MemoryContext context, void *pointer);
+static Size SlabMemAllocated(MemoryContext context);
 static bool SlabIsEmpty(MemoryContext context);
 static void SlabStats(MemoryContext context,
                      MemoryStatsPrintFunc printfunc, void *passthru,
@@ -150,6 +152,7 @@ static const MemoryContextMethods SlabMethods = {
    SlabReset,
    SlabDelete,
    SlabGetChunkSpace,
+   SlabMemAllocated,
    SlabIsEmpty,
    SlabStats
 #ifdef MEMORY_CONTEXT_CHECKING
@@ -262,6 +265,7 @@ SlabContextCreate(MemoryContext parent,
    slab->fullChunkSize = fullChunkSize;
    slab->blockSize = blockSize;
    slab->headerSize = headerSize;
+   slab->memAllocated = 0;
    slab->chunksPerBlock = chunksPerBlock;
    slab->minFreeChunks = 0;
    slab->nblocks = 0;
@@ -286,6 +290,17 @@ SlabContextCreate(MemoryContext parent,
    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
  *     Frees all memory which is allocated in the given set.
@@ -322,14 +337,14 @@ SlabReset(MemoryContext context)
 #endif
            free(block);
            slab->nblocks--;
-           context->mem_allocated -= slab->blockSize;
+           slab->memAllocated -= slab->blockSize;
        }
    }
 
    slab->minFreeChunks = 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->nblocks += 1;
-       context->mem_allocated += slab->blockSize;
+       slab->memAllocated += slab->blockSize;
    }
 
    /* grab the block from the freelist (even the new block is there) */
@@ -501,7 +516,7 @@ SlabAlloc(MemoryContext context, Size size)
 
    SlabAllocInfo(slab, chunk);
 
-   Assert(slab->nblocks * slab->blockSize == context->mem_allocated);
+   Assert(slab->nblocks * slab->blockSize == slab->memAllocated);
 
    return SlabChunkGetPointer(chunk);
 }
@@ -578,13 +593,13 @@ SlabFree(MemoryContext context, void *pointer)
    {
        free(block);
        slab->nblocks--;
-       context->mem_allocated -= slab->blockSize;
+       slab->memAllocated -= slab->blockSize;
    }
    else
        dlist_push_head(&slab->freelist[block->nfree], &block->node);
 
    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 */
index c9f2bbcb367e5efb1342bc50d37431b1238af54a..b6296af7d5f238e3e707811e24fe2b9210cea557 100644 (file)
@@ -63,6 +63,7 @@ typedef struct MemoryContextMethods
    void        (*reset) (MemoryContext context);
    void        (*delete_context) (MemoryContext context);
    Size        (*get_chunk_space) (MemoryContext context, void *pointer);
+   Size        (*mem_allocated) (MemoryContext context);
    bool        (*is_empty) (MemoryContext context);
    void        (*stats) (MemoryContext context,
                          MemoryStatsPrintFunc printfunc, void *passthru,
@@ -79,7 +80,6 @@ typedef struct MemoryContextData
    /* these two fields are placed here to minimize alignment wastage: */
    bool        isReset;        /* T = no space alloced since last reset */
    bool        allowInCritSection; /* allow palloc in critical section */
-   Size        mem_allocated;  /* track memory allocated for this context */
    const MemoryContextMethods *methods;    /* virtual function table */
    MemoryContext parent;       /* NULL if no parent (toplevel context) */
    MemoryContext firstchild;   /* head of linked list of children */