On Fri, 2014-08-29 at 10:12 -0400, Tom Lane wrote: > What about removing the callback per se and just keeping the argument, > as it were. That is, a MemoryContext has a field of type "size_t *" > that is normally NULL, but if set to non-NULL, then we increment the > pointed-to value for pallocs and decrement for pfrees.
I like that idea; patch attached. Two differences: * At block level, not chunk level. * I use a uint64, because a size_t is only guaranteed to hold one allocation, and we need to sum up many allocations. When unused, it does still appear to have a little overhead in Robert's test on his power machine. It seems to be between a 0.5-1.0% regression. There's not much extra code on that path, just a branch, pointer dereference, and an addition; so I don't think it will get much cheaper than it is. This regression seems harder to reproduce on my workstation, or perhaps it's just noisier. > One thought in either case is that we don't have to touch the API for > MemoryContextCreate: rather, the feature can be enabled in a separate > call after making the context. That seems fairly awkward to me because the pointer needs to be inherited from the parent context when not specified. I left the extra API call in. The inheritance is awkward anyway, though. If you create a tracked context as a child of an already-tracked context, allocations in the newer one won't count against the original. I don't see a way around that without introducing even more performance problems. If this patch is unacceptable, my only remaining idea is to add the memory only for the current context with no inheritance (thereby eliminating the branch, also). That will require HashAgg to traverse all the child contexts to check whether the memory limit has been exceeded. As Tomas pointed out, that could be a lot of work in the case of array_agg with many groups. Regards, Jeff Davis
*** a/src/backend/utils/mmgr/aset.c --- b/src/backend/utils/mmgr/aset.c *************** *** 438,451 **** AllocSetContextCreate(MemoryContext parent, Size initBlockSize, Size maxBlockSize) { ! AllocSet context; /* Do the type-independent part of context creation */ ! context = (AllocSet) MemoryContextCreate(T_AllocSetContext, ! sizeof(AllocSetContext), ! &AllocSetMethods, ! parent, ! name); /* * Make sure alloc parameters are reasonable, and save them. --- 438,482 ---- Size initBlockSize, Size maxBlockSize) { ! /* inherit 'track_mem' from parent context, if available */ ! uint64 *track_mem = (parent == NULL) ? NULL : parent->track_mem; ! ! return AllocSetContextCreateTracked(parent, name, minContextSize, ! initBlockSize, maxBlockSize, ! track_mem); ! } ! ! /* ! * AllocSetContextCreateTracked ! * Create a new AllocSet context, tracking total memory usage. ! * ! * parent: parent context, or NULL if top-level context ! * name: name of context (for debugging --- string will be copied) ! * minContextSize: minimum context size ! * initBlockSize: initial allocation block size ! * maxBlockSize: maximum allocation block size ! * track_mem: caller-supplied variable to use for memory tracking ! */ ! MemoryContext ! AllocSetContextCreateTracked(MemoryContext parent, ! const char *name, ! Size minContextSize, ! Size initBlockSize, ! Size maxBlockSize, ! uint64 *track_mem) ! { ! AllocSet set; ! MemoryContext context; /* Do the type-independent part of context creation */ ! context = MemoryContextCreate(T_AllocSetContext, ! sizeof(AllocSetContext), ! &AllocSetMethods, ! parent, ! name, ! track_mem); ! ! set = (AllocSet) context; /* * Make sure alloc parameters are reasonable, and save them. *************** *** 459,467 **** AllocSetContextCreate(MemoryContext parent, if (maxBlockSize < initBlockSize) maxBlockSize = initBlockSize; Assert(AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */ ! context->initBlockSize = initBlockSize; ! context->maxBlockSize = maxBlockSize; ! context->nextBlockSize = initBlockSize; /* * Compute the allocation chunk size limit for this context. It can't be --- 490,498 ---- if (maxBlockSize < initBlockSize) maxBlockSize = initBlockSize; Assert(AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */ ! set->initBlockSize = initBlockSize; ! set->maxBlockSize = maxBlockSize; ! set->nextBlockSize = initBlockSize; /* * Compute the allocation chunk size limit for this context. It can't be *************** *** 477,486 **** AllocSetContextCreate(MemoryContext parent, * and actually-allocated sizes of any chunk must be on the same side of * the limit, else we get confused about whether the chunk is "big". */ ! context->allocChunkLimit = ALLOC_CHUNK_LIMIT; ! while ((Size) (context->allocChunkLimit + ALLOC_CHUNKHDRSZ) > (Size) ((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION)) ! context->allocChunkLimit >>= 1; /* * Grab always-allocated space, if requested --- 508,517 ---- * and actually-allocated sizes of any chunk must be on the same side of * the limit, else we get confused about whether the chunk is "big". */ ! set->allocChunkLimit = ALLOC_CHUNK_LIMIT; ! while ((Size) (set->allocChunkLimit + ALLOC_CHUNKHDRSZ) > (Size) ((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION)) ! set->allocChunkLimit >>= 1; /* * Grab always-allocated space, if requested *************** *** 500,519 **** AllocSetContextCreate(MemoryContext parent, errdetail("Failed while creating memory context \"%s\".", name))); } ! block->aset = context; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; ! block->next = context->blocks; ! context->blocks = block; /* Mark block as not to be released at reset time */ ! context->keeper = block; /* Mark unallocated space NOACCESS; leave the block header alone. */ VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, blksize - ALLOC_BLOCKHDRSZ); } ! return (MemoryContext) context; } /* --- 531,554 ---- errdetail("Failed while creating memory context \"%s\".", name))); } ! ! if (context->track_mem != NULL) ! *context->track_mem += blksize; ! ! block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; ! block->next = set->blocks; ! set->blocks = block; /* Mark block as not to be released at reset time */ ! set->keeper = block; /* Mark unallocated space NOACCESS; leave the block header alone. */ VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, blksize - ALLOC_BLOCKHDRSZ); } ! return context; } /* *************** *** 590,595 **** AllocSetReset(MemoryContext context) --- 625,633 ---- else { /* Normal case, release the block */ + if (context->track_mem != NULL) + *context->track_mem -= block->endptr - ((char*) block); + #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif *************** *** 632,637 **** AllocSetDelete(MemoryContext context) --- 670,678 ---- { AllocBlock next = block->next; + if (context->track_mem != NULL) + *context->track_mem -= block->endptr - ((char *) block); + #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif *************** *** 678,683 **** AllocSetAlloc(MemoryContext context, Size size) --- 719,728 ---- errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } + + if (context->track_mem != NULL) + *context->track_mem += blksize; + block->aset = set; block->freeptr = block->endptr = ((char *) block) + blksize; *************** *** 873,878 **** AllocSetAlloc(MemoryContext context, Size size) --- 918,926 ---- errdetail("Failed on request of size %zu.", size))); } + if (context->track_mem != NULL) + *context->track_mem += blksize; + block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; *************** *** 976,981 **** AllocSetFree(MemoryContext context, void *pointer) --- 1024,1033 ---- set->blocks = block->next; else prevblock->next = block->next; + + if (context->track_mem != NULL) + *context->track_mem -= block->endptr - ((char*) block); + #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif *************** *** 1088,1093 **** AllocSetRealloc(MemoryContext context, void *pointer, Size size) --- 1140,1146 ---- AllocBlock prevblock = NULL; Size chksize; Size blksize; + Size oldblksize; while (block != NULL) { *************** *** 1105,1110 **** AllocSetRealloc(MemoryContext context, void *pointer, Size size) --- 1158,1165 ---- /* 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) { *************** *** 1114,1119 **** AllocSetRealloc(MemoryContext context, void *pointer, Size size) --- 1169,1177 ---- errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } + if (context->track_mem != NULL) + *context->track_mem += blksize - oldblksize; + block->freeptr = block->endptr = ((char *) block) + blksize; /* Update pointers since block has likely been moved */ *** a/src/backend/utils/mmgr/mcxt.c --- b/src/backend/utils/mmgr/mcxt.c *************** *** 546,552 **** MemoryContext MemoryContextCreate(NodeTag tag, Size size, MemoryContextMethods *methods, MemoryContext parent, ! const char *name) { MemoryContext node; Size needed = size + strlen(name) + 1; --- 546,553 ---- MemoryContextCreate(NodeTag tag, Size size, MemoryContextMethods *methods, MemoryContext parent, ! const char *name, ! uint64 *track_mem) { MemoryContext node; Size needed = size + strlen(name) + 1; *************** *** 576,581 **** MemoryContextCreate(NodeTag tag, Size size, --- 577,583 ---- node->firstchild = NULL; node->nextchild = NULL; node->isReset = true; + node->track_mem = track_mem; node->name = ((char *) node) + size; strcpy(node->name, name); *** a/src/include/nodes/memnodes.h --- b/src/include/nodes/memnodes.h *************** *** 60,65 **** typedef struct MemoryContextData --- 60,66 ---- MemoryContext nextchild; /* next child of same parent */ char *name; /* context name (just for debugging) */ bool isReset; /* T = no space alloced since last reset */ + uint64 *track_mem; /* track memory usage for caller */ #ifdef USE_ASSERT_CHECKING bool allowInCritSection; /* allow palloc in critical section */ #endif *** a/src/include/utils/memutils.h --- b/src/include/utils/memutils.h *************** *** 117,123 **** extern bool MemoryContextContains(MemoryContext context, void *pointer); extern MemoryContext MemoryContextCreate(NodeTag tag, Size size, MemoryContextMethods *methods, MemoryContext parent, ! const char *name); /* --- 117,124 ---- extern MemoryContext MemoryContextCreate(NodeTag tag, Size size, MemoryContextMethods *methods, MemoryContext parent, ! const char *name, ! uint64 *track_mem); /* *************** *** 130,135 **** extern MemoryContext AllocSetContextCreate(MemoryContext parent, --- 131,142 ---- Size minContextSize, Size initBlockSize, Size maxBlockSize); + extern MemoryContext AllocSetContextCreateTracked(MemoryContext parent, + const char *name, + Size minContextSize, + Size initBlockSize, + Size maxBlockSize, + uint64 *total_mem); /* * Recommended default alloc parameters, suitable for "ordinary" contexts
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers