mirror of
https://github.com/postgres/postgres.git
synced 2025-05-11 05:41:32 +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
|
realloc are trickier. To make those work, we require all memory
|
||||||
context types to produce allocated chunks that are immediately,
|
context types to produce allocated chunks that are immediately,
|
||||||
without any padding, preceded by a uint64 value of which the least
|
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
|
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.
|
into that array.
|
||||||
|
|
||||||
If a type of allocator needs additional information about its chunks,
|
If a type of allocator needs additional information about its chunks,
|
||||||
like e.g. the size of the allocation, that information can in turn
|
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
|
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.
|
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
|
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
|
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
|
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
|
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
|
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
|
belongs to. 30 bits are used for each of these, but only a total of 59 bits
|
||||||
required then the memory context must manage that itself. This can be done by
|
as the lowest bit for the chunk to block offset is the same bit as the highest
|
||||||
calling the MemoryChunkSetHdrMaskExternal() function on the given chunk.
|
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
|
Currently, each memory context type stores large allocations on dedicated
|
||||||
blocks (which always contain only a single chunk). For these, finding the
|
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
|
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 *
|
* 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[] = {
|
static const MemoryContextMethods mcxt_methods[] = {
|
||||||
/* aset.c */
|
/* aset.c */
|
||||||
@ -97,33 +102,27 @@ static const MemoryContextMethods mcxt_methods[] = {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unused (as yet) IDs should have dummy entries here. This allows us to
|
* Reserved and unused IDs should have dummy entries here. This allows us
|
||||||
* fail cleanly if a bogus pointer is passed to pfree or the like. It
|
* 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
|
* seems sufficient to provide routines for the methods that might get
|
||||||
* invoked from inspection of a chunk (see MCXT_METHOD calls below).
|
* invoked from inspection of a chunk (see MCXT_METHOD calls below).
|
||||||
*/
|
*/
|
||||||
|
BOGUS_MCTX(MCTX_1_RESERVED_GLIBC_ID),
|
||||||
[MCTX_UNUSED1_ID].free_p = BogusFree,
|
BOGUS_MCTX(MCTX_2_RESERVED_GLIBC_ID),
|
||||||
[MCTX_UNUSED1_ID].realloc = BogusRealloc,
|
BOGUS_MCTX(MCTX_7_UNUSED_ID),
|
||||||
[MCTX_UNUSED1_ID].get_chunk_context = BogusGetChunkContext,
|
BOGUS_MCTX(MCTX_8_UNUSED_ID),
|
||||||
[MCTX_UNUSED1_ID].get_chunk_space = BogusGetChunkSpace,
|
BOGUS_MCTX(MCTX_9_UNUSED_ID),
|
||||||
|
BOGUS_MCTX(MCTX_10_UNUSED_ID),
|
||||||
[MCTX_UNUSED2_ID].free_p = BogusFree,
|
BOGUS_MCTX(MCTX_11_UNUSED_ID),
|
||||||
[MCTX_UNUSED2_ID].realloc = BogusRealloc,
|
BOGUS_MCTX(MCTX_12_UNUSED_ID),
|
||||||
[MCTX_UNUSED2_ID].get_chunk_context = BogusGetChunkContext,
|
BOGUS_MCTX(MCTX_13_UNUSED_ID),
|
||||||
[MCTX_UNUSED2_ID].get_chunk_space = BogusGetChunkSpace,
|
BOGUS_MCTX(MCTX_14_UNUSED_ID),
|
||||||
|
BOGUS_MCTX(MCTX_0_RESERVED_UNUSEDMEM_ID),
|
||||||
[MCTX_UNUSED3_ID].free_p = BogusFree,
|
BOGUS_MCTX(MCTX_15_RESERVED_WIPEDMEM_ID)
|
||||||
[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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#undef BOGUS_MCTX
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CurrentMemoryContext
|
* CurrentMemoryContext
|
||||||
* Default memory context for allocations.
|
* Default memory context for allocations.
|
||||||
|
@ -104,21 +104,29 @@ extern Size AlignedAllocGetChunkSpace(void *pointer);
|
|||||||
*/
|
*/
|
||||||
typedef enum MemoryContextMethodID
|
typedef enum MemoryContextMethodID
|
||||||
{
|
{
|
||||||
MCTX_UNUSED1_ID, /* 000 occurs in never-used memory */
|
MCTX_0_RESERVED_UNUSEDMEM_ID, /* 0000 occurs in never-used memory */
|
||||||
MCTX_UNUSED2_ID, /* glibc malloc'd chunks usually match 001 */
|
MCTX_1_RESERVED_GLIBC_ID, /* glibc malloc'd chunks usually match 0001 */
|
||||||
MCTX_UNUSED3_ID, /* glibc malloc'd chunks > 128kB match 010 */
|
MCTX_2_RESERVED_GLIBC_ID, /* glibc malloc'd chunks > 128kB match 0010 */
|
||||||
MCTX_ASET_ID,
|
MCTX_ASET_ID,
|
||||||
MCTX_GENERATION_ID,
|
MCTX_GENERATION_ID,
|
||||||
MCTX_SLAB_ID,
|
MCTX_SLAB_ID,
|
||||||
MCTX_ALIGNED_REDIRECT_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;
|
} MemoryContextMethodID;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The number of bits that 8-byte memory chunk headers can use to encode the
|
* The number of bits that 8-byte memory chunk headers can use to encode the
|
||||||
* MemoryContextMethodID.
|
* MemoryContextMethodID.
|
||||||
*/
|
*/
|
||||||
#define MEMORY_CONTEXT_METHODID_BITS 3
|
#define MEMORY_CONTEXT_METHODID_BITS 4
|
||||||
#define MEMORY_CONTEXT_METHODID_MASK \
|
#define MEMORY_CONTEXT_METHODID_MASK \
|
||||||
((((uint64) 1) << MEMORY_CONTEXT_METHODID_BITS) - 1)
|
((((uint64) 1) << MEMORY_CONTEXT_METHODID_BITS) - 1)
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* Although MemoryChunks are used by each of our MemoryContexts, future
|
* Although MemoryChunks are used by each of our MemoryContexts, future
|
||||||
* implementations may choose to implement their own method for storing chunk
|
* 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
|
* 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.
|
* of the given context.
|
||||||
*
|
*
|
||||||
* By default, a MemoryChunk is 8 bytes in size, however, when
|
* 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
|
* used to encode 4 separate pieces of information. Starting with the least
|
||||||
* significant bits of 'hdrmask', the bit space is reserved as follows:
|
* 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
|
* MEMORY_CONTEXT_METHODID_MASK
|
||||||
* 2. 1-bit to denote an "external" chunk (see below)
|
* 2. 1-bit to denote an "external" chunk (see below)
|
||||||
* 3. 30-bits reserved for the MemoryContext to use for anything it
|
* 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.
|
* chunk here.
|
||||||
* 4. 30-bits for the number of bytes that must be subtracted from the chunk
|
* 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.
|
* 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
|
* 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
|
* 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
|
* required for the chunk. In this case, the MemoryContext can choose to mark
|
||||||
@ -93,10 +101,16 @@
|
|||||||
*/
|
*/
|
||||||
#define MEMORYCHUNK_MAX_BLOCKOFFSET UINT64CONST(0x3FFFFFFF)
|
#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 the least significant base-0 bit of each portion of the hdrmask */
|
||||||
#define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS
|
#define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS
|
||||||
#define MEMORYCHUNK_VALUE_BASEBIT (MEMORYCHUNK_EXTERNAL_BASEBIT + 1)
|
#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
|
* 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)
|
(((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT) & MEMORYCHUNK_MAX_VALUE)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We should have used up all the bits here, so the compiler is likely to
|
* Shift the block offset down to the 0th bit position and mask off the single
|
||||||
* optimize out the & MEMORYCHUNK_MAX_BLOCKOFFSET.
|
* bit that's shared with the MemoryChunkGetValue field.
|
||||||
*/
|
*/
|
||||||
#define HdrMaskBlockOffset(hdrmask) \
|
#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 */
|
/* For external chunks only, check the magic number matches */
|
||||||
#define HdrMaskCheckMagic(hdrmask) \
|
#define HdrMaskCheckMagic(hdrmask) \
|
||||||
@ -149,6 +163,7 @@ typedef struct MemoryChunk
|
|||||||
* The number of bytes between 'block' and 'chunk' must be <=
|
* The number of bytes between 'block' and 'chunk' must be <=
|
||||||
* MEMORYCHUNK_MAX_BLOCKOFFSET.
|
* MEMORYCHUNK_MAX_BLOCKOFFSET.
|
||||||
* 'value' must be <= MEMORYCHUNK_MAX_VALUE.
|
* 'value' must be <= MEMORYCHUNK_MAX_VALUE.
|
||||||
|
* Both 'chunk' and 'block' must be MAXALIGNed pointers.
|
||||||
*/
|
*/
|
||||||
static inline void
|
static inline void
|
||||||
MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
|
MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
|
||||||
@ -157,7 +172,7 @@ MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
|
|||||||
Size blockoffset = (char *) chunk - (char *) block;
|
Size blockoffset = (char *) chunk - (char *) block;
|
||||||
|
|
||||||
Assert((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(value <= MEMORYCHUNK_MAX_VALUE);
|
||||||
Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
|
Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
|
||||||
|
|
||||||
@ -225,6 +240,7 @@ MemoryChunkGetBlock(MemoryChunk *chunk)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* cleanup all internal definitions */
|
/* cleanup all internal definitions */
|
||||||
|
#undef MEMORYCHUNK_BLOCKOFFSET_MASK
|
||||||
#undef MEMORYCHUNK_EXTERNAL_BASEBIT
|
#undef MEMORYCHUNK_EXTERNAL_BASEBIT
|
||||||
#undef MEMORYCHUNK_VALUE_BASEBIT
|
#undef MEMORYCHUNK_VALUE_BASEBIT
|
||||||
#undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT
|
#undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT
|
||||||
|
Loading…
x
Reference in New Issue
Block a user