mirror of
https://github.com/postgres/postgres.git
synced 2025-05-06 19:59:18 +03:00
Enlarge bit-space for MemoryContextMethodID
Reserve 4 bits for MemoryContextMethodID rather than 3. 3 bits did technically allow a maximum of 8 memory context types, however, we've opted to reserve some bit patterns which left us with only 4 slots, all of which were used. Here we add another bit which frees up 8 slots for future memory context types. In passing, adjust the enum names in MemoryContextMethodID to make it more clear which ones can be used and which ones are reserved. Author: Matthias van de Meent, David Rowley Discussion: https://postgr.es/m/CAApHDvqGSpCU95TmM=Bp=6xjL_nLys4zdZOpfNyWBk97Xrdj2w@mail.gmail.com
This commit is contained in:
parent
c4ab7da606
commit
0ba8b75e7e
@ -395,14 +395,14 @@ relevant MemoryContext as a parameter, operations like free and
|
||||
realloc are trickier. To make those work, we require all memory
|
||||
context types to produce allocated chunks that are immediately,
|
||||
without any padding, preceded by a uint64 value of which the least
|
||||
significant 3 bits are set to the owning context's MemoryContextMethodID.
|
||||
significant 4 bits are set to the owning context's MemoryContextMethodID.
|
||||
This allows the code to determine the correct MemoryContextMethods to
|
||||
use by looking up the mcxt_methods[] array using the 3 bits as an index
|
||||
use by looking up the mcxt_methods[] array using the 4 bits as an index
|
||||
into that array.
|
||||
|
||||
If a type of allocator needs additional information about its chunks,
|
||||
like e.g. the size of the allocation, that information can in turn
|
||||
either be encoded into the remaining 61 bits of the preceding uint64 value
|
||||
either be encoded into the remaining 60 bits of the preceding uint64 value
|
||||
or if more space is required, additional values may be stored directly prior
|
||||
to the uint64 value. It is up to the context implementation to manage this.
|
||||
|
||||
@ -420,13 +420,20 @@ pfree(void *pointer)
|
||||
|
||||
All of the current memory contexts make use of the MemoryChunk header type
|
||||
which is defined in memutils_memorychunk.h. This suits all of the existing
|
||||
context types well as it makes use of the remaining 61-bits of the uint64
|
||||
context types well as it makes use of the remaining 60-bits of the uint64
|
||||
header to efficiently encode the size of the chunk of memory (or freelist
|
||||
index, in the case of aset.c) and the number of bytes which must be subtracted
|
||||
from the chunk in order to obtain a reference to the block that the chunk
|
||||
belongs to. 30 bits are used for each of these. If more than 30 bits are
|
||||
required then the memory context must manage that itself. This can be done by
|
||||
calling the MemoryChunkSetHdrMaskExternal() function on the given chunk.
|
||||
belongs to. 30 bits are used for each of these, but only a total of 59 bits
|
||||
as the lowest bit for the chunk to block offset is the same bit as the highest
|
||||
bit of the chunk size. This overlapping is possible as the relative offset
|
||||
between the block and the chunk is expected to be a MAXALIGNed value which
|
||||
guarantees the lowest bit is always 0. If more than 30 bits are required for
|
||||
each of these fields then the memory context must manage that itself. This
|
||||
can be done by calling the MemoryChunkSetHdrMaskExternal() function on the
|
||||
given chunk. Whether a chunk is an external chunk can be determined by the 1
|
||||
remaining bit from the 64-bit MemoryChunk.
|
||||
|
||||
Currently, each memory context type stores large allocations on dedicated
|
||||
blocks (which always contain only a single chunk). For these, finding the
|
||||
block is simple as we know that the chunk must be the first on the given
|
||||
|
@ -37,6 +37,11 @@ static Size BogusGetChunkSpace(void *pointer);
|
||||
/*****************************************************************************
|
||||
* GLOBAL MEMORY *
|
||||
*****************************************************************************/
|
||||
#define BOGUS_MCTX(id) \
|
||||
[id].free_p = BogusFree, \
|
||||
[id].realloc = BogusRealloc, \
|
||||
[id].get_chunk_context = BogusGetChunkContext, \
|
||||
[id].get_chunk_space = BogusGetChunkSpace
|
||||
|
||||
static const MemoryContextMethods mcxt_methods[] = {
|
||||
/* aset.c */
|
||||
@ -97,33 +102,27 @@ static const MemoryContextMethods mcxt_methods[] = {
|
||||
|
||||
|
||||
/*
|
||||
* Unused (as yet) IDs should have dummy entries here. This allows us to
|
||||
* fail cleanly if a bogus pointer is passed to pfree or the like. It
|
||||
* Reserved and unused IDs should have dummy entries here. This allows us
|
||||
* to fail cleanly if a bogus pointer is passed to pfree or the like. It
|
||||
* seems sufficient to provide routines for the methods that might get
|
||||
* invoked from inspection of a chunk (see MCXT_METHOD calls below).
|
||||
*/
|
||||
|
||||
[MCTX_UNUSED1_ID].free_p = BogusFree,
|
||||
[MCTX_UNUSED1_ID].realloc = BogusRealloc,
|
||||
[MCTX_UNUSED1_ID].get_chunk_context = BogusGetChunkContext,
|
||||
[MCTX_UNUSED1_ID].get_chunk_space = BogusGetChunkSpace,
|
||||
|
||||
[MCTX_UNUSED2_ID].free_p = BogusFree,
|
||||
[MCTX_UNUSED2_ID].realloc = BogusRealloc,
|
||||
[MCTX_UNUSED2_ID].get_chunk_context = BogusGetChunkContext,
|
||||
[MCTX_UNUSED2_ID].get_chunk_space = BogusGetChunkSpace,
|
||||
|
||||
[MCTX_UNUSED3_ID].free_p = BogusFree,
|
||||
[MCTX_UNUSED3_ID].realloc = BogusRealloc,
|
||||
[MCTX_UNUSED3_ID].get_chunk_context = BogusGetChunkContext,
|
||||
[MCTX_UNUSED3_ID].get_chunk_space = BogusGetChunkSpace,
|
||||
|
||||
[MCTX_UNUSED4_ID].free_p = BogusFree,
|
||||
[MCTX_UNUSED4_ID].realloc = BogusRealloc,
|
||||
[MCTX_UNUSED4_ID].get_chunk_context = BogusGetChunkContext,
|
||||
[MCTX_UNUSED4_ID].get_chunk_space = BogusGetChunkSpace,
|
||||
BOGUS_MCTX(MCTX_1_RESERVED_GLIBC_ID),
|
||||
BOGUS_MCTX(MCTX_2_RESERVED_GLIBC_ID),
|
||||
BOGUS_MCTX(MCTX_7_UNUSED_ID),
|
||||
BOGUS_MCTX(MCTX_8_UNUSED_ID),
|
||||
BOGUS_MCTX(MCTX_9_UNUSED_ID),
|
||||
BOGUS_MCTX(MCTX_10_UNUSED_ID),
|
||||
BOGUS_MCTX(MCTX_11_UNUSED_ID),
|
||||
BOGUS_MCTX(MCTX_12_UNUSED_ID),
|
||||
BOGUS_MCTX(MCTX_13_UNUSED_ID),
|
||||
BOGUS_MCTX(MCTX_14_UNUSED_ID),
|
||||
BOGUS_MCTX(MCTX_0_RESERVED_UNUSEDMEM_ID),
|
||||
BOGUS_MCTX(MCTX_15_RESERVED_WIPEDMEM_ID)
|
||||
};
|
||||
|
||||
#undef BOGUS_MCTX
|
||||
|
||||
/*
|
||||
* CurrentMemoryContext
|
||||
* Default memory context for allocations.
|
||||
|
@ -104,21 +104,29 @@ extern Size AlignedAllocGetChunkSpace(void *pointer);
|
||||
*/
|
||||
typedef enum MemoryContextMethodID
|
||||
{
|
||||
MCTX_UNUSED1_ID, /* 000 occurs in never-used memory */
|
||||
MCTX_UNUSED2_ID, /* glibc malloc'd chunks usually match 001 */
|
||||
MCTX_UNUSED3_ID, /* glibc malloc'd chunks > 128kB match 010 */
|
||||
MCTX_0_RESERVED_UNUSEDMEM_ID, /* 0000 occurs in never-used memory */
|
||||
MCTX_1_RESERVED_GLIBC_ID, /* glibc malloc'd chunks usually match 0001 */
|
||||
MCTX_2_RESERVED_GLIBC_ID, /* glibc malloc'd chunks > 128kB match 0010 */
|
||||
MCTX_ASET_ID,
|
||||
MCTX_GENERATION_ID,
|
||||
MCTX_SLAB_ID,
|
||||
MCTX_ALIGNED_REDIRECT_ID,
|
||||
MCTX_UNUSED4_ID, /* 111 occurs in wipe_mem'd memory */
|
||||
MCTX_7_UNUSED_ID,
|
||||
MCTX_8_UNUSED_ID,
|
||||
MCTX_9_UNUSED_ID,
|
||||
MCTX_10_UNUSED_ID,
|
||||
MCTX_11_UNUSED_ID,
|
||||
MCTX_12_UNUSED_ID,
|
||||
MCTX_13_UNUSED_ID,
|
||||
MCTX_14_UNUSED_ID,
|
||||
MCTX_15_RESERVED_WIPEDMEM_ID /* 1111 occurs in wipe_mem'd memory */
|
||||
} MemoryContextMethodID;
|
||||
|
||||
/*
|
||||
* The number of bits that 8-byte memory chunk headers can use to encode the
|
||||
* MemoryContextMethodID.
|
||||
*/
|
||||
#define MEMORY_CONTEXT_METHODID_BITS 3
|
||||
#define MEMORY_CONTEXT_METHODID_BITS 4
|
||||
#define MEMORY_CONTEXT_METHODID_MASK \
|
||||
((((uint64) 1) << MEMORY_CONTEXT_METHODID_BITS) - 1)
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
* Although MemoryChunks are used by each of our MemoryContexts, future
|
||||
* implementations may choose to implement their own method for storing chunk
|
||||
* headers. The only requirement is that the header ends with an 8-byte value
|
||||
* which the least significant 3-bits of are set to the MemoryContextMethodID
|
||||
* which the least significant 4-bits of are set to the MemoryContextMethodID
|
||||
* of the given context.
|
||||
*
|
||||
* By default, a MemoryChunk is 8 bytes in size, however, when
|
||||
@ -25,15 +25,23 @@
|
||||
* used to encode 4 separate pieces of information. Starting with the least
|
||||
* significant bits of 'hdrmask', the bit space is reserved as follows:
|
||||
*
|
||||
* 1. 3-bits to indicate the MemoryContextMethodID as defined by
|
||||
* 1. 4-bits to indicate the MemoryContextMethodID as defined by
|
||||
* MEMORY_CONTEXT_METHODID_MASK
|
||||
* 2. 1-bit to denote an "external" chunk (see below)
|
||||
* 3. 30-bits reserved for the MemoryContext to use for anything it
|
||||
* requires. Most MemoryContext likely want to store the size of the
|
||||
* requires. Most MemoryContexts likely want to store the size of the
|
||||
* chunk here.
|
||||
* 4. 30-bits for the number of bytes that must be subtracted from the chunk
|
||||
* to obtain the address of the block that the chunk is stored on.
|
||||
*
|
||||
* If you're paying close attention, you'll notice this adds up to 65 bits
|
||||
* rather than 64 bits. This is because the highest-order bit of #3 is the
|
||||
* same bit as the lowest-order bit of #4. We can do this as we insist that
|
||||
* the chunk and block pointers are both MAXALIGNed, therefore the relative
|
||||
* offset between those will always be a MAXALIGNed value which means the
|
||||
* lowest order bit is always 0. When fetching the chunk to block offset we
|
||||
* mask out the lowest-order bit to ensure it's still zero.
|
||||
*
|
||||
* In some cases, for example when memory allocations become large, it's
|
||||
* possible fields 3 and 4 above are not large enough to store the values
|
||||
* required for the chunk. In this case, the MemoryContext can choose to mark
|
||||
@ -93,10 +101,16 @@
|
||||
*/
|
||||
#define MEMORYCHUNK_MAX_BLOCKOFFSET UINT64CONST(0x3FFFFFFF)
|
||||
|
||||
/*
|
||||
* As above, but mask out the lowest-order (always zero) bit as this is shared
|
||||
* with the MemoryChunkGetValue field.
|
||||
*/
|
||||
#define MEMORYCHUNK_BLOCKOFFSET_MASK UINT64CONST(0x3FFFFFFE)
|
||||
|
||||
/* define the least significant base-0 bit of each portion of the hdrmask */
|
||||
#define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS
|
||||
#define MEMORYCHUNK_VALUE_BASEBIT (MEMORYCHUNK_EXTERNAL_BASEBIT + 1)
|
||||
#define MEMORYCHUNK_BLOCKOFFSET_BASEBIT (MEMORYCHUNK_VALUE_BASEBIT + 30)
|
||||
#define MEMORYCHUNK_BLOCKOFFSET_BASEBIT (MEMORYCHUNK_VALUE_BASEBIT + 29)
|
||||
|
||||
/*
|
||||
* A magic number for storing in the free bits of an external chunk. This
|
||||
@ -131,11 +145,11 @@ typedef struct MemoryChunk
|
||||
(((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT) & MEMORYCHUNK_MAX_VALUE)
|
||||
|
||||
/*
|
||||
* We should have used up all the bits here, so the compiler is likely to
|
||||
* optimize out the & MEMORYCHUNK_MAX_BLOCKOFFSET.
|
||||
* Shift the block offset down to the 0th bit position and mask off the single
|
||||
* bit that's shared with the MemoryChunkGetValue field.
|
||||
*/
|
||||
#define HdrMaskBlockOffset(hdrmask) \
|
||||
(((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET)
|
||||
(((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_BLOCKOFFSET_MASK)
|
||||
|
||||
/* For external chunks only, check the magic number matches */
|
||||
#define HdrMaskCheckMagic(hdrmask) \
|
||||
@ -149,6 +163,7 @@ typedef struct MemoryChunk
|
||||
* The number of bytes between 'block' and 'chunk' must be <=
|
||||
* MEMORYCHUNK_MAX_BLOCKOFFSET.
|
||||
* 'value' must be <= MEMORYCHUNK_MAX_VALUE.
|
||||
* Both 'chunk' and 'block' must be MAXALIGNed pointers.
|
||||
*/
|
||||
static inline void
|
||||
MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
|
||||
@ -157,7 +172,7 @@ MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
|
||||
Size blockoffset = (char *) chunk - (char *) block;
|
||||
|
||||
Assert((char *) chunk >= (char *) block);
|
||||
Assert(blockoffset <= MEMORYCHUNK_MAX_BLOCKOFFSET);
|
||||
Assert((blockoffset & MEMORYCHUNK_BLOCKOFFSET_MASK) == blockoffset);
|
||||
Assert(value <= MEMORYCHUNK_MAX_VALUE);
|
||||
Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
|
||||
|
||||
@ -225,6 +240,7 @@ MemoryChunkGetBlock(MemoryChunk *chunk)
|
||||
}
|
||||
|
||||
/* cleanup all internal definitions */
|
||||
#undef MEMORYCHUNK_BLOCKOFFSET_MASK
|
||||
#undef MEMORYCHUNK_EXTERNAL_BASEBIT
|
||||
#undef MEMORYCHUNK_VALUE_BASEBIT
|
||||
#undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT
|
||||
|
Loading…
x
Reference in New Issue
Block a user