HI hackers, I thought it would be better to start a new thread to discuss. While working with sorting patch, and read others threads, I have some ideas to reduces memory consumption by aset and generation memory modules.
I have done basic benchmarks, and it seems to improve performance. I think it's really worth it, if it really is possible to reduce memory consumption. Linux Ubuntu 64 bits work_mem = 64MB set max_parallel_workers_per_gather = 0; create table t (a bigint not null, b bigint not null, c bigint not null, d bigint not null, e bigint not null, f bigint not null); insert into t select x,x,x,x,x,x from generate_Series(1,140247142) x; -- 10GB! vacuum freeze t; select * from t order by a offset 140247142; HEAD: postgres=# select * from t order by a offset 140247142; a | b | c | d | e | f ---+---+---+---+---+--- (0 rows) work_mem=64MB Time: 99603,544 ms (01:39,604) Time: 94000,342 ms (01:34,000) postgres=# set work_mem="64.2MB"; SET Time: 0,210 ms postgres=# select * from t order by a offset 140247142; a | b | c | d | e | f ---+---+---+---+---+--- (0 rows) Time: 95306,254 ms (01:35,306) PATCHED: postgres=# explain analyze select * from t order by a offset 140247142; a | b | c | d | e | f ---+---+---+---+---+--- (0 rows) work_mem=64MB Time: 90946,482 ms (01:30,946) postgres=# set work_mem="64.2MB"; SET Time: 0,210 ms postgres=# select * from t order by a offset 140247142; a | b | c | d | e | f ---+---+---+---+---+--- (0 rows) Time: 91817,533 ms (01:31,818) There is still room for further improvements, and at this point I need help. Regarding the patches we have: 1) 001-aset-reduces-memory-consumption.patch Reduces memory used by struct AllocBlockData by minus 8 bits, reducing the total size to 32 bits, which leads to "fitting" two structs in a 64bit cache. Move some stores to fields struct, for the order of declaration, within the structures. Remove tests elog(ERROR, "could not find block containing chunk %p" and elog(ERROR, "could not find block containing chunk %p", moving them to MEMORY_CONTEXT_CHECKING context. Since 8.2 versions, nobody complains about these tests. But if is not acceptable, have the option (3) 003-aset-reduces-memory-consumption.patch 2) 002-generation-reduces-memory-consumption.patch Reduces memory used by struct GenerationBlock, by minus 8 bits, reducing the total size to 32 bits, which leads to "fitting" two structs in a 64bit cache. Remove all references to the field *block* used by struct GenerationChunk, enabling its removal! (not done yet). What would take the final size to 16 bits, which leads to "fitting" four structs in a 64bit cache. Unfortunately, everything works only for the size 24, see the (4). Move some stores to fields struct, for the order of declaration, within the structures. 3) 003-aset-reduces-memory-consumption.patch Same to the (1), but without remove the tests: elog(ERROR, "could not find block containing chunk %p" and elog(ERROR, "could not find block containing chunk %p", But at the cost of removing a one tiny part of the tests. Since 8.2 versions, nobody complains about these tests. 4) 004-generation-reduces-memory-consumption-BUG.patch Same to the (2), but with BUG. It only takes a few tweaks to completely remove the field block. @@ -117,9 +116,9 @@ struct GenerationChunk /* this is zero in a free chunk */ Size requested_size; -#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T * 2 + SIZEOF_VOID_P * 2) +#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T * 2 + SIZEOF_VOID_P) #else -#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T + SIZEOF_VOID_P * 2) +#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T + SIZEOF_VOID_P) #endif /* MEMORY_CONTEXT_CHECKING */ /* ensure proper alignment by adding padding if needed */ @@ -127,7 +126,6 @@ struct GenerationChunk char padding[MAXIMUM_ALIGNOF - GENERATIONCHUNK_RAWSIZE % MAXIMUM_ALIGNOF]; #endif - GenerationBlock *block; /* block owning this chunk */ GenerationContext *context; /* owning context, or NULL if freed chunk */ /* there must not be any padding to reach a MAXALIGN boundary here! */ }; This fails with make check. I couldn't figure out why it doesn't work with 16 bits (struct GenerationChunk).
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index ec3e264a73..5e624ff733 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -150,11 +150,13 @@ typedef AllocSetContext *AllocSet; */ typedef struct AllocBlockData { - AllocSet aset; /* aset that owns this block */ AllocBlock prev; /* prev block in aset's blocks list, if any */ AllocBlock next; /* next block in aset's blocks list, if any */ char *freeptr; /* start of free space in this block */ char *endptr; /* end of space in this block */ +#ifdef MEMORY_CONTEXT_CHECKING + AllocSet aset; /* aset that owns this block */ +#endif } AllocBlockData; /* @@ -467,7 +469,7 @@ AllocSetContextCreateInternal(MemoryContext parent, * the context header and its block header follows that. */ set = (AllocSet) malloc(firstBlockSize); - if (set == NULL) + if (unlikely(set == NULL)) { if (TopMemoryContext) MemoryContextStats(TopMemoryContext); @@ -485,11 +487,13 @@ AllocSetContextCreateInternal(MemoryContext parent, /* Fill in the initial block's block header */ block = (AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext))); - block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) set) + firstBlockSize; block->prev = NULL; block->next = NULL; +#ifdef MEMORY_CONTEXT_CHECKING + block->aset = set; +#endif /* Mark unallocated space NOACCESS; leave the block header alone. */ VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, block->endptr - block->freeptr); @@ -738,17 +742,18 @@ AllocSetAlloc(MemoryContext context, Size size) chunk_size = MAXALIGN(size); blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; block = (AllocBlock) malloc(blksize); - if (block == NULL) + if (unlikely(block == NULL)) return NULL; - context->mem_allocated += blksize; - - block->aset = set; block->freeptr = block->endptr = ((char *) block) + blksize; +#ifdef MEMORY_CONTEXT_CHECKING + block->aset = set; +#endif chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ); - chunk->aset = set; chunk->size = chunk_size; + chunk->aset = set; + #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ @@ -779,6 +784,8 @@ AllocSetAlloc(MemoryContext context, Size size) set->blocks = block; } + context->mem_allocated += blksize; + /* Ensure any padding bytes are marked NOACCESS. */ VALGRIND_MAKE_MEM_NOACCESS((char *) AllocChunkGetPointer(chunk) + size, chunk_size - size); @@ -931,14 +938,14 @@ AllocSetAlloc(MemoryContext context, Size size) block = (AllocBlock) malloc(blksize); } - if (block == NULL) + if (unlikely(block == NULL)) return NULL; - context->mem_allocated += blksize; - - block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; +#ifdef MEMORY_CONTEXT_CHECKING + block->aset = set; +#endif /* Mark unallocated space NOACCESS. */ VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, @@ -948,7 +955,9 @@ AllocSetAlloc(MemoryContext context, Size size) block->next = set->blocks; if (block->next) block->next->prev = block; + set->blocks = block; + context->mem_allocated += blksize; } /* @@ -964,6 +973,7 @@ AllocSetAlloc(MemoryContext context, Size size) chunk->aset = (void *) set; chunk->size = chunk_size; + #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ @@ -1014,6 +1024,7 @@ AllocSetFree(MemoryContext context, void *pointer) */ AllocBlock block = (AllocBlock) (((char *) chunk) - ALLOC_BLOCKHDRSZ); +#ifdef MEMORY_CONTEXT_CHECKING /* * Try to verify that we have a sane block pointer: it should * reference the correct aset, and freeptr and endptr should point @@ -1024,6 +1035,7 @@ AllocSetFree(MemoryContext context, void *pointer) block->freeptr != ((char *) block) + (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)) elog(ERROR, "could not find block containing chunk %p", chunk); +#endif /* OK, remove block from aset's list and free it */ if (block->prev) @@ -1103,6 +1115,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) Size blksize; Size oldblksize; +#ifdef MEMORY_CONTEXT_CHECKING /* * Try to verify that we have a sane block pointer: it should * reference the correct aset, and freeptr and endptr should point @@ -1113,6 +1126,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) block->freeptr != ((char *) block) + (oldsize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)) elog(ERROR, "could not find block containing chunk %p", chunk); +#endif /* * Even if the new request is less than set->allocChunkLimit, we stick @@ -1128,17 +1142,13 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) oldblksize = block->endptr - ((char *) block); block = (AllocBlock) realloc(block, blksize); - if (block == NULL) + if (unlikely(block == NULL)) { /* Disallow external access to private part of chunk header. */ VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOCCHUNK_PRIVATE_LEN); return NULL; } - /* updated separately, not to underflow when (oldblksize > blksize) */ - context->mem_allocated -= oldblksize; - context->mem_allocated += blksize; - block->freeptr = block->endptr = ((char *) block) + blksize; /* Update pointers since block has likely been moved */ @@ -1186,6 +1196,10 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize); #endif + /* updated separately, not to underflow when (oldblksize > blksize) */ + context->mem_allocated -= oldblksize; + context->mem_allocated += blksize; + /* Ensure any padding bytes are marked NOACCESS. */ VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size, chksize - size); @@ -1263,7 +1277,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) newPointer = AllocSetAlloc((MemoryContext) set, size); /* leave immediately if request was not completed */ - if (newPointer == NULL) + if (unlikely(newPointer == NULL)) { /* Disallow external access to private part of chunk header. */ VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOCCHUNK_PRIVATE_LEN);
diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c index e530e272e0..17d768159f 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -89,7 +89,6 @@ typedef struct GenerationContext struct GenerationBlock { dlist_node node; /* doubly-linked list of blocks */ - Size blksize; /* allocated size of this block */ int nchunks; /* number of chunks in the block */ int nfree; /* number of free chunks */ char *freeptr; /* start of free space in this block */ @@ -127,7 +126,7 @@ struct GenerationChunk char padding[MAXIMUM_ALIGNOF - GENERATIONCHUNK_RAWSIZE % MAXIMUM_ALIGNOF]; #endif - GenerationBlock *block; /* block owning this chunk */ + GenerationBlock *block; /* TODO: remove me */ GenerationContext *context; /* owning context, or NULL if freed chunk */ /* there must not be any padding to reach a MAXALIGN boundary here! */ }; @@ -258,7 +257,7 @@ GenerationContextCreate(MemoryContext parent, * starts with the context header and its block header follows that. */ set = (GenerationContext *) malloc(allocSize); - if (set == NULL) + if (unlikely(set == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, @@ -276,12 +275,15 @@ GenerationContextCreate(MemoryContext parent, /* Fill in the initial block's block header */ block = (GenerationBlock *) (((char *) set) + MAXALIGN(sizeof(GenerationContext))); + /* determine the block size and initialize it */ firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext)); GenerationBlockInit(block, firstBlockSize); - /* add it to the doubly-linked list of blocks */ - dlist_push_head(&set->blocks, &block->node); + /* Fill in GenerationContext-specific header fields */ + set->initBlockSize = initBlockSize; + set->maxBlockSize = maxBlockSize; + set->nextBlockSize = initBlockSize; /* use it as the current allocation block */ set->block = block; @@ -292,10 +294,8 @@ GenerationContextCreate(MemoryContext parent, /* Mark block as not to be released at reset time */ set->keeper = block; - /* Fill in GenerationContext-specific header fields */ - set->initBlockSize = initBlockSize; - set->maxBlockSize = maxBlockSize; - set->nextBlockSize = initBlockSize; + /* add it to the doubly-linked list of blocks */ + dlist_push_head(&set->blocks, &block->node); /* * Compute the allocation chunk size limit for this context. @@ -356,12 +356,12 @@ GenerationReset(MemoryContext context) GenerationBlockFree(set, block); } - /* set it so new allocations to make use of the keeper block */ - set->block = set->keeper; - /* Reset block size allocation sequence, too */ set->nextBlockSize = set->initBlockSize; + /* set it so new allocations to make use of the keeper block */ + set->block = set->keeper; + /* Ensure there is only 1 item in the dlist */ Assert(!dlist_is_empty(&set->blocks)); Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks))); @@ -408,23 +408,22 @@ GenerationAlloc(MemoryContext context, Size size) Size blksize = required_size + Generation_BLOCKHDRSZ; block = (GenerationBlock *) malloc(blksize); - if (block == NULL) + if (unlikely(block == NULL)) return NULL; - context->mem_allocated += blksize; - /* block with a single (used) chunk */ - block->blksize = blksize; block->nchunks = 1; block->nfree = 0; /* the block is completely full */ block->freeptr = block->endptr = ((char *) block) + blksize; + /* add the block to the list of allocated blocks */ + dlist_push_head(&set->blocks, &block->node); + chunk = (GenerationChunk *) (((char *) block) + Generation_BLOCKHDRSZ); - chunk->block = block; - chunk->context = set; chunk->size = chunk_size; + chunk->context = set; #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; @@ -437,8 +436,7 @@ GenerationAlloc(MemoryContext context, Size size) randomize_mem((char *) GenerationChunkGetPointer(chunk), size); #endif - /* add the block to the list of allocated blocks */ - dlist_push_head(&set->blocks, &block->node); + context->mem_allocated += blksize; /* Ensure any padding bytes are marked NOACCESS. */ VALGRIND_MAKE_MEM_NOACCESS((char *) GenerationChunkGetPointer(chunk) + size, @@ -470,7 +468,6 @@ GenerationAlloc(MemoryContext context, Size size) if (block == NULL || GenerationBlockFreeBytes(block) < required_size) { - Size blksize; GenerationBlock *freeblock = set->freeblock; if (freeblock != NULL && @@ -492,6 +489,8 @@ GenerationAlloc(MemoryContext context, Size size) } else { + Size blksize; + /* * The first such block has size initBlockSize, and we double the * space in each succeeding block, but not more than maxBlockSize. @@ -510,17 +509,17 @@ GenerationAlloc(MemoryContext context, Size size) block = (GenerationBlock *) malloc(blksize); - if (block == NULL) + if (unlikely(block == NULL)) return NULL; - context->mem_allocated += blksize; - /* initialize the new block */ GenerationBlockInit(block, blksize); /* add it to the doubly-linked list of blocks */ dlist_push_head(&set->blocks, &block->node); + context->mem_allocated += blksize; + /* Zero out the freeblock in case it's become full */ set->freeblock = NULL; } @@ -543,9 +542,8 @@ GenerationAlloc(MemoryContext context, Size size) Assert(block->freeptr <= block->endptr); - chunk->block = block; - chunk->context = set; chunk->size = chunk_size; + chunk->context = set; #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; @@ -576,7 +574,6 @@ GenerationAlloc(MemoryContext context, Size size) static inline void GenerationBlockInit(GenerationBlock *block, Size blksize) { - block->blksize = blksize; block->nchunks = 0; block->nfree = 0; @@ -647,10 +644,10 @@ GenerationBlockFree(GenerationContext *set, GenerationBlock *block) /* release the block from the list of blocks */ dlist_delete(&block->node); - ((MemoryContext) set)->mem_allocated -= block->blksize; + ((MemoryContext) set)->mem_allocated -= block->endptr - ((char *) block); #ifdef CLOBBER_FREED_MEMORY - wipe_mem(block, block->blksize); + wipe_mem(block, block->endptr - ((char *) block)); #endif free(block); @@ -671,8 +668,6 @@ GenerationFree(MemoryContext context, void *pointer) /* Allow access to private part of chunk header. */ VALGRIND_MAKE_MEM_DEFINED(chunk, GENERATIONCHUNK_PRIVATE_LEN); - block = chunk->block; - #ifdef MEMORY_CONTEXT_CHECKING /* Test for someone scribbling on unused space in chunk */ if (chunk->requested_size < chunk->size) @@ -693,6 +688,7 @@ GenerationFree(MemoryContext context, void *pointer) chunk->requested_size = 0; #endif + block = (GenerationBlock *) (((char *) pointer) - Generation_BLOCKHDRSZ); block->nfree += 1; Assert(block->nchunks > 0); @@ -732,7 +728,7 @@ GenerationFree(MemoryContext context, void *pointer) */ dlist_delete(&block->node); - context->mem_allocated -= block->blksize; + context->mem_allocated -= block->endptr - ((char *) block); free(block); } @@ -822,7 +818,7 @@ GenerationRealloc(MemoryContext context, void *pointer, Size size) newPointer = GenerationAlloc((MemoryContext) set, size); /* leave immediately if request was not completed */ - if (newPointer == NULL) + if (unlikely(newPointer == NULL)) { /* Disallow external access to private part of chunk header. */ VALGRIND_MAKE_MEM_NOACCESS(chunk, GENERATIONCHUNK_PRIVATE_LEN); @@ -926,7 +922,7 @@ GenerationStats(MemoryContext context, nblocks++; nchunks += block->nchunks; nfreechunks += block->nfree; - totalspace += block->blksize; + totalspace += block->endptr - ((char *) block); freespace += (block->endptr - block->freeptr); } @@ -977,7 +973,7 @@ GenerationCheck(MemoryContext context) nchunks; char *ptr; - total_allocated += block->blksize; + total_allocated += block->endptr - ((char *) block); /* * nfree > nchunks is surely wrong. Equality is allowed as the block @@ -1004,11 +1000,6 @@ GenerationCheck(MemoryContext context) nchunks += 1; - /* chunks have both block and context pointers, so check both */ - if (chunk->block != block) - elog(WARNING, "problem in Generation %s: bogus block link in block %p, chunk %p", - name, block, chunk); - /* * Check for valid context pointer. Note this is an incomplete * test, since palloc(0) produces an allocated chunk with
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index ec3e264a73..8d6d804025 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -150,11 +150,13 @@ typedef AllocSetContext *AllocSet; */ typedef struct AllocBlockData { - AllocSet aset; /* aset that owns this block */ AllocBlock prev; /* prev block in aset's blocks list, if any */ AllocBlock next; /* next block in aset's blocks list, if any */ char *freeptr; /* start of free space in this block */ char *endptr; /* end of space in this block */ +#ifdef MEMORY_CONTEXT_CHECKING + AllocSet aset; /* aset that owns this block */ +#endif } AllocBlockData; /* @@ -467,7 +469,7 @@ AllocSetContextCreateInternal(MemoryContext parent, * the context header and its block header follows that. */ set = (AllocSet) malloc(firstBlockSize); - if (set == NULL) + if (unlikely(set == NULL)) { if (TopMemoryContext) MemoryContextStats(TopMemoryContext); @@ -485,11 +487,13 @@ AllocSetContextCreateInternal(MemoryContext parent, /* Fill in the initial block's block header */ block = (AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext))); - block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) set) + firstBlockSize; block->prev = NULL; block->next = NULL; +#ifdef MEMORY_CONTEXT_CHECKING + block->aset = set; +#endif /* Mark unallocated space NOACCESS; leave the block header alone. */ VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, block->endptr - block->freeptr); @@ -738,17 +742,18 @@ AllocSetAlloc(MemoryContext context, Size size) chunk_size = MAXALIGN(size); blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; block = (AllocBlock) malloc(blksize); - if (block == NULL) + if (unlikely(block == NULL)) return NULL; - context->mem_allocated += blksize; - - block->aset = set; block->freeptr = block->endptr = ((char *) block) + blksize; +#ifdef MEMORY_CONTEXT_CHECKING + block->aset = set; +#endif chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ); - chunk->aset = set; chunk->size = chunk_size; + chunk->aset = set; + #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ @@ -779,6 +784,8 @@ AllocSetAlloc(MemoryContext context, Size size) set->blocks = block; } + context->mem_allocated += blksize; + /* Ensure any padding bytes are marked NOACCESS. */ VALGRIND_MAKE_MEM_NOACCESS((char *) AllocChunkGetPointer(chunk) + size, chunk_size - size); @@ -931,14 +938,14 @@ AllocSetAlloc(MemoryContext context, Size size) block = (AllocBlock) malloc(blksize); } - if (block == NULL) + if (unlikely(block == NULL)) return NULL; - context->mem_allocated += blksize; - - block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; +#ifdef MEMORY_CONTEXT_CHECKING + block->aset = set; +#endif /* Mark unallocated space NOACCESS. */ VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, @@ -948,7 +955,9 @@ AllocSetAlloc(MemoryContext context, Size size) block->next = set->blocks; if (block->next) block->next->prev = block; + set->blocks = block; + context->mem_allocated += blksize; } /* @@ -964,6 +973,7 @@ AllocSetAlloc(MemoryContext context, Size size) chunk->aset = (void *) set; chunk->size = chunk_size; + #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ @@ -1019,8 +1029,7 @@ AllocSetFree(MemoryContext context, void *pointer) * reference the correct aset, and freeptr and endptr should point * just past the chunk. */ - if (block->aset != set || - block->freeptr != block->endptr || + if (block->freeptr != block->endptr || block->freeptr != ((char *) block) + (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)) elog(ERROR, "could not find block containing chunk %p", chunk); @@ -1108,8 +1117,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) * reference the correct aset, and freeptr and endptr should point * just past the chunk. */ - if (block->aset != set || - block->freeptr != block->endptr || + if (block->freeptr != block->endptr || block->freeptr != ((char *) block) + (oldsize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)) elog(ERROR, "could not find block containing chunk %p", chunk); @@ -1128,17 +1136,13 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) oldblksize = block->endptr - ((char *) block); block = (AllocBlock) realloc(block, blksize); - if (block == NULL) + if (unlikely(block == NULL)) { /* Disallow external access to private part of chunk header. */ VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOCCHUNK_PRIVATE_LEN); return NULL; } - /* updated separately, not to underflow when (oldblksize > blksize) */ - context->mem_allocated -= oldblksize; - context->mem_allocated += blksize; - block->freeptr = block->endptr = ((char *) block) + blksize; /* Update pointers since block has likely been moved */ @@ -1186,6 +1190,10 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize); #endif + /* updated separately, not to underflow when (oldblksize > blksize) */ + context->mem_allocated -= oldblksize; + context->mem_allocated += blksize; + /* Ensure any padding bytes are marked NOACCESS. */ VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size, chksize - size); @@ -1263,7 +1271,7 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) newPointer = AllocSetAlloc((MemoryContext) set, size); /* leave immediately if request was not completed */ - if (newPointer == NULL) + if (unlikely(newPointer == NULL)) { /* Disallow external access to private part of chunk header. */ VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOCCHUNK_PRIVATE_LEN);
diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c index e530e272e0..8ac2144ddd 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -89,7 +89,6 @@ typedef struct GenerationContext struct GenerationBlock { dlist_node node; /* doubly-linked list of blocks */ - Size blksize; /* allocated size of this block */ int nchunks; /* number of chunks in the block */ int nfree; /* number of free chunks */ char *freeptr; /* start of free space in this block */ @@ -117,9 +116,9 @@ struct GenerationChunk /* this is zero in a free chunk */ Size requested_size; -#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T * 2 + SIZEOF_VOID_P * 2) +#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T * 2 + SIZEOF_VOID_P) #else -#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T + SIZEOF_VOID_P * 2) +#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T + SIZEOF_VOID_P) #endif /* MEMORY_CONTEXT_CHECKING */ /* ensure proper alignment by adding padding if needed */ @@ -127,7 +126,6 @@ struct GenerationChunk char padding[MAXIMUM_ALIGNOF - GENERATIONCHUNK_RAWSIZE % MAXIMUM_ALIGNOF]; #endif - GenerationBlock *block; /* block owning this chunk */ GenerationContext *context; /* owning context, or NULL if freed chunk */ /* there must not be any padding to reach a MAXALIGN boundary here! */ }; @@ -258,7 +256,7 @@ GenerationContextCreate(MemoryContext parent, * starts with the context header and its block header follows that. */ set = (GenerationContext *) malloc(allocSize); - if (set == NULL) + if (unlikely(set == NULL)) { MemoryContextStats(TopMemoryContext); ereport(ERROR, @@ -276,12 +274,15 @@ GenerationContextCreate(MemoryContext parent, /* Fill in the initial block's block header */ block = (GenerationBlock *) (((char *) set) + MAXALIGN(sizeof(GenerationContext))); + /* determine the block size and initialize it */ firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext)); GenerationBlockInit(block, firstBlockSize); - /* add it to the doubly-linked list of blocks */ - dlist_push_head(&set->blocks, &block->node); + /* Fill in GenerationContext-specific header fields */ + set->initBlockSize = initBlockSize; + set->maxBlockSize = maxBlockSize; + set->nextBlockSize = initBlockSize; /* use it as the current allocation block */ set->block = block; @@ -292,10 +293,8 @@ GenerationContextCreate(MemoryContext parent, /* Mark block as not to be released at reset time */ set->keeper = block; - /* Fill in GenerationContext-specific header fields */ - set->initBlockSize = initBlockSize; - set->maxBlockSize = maxBlockSize; - set->nextBlockSize = initBlockSize; + /* add it to the doubly-linked list of blocks */ + dlist_push_head(&set->blocks, &block->node); /* * Compute the allocation chunk size limit for this context. @@ -356,12 +355,12 @@ GenerationReset(MemoryContext context) GenerationBlockFree(set, block); } - /* set it so new allocations to make use of the keeper block */ - set->block = set->keeper; - /* Reset block size allocation sequence, too */ set->nextBlockSize = set->initBlockSize; + /* set it so new allocations to make use of the keeper block */ + set->block = set->keeper; + /* Ensure there is only 1 item in the dlist */ Assert(!dlist_is_empty(&set->blocks)); Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks))); @@ -408,23 +407,22 @@ GenerationAlloc(MemoryContext context, Size size) Size blksize = required_size + Generation_BLOCKHDRSZ; block = (GenerationBlock *) malloc(blksize); - if (block == NULL) + if (unlikely(block == NULL)) return NULL; - context->mem_allocated += blksize; - /* block with a single (used) chunk */ - block->blksize = blksize; block->nchunks = 1; block->nfree = 0; /* the block is completely full */ block->freeptr = block->endptr = ((char *) block) + blksize; + /* add the block to the list of allocated blocks */ + dlist_push_head(&set->blocks, &block->node); + chunk = (GenerationChunk *) (((char *) block) + Generation_BLOCKHDRSZ); - chunk->block = block; - chunk->context = set; chunk->size = chunk_size; + chunk->context = set; #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; @@ -437,8 +435,7 @@ GenerationAlloc(MemoryContext context, Size size) randomize_mem((char *) GenerationChunkGetPointer(chunk), size); #endif - /* add the block to the list of allocated blocks */ - dlist_push_head(&set->blocks, &block->node); + context->mem_allocated += blksize; /* Ensure any padding bytes are marked NOACCESS. */ VALGRIND_MAKE_MEM_NOACCESS((char *) GenerationChunkGetPointer(chunk) + size, @@ -470,7 +467,6 @@ GenerationAlloc(MemoryContext context, Size size) if (block == NULL || GenerationBlockFreeBytes(block) < required_size) { - Size blksize; GenerationBlock *freeblock = set->freeblock; if (freeblock != NULL && @@ -492,6 +488,8 @@ GenerationAlloc(MemoryContext context, Size size) } else { + Size blksize; + /* * The first such block has size initBlockSize, and we double the * space in each succeeding block, but not more than maxBlockSize. @@ -510,17 +508,17 @@ GenerationAlloc(MemoryContext context, Size size) block = (GenerationBlock *) malloc(blksize); - if (block == NULL) + if (unlikely(block == NULL)) return NULL; - context->mem_allocated += blksize; - /* initialize the new block */ GenerationBlockInit(block, blksize); /* add it to the doubly-linked list of blocks */ dlist_push_head(&set->blocks, &block->node); + context->mem_allocated += blksize; + /* Zero out the freeblock in case it's become full */ set->freeblock = NULL; } @@ -543,9 +541,8 @@ GenerationAlloc(MemoryContext context, Size size) Assert(block->freeptr <= block->endptr); - chunk->block = block; - chunk->context = set; chunk->size = chunk_size; + chunk->context = set; #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; @@ -576,7 +573,6 @@ GenerationAlloc(MemoryContext context, Size size) static inline void GenerationBlockInit(GenerationBlock *block, Size blksize) { - block->blksize = blksize; block->nchunks = 0; block->nfree = 0; @@ -647,10 +643,10 @@ GenerationBlockFree(GenerationContext *set, GenerationBlock *block) /* release the block from the list of blocks */ dlist_delete(&block->node); - ((MemoryContext) set)->mem_allocated -= block->blksize; + ((MemoryContext) set)->mem_allocated -= block->endptr - ((char *) block); #ifdef CLOBBER_FREED_MEMORY - wipe_mem(block, block->blksize); + wipe_mem(block, block->endptr - ((char *) block)); #endif free(block); @@ -671,8 +667,6 @@ GenerationFree(MemoryContext context, void *pointer) /* Allow access to private part of chunk header. */ VALGRIND_MAKE_MEM_DEFINED(chunk, GENERATIONCHUNK_PRIVATE_LEN); - block = chunk->block; - #ifdef MEMORY_CONTEXT_CHECKING /* Test for someone scribbling on unused space in chunk */ if (chunk->requested_size < chunk->size) @@ -693,6 +687,7 @@ GenerationFree(MemoryContext context, void *pointer) chunk->requested_size = 0; #endif + block = (GenerationBlock *) (((char *) pointer) - Generation_BLOCKHDRSZ); block->nfree += 1; Assert(block->nchunks > 0); @@ -732,7 +727,7 @@ GenerationFree(MemoryContext context, void *pointer) */ dlist_delete(&block->node); - context->mem_allocated -= block->blksize; + context->mem_allocated -= block->endptr - ((char *) block); free(block); } @@ -822,7 +817,7 @@ GenerationRealloc(MemoryContext context, void *pointer, Size size) newPointer = GenerationAlloc((MemoryContext) set, size); /* leave immediately if request was not completed */ - if (newPointer == NULL) + if (unlikely(newPointer == NULL)) { /* Disallow external access to private part of chunk header. */ VALGRIND_MAKE_MEM_NOACCESS(chunk, GENERATIONCHUNK_PRIVATE_LEN); @@ -926,7 +921,7 @@ GenerationStats(MemoryContext context, nblocks++; nchunks += block->nchunks; nfreechunks += block->nfree; - totalspace += block->blksize; + totalspace += block->endptr - ((char *) block); freespace += (block->endptr - block->freeptr); } @@ -977,7 +972,7 @@ GenerationCheck(MemoryContext context) nchunks; char *ptr; - total_allocated += block->blksize; + total_allocated += block->endptr - ((char *) block); /* * nfree > nchunks is surely wrong. Equality is allowed as the block @@ -1004,11 +999,6 @@ GenerationCheck(MemoryContext context) nchunks += 1; - /* chunks have both block and context pointers, so check both */ - if (chunk->block != block) - elog(WARNING, "problem in Generation %s: bogus block link in block %p, chunk %p", - name, block, chunk); - /* * Check for valid context pointer. Note this is an incomplete * test, since palloc(0) produces an allocated chunk with