On Thu, 2015-07-09 at 14:41 +1200, David Rowley wrote: > Are you going to implement this? or are you happy with the single > level context tracking is the right thing? > I'm going to mark the patch as waiting on author for now.
Attached a version of the patch that does multi-level tracking, v12. It does this is a simpler way, just like an earlier version of the patch: it simply traverses up the chain and adds to each parent in a "total_allocated" field. The idea you mentioned is a possible optimization of this idea, but it only makes sense if I'm able to detect a difference between v11 (single-level) and v12 (multi-level). I tried Robert's test[1] again and I didn't see a difference on my workstation (technically, v12 came out the fastest, which means I'm just seeing noise anyway), so I can't evaluate whether your idea will improve things. After talking with a few people at PGCon, small noisy differences in CPU timings can appear for almost any tweak to the code, and aren't necessarily cause for major concern. Regards, Jeff Davis [1] pgbench -i -s 300, then do the following 3 times each for master, v11, and v12, and take the median of logged traces: start server; set trace_sort=on; reindex index pgbench_accounts_pkey; stop server
*** a/src/backend/utils/mmgr/aset.c --- b/src/backend/utils/mmgr/aset.c *************** *** 242,247 **** typedef struct AllocChunkData --- 242,249 ---- #define AllocChunkGetPointer(chk) \ ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ)) + static void update_alloc(MemoryContext context, int64 size); + /* * These functions implement the MemoryContext API for AllocSet contexts. */ *************** *** 500,505 **** AllocSetContextCreate(MemoryContext parent, --- 502,510 ---- errdetail("Failed while creating memory context \"%s\".", name))); } + + update_alloc((MemoryContext) set, blksize); + block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; *************** *** 590,595 **** AllocSetReset(MemoryContext context) --- 595,602 ---- else { /* Normal case, release the block */ + update_alloc(context, -(block->endptr - ((char*) block))); + #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif *************** *** 635,640 **** AllocSetDelete(MemoryContext context) --- 642,654 ---- #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif + + /* + * Parent is already unlinked from this context, so we can't use + * update_alloc(). The caller should have already subtracted from the + * parents' total_allocated. + */ + free(block); block = next; } *************** *** 672,677 **** AllocSetAlloc(MemoryContext context, Size size) --- 686,694 ---- block = (AllocBlock) malloc(blksize); if (block == NULL) return NULL; + + update_alloc(context, blksize); + block->aset = set; block->freeptr = block->endptr = ((char *) block) + blksize; *************** *** 861,866 **** AllocSetAlloc(MemoryContext context, Size size) --- 878,885 ---- if (block == NULL) return NULL; + update_alloc(context, blksize); + block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; *************** *** 964,969 **** AllocSetFree(MemoryContext context, void *pointer) --- 983,991 ---- set->blocks = block->next; else prevblock->next = block->next; + + update_alloc(context, -(block->endptr - ((char*) block))); + #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif *************** *** 1077,1082 **** AllocSetRealloc(MemoryContext context, void *pointer, Size size) --- 1099,1105 ---- AllocBlock prevblock = NULL; Size chksize; Size blksize; + Size oldblksize; while (block != NULL) { *************** *** 1094,1102 **** AllocSetRealloc(MemoryContext context, void *pointer, Size size) --- 1117,1130 ---- /* Do the realloc */ chksize = MAXALIGN(size); blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; + oldblksize = block->endptr - ((char *)block); + block = (AllocBlock) realloc(block, blksize); if (block == NULL) return NULL; + + update_alloc(context, blksize - oldblksize); + block->freeptr = block->endptr = ((char *) block) + blksize; /* Update pointers since block has likely been moved */ *************** *** 1361,1363 **** AllocSetCheck(MemoryContext context) --- 1389,1407 ---- } #endif /* MEMORY_CONTEXT_CHECKING */ + + /* + * Update self_allocated and total_allocated for the context. Size can be + * positive or negative. + */ + void + update_alloc(MemoryContext context, int64 size) + { + MemoryContext parent; + + context->self_allocated += size; + + /* update total_allocated for self and all parents */ + for (parent = context; parent != NULL; parent = parent->parent) + parent->total_allocated += size; + } *** a/src/backend/utils/mmgr/mcxt.c --- b/src/backend/utils/mmgr/mcxt.c *************** *** 203,208 **** MemoryContextResetChildren(MemoryContext context) --- 203,210 ---- void MemoryContextDelete(MemoryContext context) { + MemoryContext parent; + AssertArg(MemoryContextIsValid(context)); /* We had better not be deleting TopMemoryContext ... */ Assert(context != TopMemoryContext); *************** *** 223,229 **** MemoryContextDelete(MemoryContext context) --- 225,236 ---- * We delink the context from its parent before deleting it, so that if * there's an error we won't have deleted/busted contexts still attached * to the context tree. Better a leak than a crash. + * + * But first we need to subtract this context's allocation from + * total_allocated in the parent contexts. */ + for(parent = context->parent; parent != NULL; parent = parent->parent) + parent->total_allocated -= context->total_allocated; MemoryContextSetParent(context, NULL); (*context->methods->delete_context) (context); *************** *** 477,482 **** MemoryContextIsEmpty(MemoryContext context) --- 484,504 ---- } /* + * Find the memory allocated to blocks for this memory context. If total is + * true, also include descendants. + */ + int64 + MemoryContextMemAllocated(MemoryContext context, bool total) + { + AssertArg(MemoryContextIsValid(context)); + + if (total) + return context->total_allocated; + else + return context->self_allocated; + } + + /* * MemoryContextStats * Print statistics about the named context and all its descendants. * *************** *** 636,641 **** MemoryContextCreate(NodeTag tag, Size size, --- 658,665 ---- node->firstchild = NULL; node->nextchild = NULL; node->isReset = true; + node->self_allocated = 0; + node->total_allocated = 0; node->name = ((char *) node) + size; strcpy(node->name, name); *** a/src/include/nodes/memnodes.h --- b/src/include/nodes/memnodes.h *************** *** 63,68 **** typedef struct MemoryContextData --- 63,70 ---- MemoryContext nextchild; /* next child of same parent */ char *name; /* context name (just for debugging) */ MemoryContextCallback *reset_cbs; /* list of reset/delete callbacks */ + int64 self_allocated; /* track memory allocated for this context */ + int64 total_allocated; /* this context plus all descendants */ } MemoryContextData; /* utils/palloc.h contains typedef struct MemoryContextData *MemoryContext */ *** a/src/include/utils/memutils.h --- b/src/include/utils/memutils.h *************** *** 103,108 **** extern Size GetMemoryChunkSpace(void *pointer); --- 103,109 ---- extern MemoryContext GetMemoryChunkContext(void *pointer); extern MemoryContext MemoryContextGetParent(MemoryContext context); extern bool MemoryContextIsEmpty(MemoryContext context); + extern int64 MemoryContextMemAllocated(MemoryContext context, bool recurse); extern void MemoryContextStats(MemoryContext context); extern void MemoryContextAllowInCriticalSection(MemoryContext context, bool allow);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers