Improve performance of and reduce overheads of memory management
[pgsql.git] / src / include / utils / memutils_memorychunk.h
blob685c177b681ad2c3de551ace9bee999a244160d3
1 /*-------------------------------------------------------------------------
3 * memutils_memorychunk.h
4 * Here we define a struct named MemoryChunk which implementations of
5 * MemoryContexts may use as a header for chunks of memory they allocate.
7 * MemoryChunk provides a lightweight header that a MemoryContext can use to
8 * store a reference back to the block the which the given chunk is allocated
9 * on and also an additional 30-bits to store another value such as the size
10 * of the allocated chunk.
12 * Although MemoryChunks are used by each of our MemoryContexts, future
13 * implementations may choose to implement their own method for storing chunk
14 * headers. The only requirement is that the header ends with an 8-byte value
15 * which the least significant 3-bits of are set to the MemoryContextMethodID
16 * of the given context.
18 * By default, a MemoryChunk is 8 bytes in size, however, when
19 * MEMORY_CONTEXT_CHECKING is defined the header becomes 16 bytes in size due
20 * to the additional requested_size field. The MemoryContext may use this
21 * field for whatever they wish, but it is intended to be used for additional
22 * checks which are only done in MEMORY_CONTEXT_CHECKING builds.
24 * The MemoryChunk contains a uint64 field named 'hdrmask'. This field is
25 * used to encode 4 separate pieces of information. Starting with the least
26 * significant bits of 'hdrmask', the bit space is reserved as follows:
28 * 1. 3-bits to indicate the MemoryContextMethodID as defined by
29 * MEMORY_CONTEXT_METHODID_MASK
30 * 2. 1-bit to denote an "external" chunk (see below)
31 * 3. 30-bits reserved for the MemoryContext to use for anything it
32 * requires. Most MemoryContext likely want to store the size of the
33 * chunk here.
34 * 4. 30-bits for the number of bytes that must be subtracted from the chunk
35 * to obtain the address of the block that the chunk is stored on.
37 * In some cases, for example when memory allocations become large, it's
38 * possible fields 3 and 4 above are not large enough to store the values
39 * required for the chunk. In this case, the MemoryContext can choose to mark
40 * the chunk as "external" by calling the MemoryChunkSetExternal() function.
41 * When this is done, fields 3 and 4 are unavailable for use by the
42 * MemoryContext and it's up to the MemoryContext itself to devise its own
43 * method for getting the reference to the block.
45 * Interface:
47 * MemoryChunkSetHdrMask:
48 * Used to set up a non-external MemoryChunk.
50 * MemoryChunkSetHdrMaskExternal:
51 * Used to set up an externally managed MemoryChunk.
53 * MemoryChunkIsExternal:
54 * Determine if the given MemoryChunk is externally managed, i.e.
55 * MemoryChunkSetHdrMaskExternal() was called on the chunk.
57 * MemoryChunkGetValue:
58 * For non-external chunks, return the stored 30-bit value as it was set
59 * in the call to MemoryChunkSetHdrMask().
61 * MemoryChunkGetBlock:
62 * For non-external chunks, return a pointer to the block as it was set
63 * in the call to MemoryChunkSetHdrMask().
65 * Also exports:
66 * MEMORYCHUNK_MAX_VALUE
67 * MEMORYCHUNK_MAX_BLOCKOFFSET
68 * PointerGetMemoryChunk
69 * MemoryChunkGetPointer
71 * Portions Copyright (c) 2022, PostgreSQL Global Development Group
72 * Portions Copyright (c) 1994, Regents of the University of California
74 * src/include/utils/memutils_memorychunk.h
76 *-------------------------------------------------------------------------
79 #ifndef MEMUTILS_MEMORYCHUNK_H
80 #define MEMUTILS_MEMORYCHUNK_H
82 #include "utils/memutils_internal.h"
85 * The maximum allowed value that MemoryContexts can store in the value
86 * field. Must be 1 less than a power of 2.
88 #define MEMORYCHUNK_MAX_VALUE UINT64CONST(0x3FFFFFFF)
91 * The maximum distance in bytes that a MemoryChunk can be offset from the
92 * block that is storing the chunk. Must be 1 less than a power of 2.
94 #define MEMORYCHUNK_MAX_BLOCKOFFSET UINT64CONST(0x3FFFFFFF)
96 /* define the least significant base-0 bit of each portion of the hdrmask */
97 #define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS
98 #define MEMORYCHUNK_VALUE_BASEBIT (MEMORYCHUNK_EXTERNAL_BASEBIT + 1)
99 #define MEMORYCHUNK_BLOCKOFFSET_BASEBIT (MEMORYCHUNK_VALUE_BASEBIT + 30)
102 * A magic number for storing in the free bits of an external chunk. This
103 * must mask out the bits used for storing the MemoryContextMethodID and the
104 * external bit.
106 #define MEMORYCHUNK_MAGIC (UINT64CONST(0xB1A8DB858EB6EFBA) >> \
107 MEMORYCHUNK_VALUE_BASEBIT << \
108 MEMORYCHUNK_VALUE_BASEBIT)
110 typedef struct MemoryChunk
112 #ifdef MEMORY_CONTEXT_CHECKING
113 Size requested_size;
114 #endif
116 /* bitfield for storing details about the chunk */
117 uint64 hdrmask; /* must be last */
118 } MemoryChunk;
120 /* Get the MemoryChunk from the pointer */
121 #define PointerGetMemoryChunk(p) \
122 ((MemoryChunk *) ((char *) (p) - sizeof(MemoryChunk)))
123 /* Get the pointer from the MemoryChunk */
124 #define MemoryChunkGetPointer(c) \
125 ((void *) ((char *) (c) + sizeof(MemoryChunk)))
127 /* private macros for making the inline functions below more simple */
128 #define HdrMaskIsExternal(hdrmask) \
129 ((hdrmask) & (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT))
130 #define HdrMaskGetValue(hdrmask) \
131 (((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT) & MEMORYCHUNK_MAX_VALUE)
134 * We should have used up all the bits here, so the compiler is likely to
135 * optimize out the & MEMORYCHUNK_MAX_BLOCKOFFSET.
137 #define HdrMaskBlockOffset(hdrmask) \
138 (((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET)
140 /* For external chunks only, check the magic number matches */
141 #define HdrMaskCheckMagic(hdrmask) \
142 (MEMORYCHUNK_MAGIC == \
143 ((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT << MEMORYCHUNK_VALUE_BASEBIT))
145 * MemoryChunkSetHdrMask
146 * Store the given 'block', 'chunk_size' and 'methodid' in the given
147 * MemoryChunk.
149 * The number of bytes between 'block' and 'chunk' must be <=
150 * MEMORYCHUNK_MAX_BLOCKOFFSET.
151 * 'value' must be <= MEMORYCHUNK_MAX_VALUE.
153 static inline void
154 MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
155 Size value, MemoryContextMethodID methodid)
157 Size blockoffset = (char *) chunk - (char *) block;
159 Assert((char *) chunk > (char *) block);
160 Assert(blockoffset <= MEMORYCHUNK_MAX_BLOCKOFFSET);
161 Assert(value <= MEMORYCHUNK_MAX_VALUE);
162 Assert(methodid <= MEMORY_CONTEXT_METHODID_MASK);
164 chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
165 (((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) |
166 methodid;
170 * MemoryChunkSetHdrMaskExternal
171 * Set 'chunk' as an externally managed chunk. Here we only record the
172 * MemoryContextMethodID and set the external chunk bit.
174 static inline void
175 MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk,
176 MemoryContextMethodID methodid)
178 Assert(methodid <= MEMORY_CONTEXT_METHODID_MASK);
180 chunk->hdrmask = MEMORYCHUNK_MAGIC | (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT) |
181 methodid;
185 * MemoryChunkIsExternal
186 * Return true if 'chunk' is marked as external.
188 static inline bool
189 MemoryChunkIsExternal(MemoryChunk *chunk)
192 * External chunks should always store MEMORYCHUNK_MAGIC in the upper
193 * portion of the hdrmask, check that nothing has stomped on that.
195 Assert(!HdrMaskIsExternal(chunk->hdrmask) ||
196 HdrMaskCheckMagic(chunk->hdrmask));
198 return HdrMaskIsExternal(chunk->hdrmask);
202 * MemoryChunkGetValue
203 * For non-external chunks, returns the value field as it was set in
204 * MemoryChunkSetHdrMask.
206 static inline Size
207 MemoryChunkGetValue(MemoryChunk *chunk)
209 Assert(!HdrMaskIsExternal(chunk->hdrmask));
211 return HdrMaskGetValue(chunk->hdrmask);
215 * MemoryChunkGetBlock
216 * For non-external chunks, returns the pointer to the block as was set
217 * in MemoryChunkSetHdrMask.
219 static inline void *
220 MemoryChunkGetBlock(MemoryChunk *chunk)
222 Assert(!HdrMaskIsExternal(chunk->hdrmask));
224 return (void *) ((char *) chunk - HdrMaskBlockOffset(chunk->hdrmask));
227 /* cleanup all internal definitions */
228 #undef MEMORYCHUNK_EXTERNAL_BASEBIT
229 #undef MEMORYCHUNK_VALUE_BASEBIT
230 #undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT
231 #undef MEMORYCHUNK_MAGIC
232 #undef HdrMaskIsExternal
233 #undef HdrMaskGetValue
234 #undef HdrMaskBlockOffset
235 #undef HdrMaskCheckMagic
237 #endif /* MEMUTILS_MEMORYCHUNK_H */