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