On Tue, 11 Jul 2023 at 02:41, Melih Mutlu <m.melihmu...@gmail.com> wrote: > > - Afaics AllocSet->keeper is unnecessary these days, as it is always > > allocated > > together with the context itself. Saves 8 bytes. > > This seemed like a safe change and removed the keeper field in > AllocSet and Generation contexts. It saves an additional 8 bytes.
Seems like a good idea for an additional 8-bytes. I looked at your v2 patch. The only thing that really looked wrong were the (Size) casts in the context creation functions. These should have been casts to uint32 rather than Size. Basically, all the casts do is say to the compiler "Yes, I know this could cause truncation due to assigning to a size smaller than the source type's size". Some compilers will likely warn without that and the cast will stop them. We know there can't be any truncation due to the Asserts. There's also the fundamental limitation that MemoryChunk can't store block offsets larger than 1GBs anyway, so things will go bad if we tried to have blocks bigger than 1GB. Aside from that, I thought that a couple of other slab.c fields could be shrunken to uint32 as the v2 patch just reduces the size of 1 field which just creates a 4-byte hole in SlabContext. The fullChunkSize field is just the MAXALIGN(chunkSize) + sizeof(MemoryChunk). We should never be using slab contexts for any chunks anywhere near that size. aset.c would be a better context for that, so it seems fine to me to further restrict the maximum supported chunk size by another 8 bytes. I've attached your patch again along with a small delta of what I adjusted. My thoughts on these changes are that it's senseless to have Size typed fields for storing a value that's never larger than 2^30. Getting rid of the keeper pointer seems like a cleanup as it's pretty much a redundant field. For small sized contexts like the ones used for storing index relcache entries, I think it makes sense to save 20 more bytes. Each backend can have many thousand of those and there could be many hundred backends. If we can fit more allocations on that initial 1 kilobyte keeper block without having to allocate any additional blocks, then that's great. I feel that Andres's results showing several hundred fewer block allocations shows this working. Albeit, his patch reduced the size of the structs even further than what v3 does. I think v3 is enough for now as the additional changes Andres mentioned require some more invasive code changes to make work. If nobody objects or has other ideas about this, modulo commit message, I plan to push the attached on Monday. David
From e5e7da6a9880b0cbc7fa96d26cdd73d8bb02c88f Mon Sep 17 00:00:00 2001 From: Melih Mutlu <m.melihmu...@gmail.com> Date: Mon, 12 Jun 2023 08:50:28 +0300 Subject: [PATCH v3 1/2] Change memory context fields to uint32 Block sizes and chunk sizes can be upper bounded by MEMORYCHUNK_MAX_BLOCKOFFSET and MEMORYCHUNK_MAX_VALUE respectively. Values of both bounds correspond to 1 less than 1GB. This allows us to store the sizes of any block or chunk size limited by those bounds in 32 bits. This patch changes types of such fields that represents block or chunk sizes from 64-bit Size to 32-bit unsigned integers. Also; keeper fields from AllocSet and Generation structs are removed and added AllocSetKeeper/GenerationKeeper macros where needed. --- src/backend/utils/mmgr/aset.c | 52 +++++++++++++++++------------ src/backend/utils/mmgr/generation.c | 51 ++++++++++++++++------------ src/backend/utils/mmgr/slab.c | 6 ++-- 3 files changed, 62 insertions(+), 47 deletions(-) diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 0bbbf93672..797abef1b5 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -156,11 +156,10 @@ typedef struct AllocSetContext AllocBlock blocks; /* head of list of blocks in this set */ MemoryChunk *freelist[ALLOCSET_NUM_FREELISTS]; /* free chunk lists */ /* Allocation parameters for this context: */ - Size initBlockSize; /* initial block size */ - Size maxBlockSize; /* maximum block size */ - Size nextBlockSize; /* next block size to allocate */ - Size allocChunkLimit; /* effective chunk size limit */ - AllocBlock keeper; /* keep this block over resets */ + uint32 initBlockSize; /* initial block size */ + uint32 maxBlockSize; /* maximum block size */ + uint32 nextBlockSize; /* next block size to allocate */ + uint32 allocChunkLimit; /* effective chunk size limit */ /* freelist this context could be put in, or -1 if not a candidate: */ int freeListIndex; /* index in context_freelists[], or -1 */ } AllocSetContext; @@ -241,6 +240,14 @@ typedef struct AllocBlockData */ #define MAX_FREE_CONTEXTS 100 /* arbitrary limit on freelist length */ +/* Obtain the keeper block for an allocation set */ +#define AllocSetKeeper(set) \ + ((AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext)))) + +/* Check if the block is the keeper block of the given allocation set */ +#define IsKeeperBlock(set, block) \ + ((block) == (AllocSetKeeper(set))) + typedef struct AllocSetFreeList { int num_free; /* current list length */ @@ -345,7 +352,7 @@ AllocSetContextCreateInternal(MemoryContext parent, Size maxBlockSize) { int freeListIndex; - Size firstBlockSize; + uint32 firstBlockSize; AllocSet set; AllocBlock block; @@ -417,7 +424,7 @@ AllocSetContextCreateInternal(MemoryContext parent, name); ((MemoryContext) set)->mem_allocated = - set->keeper->endptr - ((char *) set); + AllocSetKeeper(set)->endptr - ((char *) set); return (MemoryContext) set; } @@ -452,8 +459,11 @@ AllocSetContextCreateInternal(MemoryContext parent, * we'd leak the header/initial block if we ereport in this stretch. */ - /* Fill in the initial block's block header */ - block = (AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext))); + /* + * Fill in the initial block's block header. The initial block is not to be + * released at reset time, considered as keeper block. + */ + block = AllocSetKeeper(set); block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) set) + firstBlockSize; @@ -465,15 +475,13 @@ AllocSetContextCreateInternal(MemoryContext parent, /* Remember block as part of block list */ set->blocks = block; - /* Mark block as not to be released at reset time */ - set->keeper = block; /* Finish filling in aset-specific parts of the context header */ MemSetAligned(set->freelist, 0, sizeof(set->freelist)); - set->initBlockSize = initBlockSize; - set->maxBlockSize = maxBlockSize; - set->nextBlockSize = initBlockSize; + set->initBlockSize = (Size) initBlockSize; + set->maxBlockSize = (Size) maxBlockSize; + set->nextBlockSize = (Size) initBlockSize; set->freeListIndex = freeListIndex; /* @@ -544,7 +552,7 @@ AllocSetReset(MemoryContext context) #endif /* Remember keeper block size for Assert below */ - keepersize = set->keeper->endptr - ((char *) set); + keepersize = AllocSetKeeper(set)->endptr - ((char *) set); /* Clear chunk freelists */ MemSetAligned(set->freelist, 0, sizeof(set->freelist)); @@ -552,13 +560,13 @@ AllocSetReset(MemoryContext context) block = set->blocks; /* New blocks list will be just the keeper block */ - set->blocks = set->keeper; + set->blocks = AllocSetKeeper(set); while (block != NULL) { AllocBlock next = block->next; - if (block == set->keeper) + if (IsKeeperBlock(set, block)) { /* Reset the block, but don't return it to malloc */ char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ; @@ -614,7 +622,7 @@ AllocSetDelete(MemoryContext context) #endif /* Remember keeper block size for Assert below */ - keepersize = set->keeper->endptr - ((char *) set); + keepersize = AllocSetKeeper(set)->endptr - ((char *) set); /* * If the context is a candidate for a freelist, put it into that freelist @@ -663,14 +671,14 @@ AllocSetDelete(MemoryContext context) { AllocBlock next = block->next; - if (block != set->keeper) + if (!IsKeeperBlock(set, block)) context->mem_allocated -= block->endptr - ((char *) block); #ifdef CLOBBER_FREED_MEMORY wipe_mem(block, block->freeptr - ((char *) block)); #endif - if (block != set->keeper) + if (!IsKeeperBlock(set, block)) free(block); block = next; @@ -1547,7 +1555,7 @@ AllocSetCheck(MemoryContext context) long nchunks = 0; bool has_external_chunk = false; - if (set->keeper == block) + if (IsKeeperBlock(set, block)) total_allocated += block->endptr - ((char *) set); else total_allocated += block->endptr - ((char *) block); @@ -1557,7 +1565,7 @@ AllocSetCheck(MemoryContext context) */ if (!blk_used) { - if (set->keeper != block) + if (!IsKeeperBlock(set, block)) elog(WARNING, "problem in alloc set %s: empty block %p", name, block); } diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c index 4fb8663cd6..d6a0811a72 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -61,17 +61,16 @@ typedef struct GenerationContext MemoryContextData header; /* Standard memory-context fields */ /* Generational context parameters */ - Size initBlockSize; /* initial block size */ - Size maxBlockSize; /* maximum block size */ - Size nextBlockSize; /* next block size to allocate */ - Size allocChunkLimit; /* effective chunk size limit */ + uint32 initBlockSize; /* initial block size */ + uint32 maxBlockSize; /* maximum block size */ + uint32 nextBlockSize; /* next block size to allocate */ + uint32 allocChunkLimit; /* effective chunk size limit */ GenerationBlock *block; /* current (most recently allocated) block, or * NULL if we've just freed the most recent * block */ GenerationBlock *freeblock; /* pointer to a block that's being recycled, * or NULL if there's no such block. */ - GenerationBlock *keeper; /* keep this block over resets */ dlist_head blocks; /* list of blocks */ } GenerationContext; @@ -120,6 +119,14 @@ struct GenerationBlock #define ExternalChunkGetBlock(chunk) \ (GenerationBlock *) ((char *) chunk - Generation_BLOCKHDRSZ) +/* Obtain the keeper block for a generation context */ +#define GenerationKeeper(set) \ + ((GenerationBlock *) (((char *) set) + MAXALIGN(sizeof(GenerationContext)))) + +/* Check if the block is the keeper block of the given generation context */ +#define IsKeeperBlock(set, block) \ + ((block) == (GenerationKeeper(set))) + /* Inlined helper functions */ static inline void GenerationBlockInit(GenerationContext *context, GenerationBlock *block, @@ -153,8 +160,8 @@ GenerationContextCreate(MemoryContext parent, Size initBlockSize, Size maxBlockSize) { - Size firstBlockSize; - Size allocSize; + uint32 firstBlockSize; + uint32 allocSize; GenerationContext *set; GenerationBlock *block; @@ -213,8 +220,11 @@ GenerationContextCreate(MemoryContext parent, */ dlist_init(&set->blocks); - /* Fill in the initial block's block header */ - block = (GenerationBlock *) (((char *) set) + MAXALIGN(sizeof(GenerationContext))); + /* + * Fill in the initial block's block header. The initial block is not to be + * released at reset time, considered as keeper block. + */ + block = GenerationKeeper(set); /* determine the block size and initialize it */ firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext)); GenerationBlockInit(set, block, firstBlockSize); @@ -228,13 +238,10 @@ GenerationContextCreate(MemoryContext parent, /* No free block, yet */ set->freeblock = NULL; - /* 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; + set->initBlockSize = (Size) initBlockSize; + set->maxBlockSize = (Size) maxBlockSize; + set->nextBlockSize = (Size) initBlockSize; /* * Compute the allocation chunk size limit for this context. @@ -294,14 +301,14 @@ GenerationReset(MemoryContext context) { GenerationBlock *block = dlist_container(GenerationBlock, node, miter.cur); - if (block == set->keeper) + if (IsKeeperBlock(set, block)) GenerationBlockMarkEmpty(block); else GenerationBlockFree(set, block); } /* set it so new allocations to make use of the keeper block */ - set->block = set->keeper; + set->block = GenerationKeeper(set); /* Reset block size allocation sequence, too */ set->nextBlockSize = set->initBlockSize; @@ -440,10 +447,10 @@ GenerationAlloc(MemoryContext context, Size size) */ set->freeblock = NULL; } - else if (GenerationBlockIsEmpty(set->keeper) && - GenerationBlockFreeBytes(set->keeper) >= required_size) + else if (GenerationBlockIsEmpty(GenerationKeeper(set)) && + GenerationBlockFreeBytes(GenerationKeeper(set)) >= required_size) { - block = set->keeper; + block = GenerationKeeper(set); } else { @@ -594,7 +601,7 @@ static inline void GenerationBlockFree(GenerationContext *set, GenerationBlock *block) { /* Make sure nobody tries to free the keeper block */ - Assert(block != set->keeper); + Assert(!IsKeeperBlock(set, block)); /* We shouldn't be freeing the freeblock either */ Assert(block != set->freeblock); @@ -691,7 +698,7 @@ GenerationFree(void *pointer) set = block->context; /* Don't try to free the keeper block, just mark it empty */ - if (block == set->keeper) + if (IsKeeperBlock(set, block)) { GenerationBlockMarkEmpty(block); return; diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c index 718dd2ba03..d5e0edbede 100644 --- a/src/backend/utils/mmgr/slab.c +++ b/src/backend/utils/mmgr/slab.c @@ -104,7 +104,7 @@ typedef struct SlabContext { MemoryContextData header; /* Standard memory-context fields */ /* Allocation parameters for this context: */ - Size chunkSize; /* the requested (non-aligned) chunk size */ + uint32 chunkSize; /* the requested (non-aligned) chunk size */ Size fullChunkSize; /* chunk size with chunk header and alignment */ Size blockSize; /* the size to make each block of chunks */ int32 chunksPerBlock; /* number of chunks that fit in 1 block */ @@ -374,7 +374,7 @@ SlabContextCreate(MemoryContext parent, */ /* Fill in SlabContext-specific header fields */ - slab->chunkSize = chunkSize; + slab->chunkSize = (Size) chunkSize; slab->fullChunkSize = fullChunkSize; slab->blockSize = blockSize; slab->chunksPerBlock = chunksPerBlock; @@ -506,7 +506,7 @@ SlabAlloc(MemoryContext context, Size size) /* make sure we only allow correct request size */ if (unlikely(size != slab->chunkSize)) - elog(ERROR, "unexpected alloc chunk size %zu (expected %zu)", + elog(ERROR, "unexpected alloc chunk size %zu (expected %u)", size, slab->chunkSize); /* -- 2.39.2
From 81825c3d3f7d12b5f5d68f3e132e5284f823e393 Mon Sep 17 00:00:00 2001 From: David Rowley <dgrow...@gmail.com> Date: Thu, 13 Jul 2023 16:41:29 +1200 Subject: [PATCH v3 2/2] fixup! Change memory context fields to uint32 --- src/backend/utils/mmgr/aset.c | 25 ++++++++++++------------- src/backend/utils/mmgr/generation.c | 29 ++++++++++++++--------------- src/backend/utils/mmgr/slab.c | 18 +++++++++++------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 797abef1b5..9b556396ee 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -241,12 +241,11 @@ typedef struct AllocBlockData #define MAX_FREE_CONTEXTS 100 /* arbitrary limit on freelist length */ /* Obtain the keeper block for an allocation set */ -#define AllocSetKeeper(set) \ +#define KeeperBlock(set) \ ((AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext)))) /* Check if the block is the keeper block of the given allocation set */ -#define IsKeeperBlock(set, block) \ - ((block) == (AllocSetKeeper(set))) +#define IsKeeperBlock(set, block) ((block) == (KeeperBlock(set))) typedef struct AllocSetFreeList { @@ -424,7 +423,7 @@ AllocSetContextCreateInternal(MemoryContext parent, name); ((MemoryContext) set)->mem_allocated = - AllocSetKeeper(set)->endptr - ((char *) set); + KeeperBlock(set)->endptr - ((char *) set); return (MemoryContext) set; } @@ -460,10 +459,10 @@ AllocSetContextCreateInternal(MemoryContext parent, */ /* - * Fill in the initial block's block header. The initial block is not to be - * released at reset time, considered as keeper block. + * Fill in the initial block's block header. The initial block is not to + * be released at reset time, considered as keeper block. */ - block = AllocSetKeeper(set); + block = KeeperBlock(set); block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) set) + firstBlockSize; @@ -479,9 +478,9 @@ AllocSetContextCreateInternal(MemoryContext parent, /* Finish filling in aset-specific parts of the context header */ MemSetAligned(set->freelist, 0, sizeof(set->freelist)); - set->initBlockSize = (Size) initBlockSize; - set->maxBlockSize = (Size) maxBlockSize; - set->nextBlockSize = (Size) initBlockSize; + set->initBlockSize = (uint32) initBlockSize; + set->maxBlockSize = (uint32) maxBlockSize; + set->nextBlockSize = (uint32) initBlockSize; set->freeListIndex = freeListIndex; /* @@ -552,7 +551,7 @@ AllocSetReset(MemoryContext context) #endif /* Remember keeper block size for Assert below */ - keepersize = AllocSetKeeper(set)->endptr - ((char *) set); + keepersize = KeeperBlock(set)->endptr - ((char *) set); /* Clear chunk freelists */ MemSetAligned(set->freelist, 0, sizeof(set->freelist)); @@ -560,7 +559,7 @@ AllocSetReset(MemoryContext context) block = set->blocks; /* New blocks list will be just the keeper block */ - set->blocks = AllocSetKeeper(set); + set->blocks = KeeperBlock(set); while (block != NULL) { @@ -622,7 +621,7 @@ AllocSetDelete(MemoryContext context) #endif /* Remember keeper block size for Assert below */ - keepersize = AllocSetKeeper(set)->endptr - ((char *) set); + keepersize = KeeperBlock(set)->endptr - ((char *) set); /* * If the context is a candidate for a freelist, put it into that freelist diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c index d6a0811a72..a152fbf0c5 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -120,12 +120,11 @@ struct GenerationBlock (GenerationBlock *) ((char *) chunk - Generation_BLOCKHDRSZ) /* Obtain the keeper block for a generation context */ -#define GenerationKeeper(set) \ +#define KeeperBlock(set) \ ((GenerationBlock *) (((char *) set) + MAXALIGN(sizeof(GenerationContext)))) /* Check if the block is the keeper block of the given generation context */ -#define IsKeeperBlock(set, block) \ - ((block) == (GenerationKeeper(set))) +#define IsKeeperBlock(set, block) ((block) == (KeeperBlock(set))) /* Inlined helper functions */ static inline void GenerationBlockInit(GenerationContext *context, @@ -160,8 +159,8 @@ GenerationContextCreate(MemoryContext parent, Size initBlockSize, Size maxBlockSize) { - uint32 firstBlockSize; - uint32 allocSize; + Size firstBlockSize; + Size allocSize; GenerationContext *set; GenerationBlock *block; @@ -221,10 +220,10 @@ GenerationContextCreate(MemoryContext parent, dlist_init(&set->blocks); /* - * Fill in the initial block's block header. The initial block is not to be - * released at reset time, considered as keeper block. + * Fill in the initial block's block header. The initial block is not to + * be released at reset time, considered as keeper block. */ - block = GenerationKeeper(set); + block = KeeperBlock(set); /* determine the block size and initialize it */ firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext)); GenerationBlockInit(set, block, firstBlockSize); @@ -239,9 +238,9 @@ GenerationContextCreate(MemoryContext parent, set->freeblock = NULL; /* Fill in GenerationContext-specific header fields */ - set->initBlockSize = (Size) initBlockSize; - set->maxBlockSize = (Size) maxBlockSize; - set->nextBlockSize = (Size) initBlockSize; + set->initBlockSize = (uint32) initBlockSize; + set->maxBlockSize = (uint32) maxBlockSize; + set->nextBlockSize = (uint32) initBlockSize; /* * Compute the allocation chunk size limit for this context. @@ -308,7 +307,7 @@ GenerationReset(MemoryContext context) } /* set it so new allocations to make use of the keeper block */ - set->block = GenerationKeeper(set); + set->block = KeeperBlock(set); /* Reset block size allocation sequence, too */ set->nextBlockSize = set->initBlockSize; @@ -447,10 +446,10 @@ GenerationAlloc(MemoryContext context, Size size) */ set->freeblock = NULL; } - else if (GenerationBlockIsEmpty(GenerationKeeper(set)) && - GenerationBlockFreeBytes(GenerationKeeper(set)) >= required_size) + else if (GenerationBlockIsEmpty(KeeperBlock(set)) && + GenerationBlockFreeBytes(KeeperBlock(set)) >= required_size) { - block = GenerationKeeper(set); + block = KeeperBlock(set); } else { diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c index d5e0edbede..40c1d401c4 100644 --- a/src/backend/utils/mmgr/slab.c +++ b/src/backend/utils/mmgr/slab.c @@ -105,8 +105,8 @@ typedef struct SlabContext MemoryContextData header; /* Standard memory-context fields */ /* Allocation parameters for this context: */ uint32 chunkSize; /* the requested (non-aligned) chunk size */ - Size fullChunkSize; /* chunk size with chunk header and alignment */ - Size blockSize; /* the size to make each block of chunks */ + uint32 fullChunkSize; /* chunk size with chunk header and alignment */ + uint32 blockSize; /* the size to make each block of chunks */ int32 chunksPerBlock; /* number of chunks that fit in 1 block */ int32 curBlocklistIndex; /* index into the blocklist[] element * containing the fullest, blocks */ @@ -314,7 +314,9 @@ SlabGetNextFreeChunk(SlabContext *slab, SlabBlock *block) * blockSize: allocation block size * chunkSize: allocation chunk size * - * The MAXALIGN(chunkSize) may not exceed MEMORYCHUNK_MAX_VALUE + * The Slab_CHUNKHDRSZ + MAXALIGN(chunkSize + 1) may not exceed + * MEMORYCHUNK_MAX_VALUE. + * 'blockSize' may not exceed MEMORYCHUNK_MAX_BLOCKOFFSET. */ MemoryContext SlabContextCreate(MemoryContext parent, @@ -330,7 +332,7 @@ SlabContextCreate(MemoryContext parent, /* ensure MemoryChunk's size is properly maxaligned */ StaticAssertDecl(Slab_CHUNKHDRSZ == MAXALIGN(Slab_CHUNKHDRSZ), "sizeof(MemoryChunk) is not maxaligned"); - Assert(MAXALIGN(chunkSize) <= MEMORYCHUNK_MAX_VALUE); + Assert(blockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET); /* * Ensure there's enough space to store the pointer to the next free chunk @@ -347,6 +349,8 @@ SlabContextCreate(MemoryContext parent, fullChunkSize = Slab_CHUNKHDRSZ + MAXALIGN(chunkSize); #endif + Assert(fullChunkSize <= MEMORYCHUNK_MAX_VALUE); + /* compute the number of chunks that will fit on each block */ chunksPerBlock = (blockSize - Slab_BLOCKHDRSZ) / fullChunkSize; @@ -374,9 +378,9 @@ SlabContextCreate(MemoryContext parent, */ /* Fill in SlabContext-specific header fields */ - slab->chunkSize = (Size) chunkSize; - slab->fullChunkSize = fullChunkSize; - slab->blockSize = blockSize; + slab->chunkSize = (uint32) chunkSize; + slab->fullChunkSize = (uint32) fullChunkSize; + slab->blockSize = (uint32) blockSize; slab->chunksPerBlock = chunksPerBlock; slab->curBlocklistIndex = 0; -- 2.39.2