Re: Reducing the chunk header sizes on all memory context types
David Rowley <dgrowleyml@gmail.com>
Attachments
- v2-0001-Improve-performance-of-and-reduce-overheads-of-me.patch (text/plain) patch v2-0001
On Wed, 13 Jul 2022 at 17:20, David Rowley <dgrowleyml@gmail.com> wrote:
> I did consider that in all cases where the allocations are above
> allocChunkLimit that the chunk is put on a dedicated block and in
> fact, the blockoffset is always the same for those. I wondered if we
> could use the full 60 bits for the chunksize for those cases. The
> reason I didn't pursue that is because:
As it turns out, we don't really need to explicitly store the chunk
size for chunks which are on dedicated blocks. We can just calculate
this by subtracting the pointer to the memory from the block's endptr.
The block offset is always fixed too, like I mentioned above.
I've now revised the patch to completely get rid of the concept of
"large" chunks and instead memory chunks are always 8 bytes in size.
I've created a struct to this effect and named it MemoryChunk. All
memory contexts make use of this new struct. The header bit which I
was previously using to denote a "large" chunk now marks if the chunk
is "external", meaning that the MemoryChunk does not have knowledge of
the chunk size and/or block offset. The MemoryContext itself is
expected to manage this when the chunk is external. I've coded up
aset.c and generation.c to always use these external chunks when size
> set->allocChunkLimit. There is now a coding pattern like the
following (taken from AllocSetRealloc:
if (MemoryChunkIsExternal(chunk))
{
block = ExternalChunkGetBlock(chunk);
oldsize = block->endptr - (char *) pointer;
}
else
{
block = MemoryChunkGetBlock(chunk);
oldsize = MemoryChunkGetSize(chunk);
}
Here the ExternalChunkGetBlock() macro is just subtracting the
ALLOC_BLOCKHDRSZ from the chunk pointer to obtain the block pointer,
whereas MemoryChunkGetBlock() is subtracting the blockoffset as is
stored in the 30-bits of the chunk header.
Andres and I had a conversation off-list about the storage of freelist
pointers. Currently I have these stored in the actual allocated
memory. The minimum allocation size is 8-bytes, which is big enough
for storing sizeof(void *). Andres suggested that it might be safer to
store this link at the end of the chunk rather than at the start.
I've not quite done that in the attached, but doing this should just
be a matter of adjusting the GetFreeListLink() macro to add the
chunksize - sizeof(AllocFreelistLink).
I did a little bit of benchmarking of the attached with a scale 1 pgbench.
master = 0b039e3a8
master
drowley@amd3990x:~$ pgbench -c 156 -j 156 -T 60 -S postgres
tps = 1638436.367793 (without initial connection time)
tps = 1652688.009579 (without initial connection time)
tps = 1671281.281856 (without initial connection time)
master + v2 patch
drowley@amd3990x:~$ pgbench -c 156 -j 156 -T 60 -S postgres
tps = 1825346.734486 (without initial connection time)
tps = 1824539.294142 (without initial connection time)
tps = 1807359.758822 (without initial connection time)
~10% faster.
There are a couple of things to note that might require discussion:
1. I've added a new restriction that block sizes cannot be above 1GB.
This is because the 30-bits in the MemoryChunk used for storing the
offset between the chunk and the block wouldn't be able to store the
offset if the chunk was offset more than 1GB from the block. I used
debian code search to see if I could find any user code that used
blocks this big. I found nothing.
2. slab.c has a new restriction that the chunk size cannot be >= 1GB.
I'm not at all concerned about this. I think if someone needed chunks
that big there'd be no benefits from slab context over an aset
context.
3. As mentioned above, aset.c now stores freelist pointers in the
allocated chunk's memory. This allows us to get the header down to 8
bytes instead of today's 16 bytes. There's an increased danger that
buggy code that writes to memory after a pfree could stomp on this.
I think the patch is now starting to take shape. I've added it to the
September commitfest [1].
David
[1] https://commitfest.postgresql.org/39/3810/
Commits
-
Harden memory context allocators against bogus chunk pointers.
- 0e87dfe46443 16.0 landed
-
Improve our ability to detect bogus pointers passed to pfree et al.
- 80ef92675823 16.0 landed
-
Remove MemoryContextContains().
- 9543eff5e015 16.0 landed
-
Remove uses of MemoryContextContains in nodeAgg.c and nodeWindowAgg.c.
- 42b746d4c982 16.0 landed
-
Temporarily make MemoryContextContains return false
- b76fb6c2a99e 16.0 landed
-
Make MemoryContextContains work correctly again
- 5265e91fd10d 16.0 landed
-
Make more effort to put a sentinel at the end of allocated memory
- 0e480385ec59 16.0 landed
-
Fix some possibly latent bugs in slab.c
- 258b0411b243 10.23 landed
- 1b1154396798 11.18 landed
- f249f1026f71 12.13 landed
- 210bece161b0 13.9 landed
- 6ec896109254 14.6 landed
- c4e861b7bba3 15.0 landed
-
Various cleanups of the new memory context header code
- 05f908423695 16.0 landed
-
Revert "Add missing padding from MemoryChunk struct"
- 5495796ad12a 16.0 landed
-
Use MAXALIGN() in calculations using sizeof(SlabBlock)
- d5ee4db0eaf6 16.0 landed
-
Add missing padding from MemoryChunk struct
- df0f4feef8de 16.0 landed
-
Improve performance of and reduce overheads of memory management
- c6e0fe1f2a08 16.0 landed