From 1477f5b3965dabd36a7d73b6577e7895896612ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Tue, 9 Jun 2026 01:06:48 +0200 Subject: [PATCH] Proper memory commitment --- src/arena.c | 58 ++++++++++++++++++++++++++---------------- src/arena.h | 10 +++++--- src/base_core.h | 9 ++++--- src/main.c | 34 +++++++++++++++++-------- src/platform.c | 67 +++++++++++++++++++++++-------------------------- src/platform.h | 23 +++++++++++++++-- 6 files changed, 123 insertions(+), 78 deletions(-) diff --git a/src/arena.c b/src/arena.c index 185c180..367c780 100644 --- a/src/arena.c +++ b/src/arena.c @@ -3,17 +3,21 @@ #include "platform.h" -MemoryBlock *memoryblock_allocate(size_t size) +MemoryBlock *memoryblock_allocate(size_t size, size_t commit_size) { - void *base = memory_allocate(size); - Assert(base != NULL); + size_t aligned_size = AlignToPageEnd(size); + size_t aligned_commit_size = AlignToPageEnd(commit_size); - MemoryBlock *block = (MemoryBlock *)base; - block->base = (U8 *)base; - block->size = size; - block->pos = MemoryBlock_Header_Size; - block->base_pos = 0; - block->prev = NULL; + MemoryBlock *block = memory_reserve(aligned_size); + Assert(block != NULL); + memory_commit(block, aligned_commit_size); + + block->base = (U8 *)block; + block->size = aligned_size; + block->pos = MemoryBlock_Header_Size; + block->base_pos = 0; + block->prev = NULL; + block->committed_pos = aligned_commit_size; return block; } @@ -21,15 +25,17 @@ MemoryBlock *memoryblock_allocate(size_t size) void arena_init(Arena *arena, const ArenaParams *params) { Assert(params->size > MemoryBlock_Header_Size); - Assert(params->size < (U32_Max - MemoryBlock_Header_Size - 1)); + Assert(params->size < (U32_Max - platform_page_size())); // keep last page for overflow detection + Assert(IsPow2(params->commit_size)); - MemoryBlock *block = memoryblock_allocate(params->size); + MemoryBlock *block = memoryblock_allocate(params->size, params->commit_size); ASAN_UNPOISON_MEMORY_REGION(block, MemoryBlock_Header_Size); arena->flags = params->flags; arena->checkpoint_level = 0; arena->current = block; - arena->requested_size = params->size; + arena->block_size = params->size; + arena->commit_size = params->commit_size; ASAN_POISON_MEMORY_REGION(&block->base[block->pos], memoryblock_available(block)); } @@ -47,20 +53,22 @@ void arena_free(Arena *arena) void *arena_push(Arena *arena, U32 size, U32 align) { - Assert(size < (U32_Max - align - MemoryBlock_Header_Size - 1)); + Assert(size < (U32_Max - platform_page_size())); Assert(IsPow2(align)); MemoryBlock *block = arena->current; U32 start_pos = AlignUpPow2(block->pos, align); - U32 available = memoryblock_available(block); + U32 available = block->size - start_pos; if (available < size) { - U32 new_size = arena->requested_size; // original user requeted size - if (new_size < size) { - new_size = AlignUpPow2(size + MemoryBlock_Header_Size,align); + U32 block_size = arena->block_size; // original user requested size + U32 new_commit_size = arena->commit_size; + if (size > block_size - MemoryBlock_Header_Size) { + block_size = AlignUpPow2(size + MemoryBlock_Header_Size,align); + new_commit_size = block_size; } - MemoryBlock *new_block = memoryblock_allocate(new_size); + MemoryBlock *new_block = memoryblock_allocate(block_size, new_commit_size); new_block->base_pos = block->base_pos + block->size; new_block->prev = block; arena->current = new_block; @@ -71,7 +79,15 @@ void *arena_push(Arena *arena, U32 size, U32 align) U32 end_pos = start_pos + size; Assert(end_pos <= block->size); - U8 *mem = &block->base[start_pos]; + U8 *mem = &block->base[start_pos]; + if (block->committed_pos < end_pos) { + // Commit more memory + U32 aligned_end_pos = AlignToPageEnd(start_pos + size); + + U32 aligned_size = RoundUpToMultiple(aligned_end_pos - block->committed_pos, arena->commit_size); + memory_commit(&block->base[block->committed_pos], aligned_size); + block->committed_pos += aligned_size; + } block->pos = end_pos; ASAN_UNPOISON_MEMORY_REGION(mem, size); @@ -92,9 +108,7 @@ void arena_pop_to(Arena *arena, U32 abs_pos) } else { U32 new_pos = abs_pos - block->base_pos; new_pos = Max(MemoryBlock_Header_Size, new_pos); - // Note, when doing commit/release, new_pos <= current->pos - // is the right thing . shouldn't be able to pop to larger than what's already commited - Assert(new_pos <= block->size); + Assert(new_pos <= block->committed_pos); block->pos = new_pos; ASAN_POISON_MEMORY_REGION(&block->base[block->pos], memoryblock_available(block)); break; diff --git a/src/arena.h b/src/arena.h index 8231515..cdbf6ab 100644 --- a/src/arena.h +++ b/src/arena.h @@ -7,6 +7,7 @@ typedef U32 ArenaFlags; // no flags defined yets typedef struct ArenaParams ArenaParams; struct ArenaParams { U32 size; + U32 commit_size; ArenaFlags flags; // No flags defined yet }; @@ -14,15 +15,17 @@ typedef struct MemoryBlock MemoryBlock; struct MemoryBlock { U8 *base; U32 size; - U32 base_pos; // absolute position of base relative to the first linked arena U32 pos; // start of next allocation + U32 base_pos; // absolute position of base relative to the first linked arena + U32 committed_pos; MemoryBlock *prev; }; typedef struct Arena Arena; struct Arena { MemoryBlock *current; - U32 requested_size; + U32 block_size; + U32 commit_size; ArenaFlags flags; S32 checkpoint_level; }; @@ -50,7 +53,8 @@ ArenaCheckPoint arena_temp_begin(Arena *arena); void arena_temp_end(ArenaCheckPoint *checkpoint); void arena_pop_to(Arena *arena, U32 abs_pos); void arena_reset(Arena *arena); -MemoryBlock *memoryblock_allocate(size_t size); +MemoryBlock *memoryblock_allocate(size_t size, size_t commit_size); + static INLINE U32 memoryblock_available(const MemoryBlock *block) { diff --git a/src/base_core.h b/src/base_core.h index 9205828..dd28138 100644 --- a/src/base_core.h +++ b/src/base_core.h @@ -107,6 +107,7 @@ typedef unsigned __int128 U128; #define AlignDownPow2(val, align) ((val) & ~((align) - 1UL)) #define IsPow2(x) ((x) && !((x) & ((x) - 1UL))) #define IsPow2OrZero(x) (!((x) & ((x) - 1UL))) +#define IsAlignedTo(x, y) (IsPow2(y) && !((x) & ((y) - 1UL))) #define GetBit(val, idx) (((val) >> (idx)) & 1) // Get n_bits starting at idx going left @@ -132,7 +133,7 @@ typedef unsigned __int128 U128; #endif /** - * Round up x / y + * Ceiling of X / y , or round up x / y * (e.g 100/9 == 12, 3/2 == 2) * x + y must not overflow. * @@ -140,7 +141,7 @@ typedef unsigned __int128 U128; * @param y denominator * @return x/y rounded up */ -#define DivRoundUp(x, y) (((x) + ((y) - 1)) / (y)) +#define CeilDiv(x, y) (((x) + ((y) - 1)) / (y)) /** * Round x up to nearest multiple of y @@ -151,7 +152,7 @@ typedef unsigned __int128 U128; * @param y denominator * @return x rounded up to nearest multiple of y */ -#define RoundUp(x, y) (DIV_ROUND_UP((x), (y)) * (y)) +#define RoundUpToMultiple(x, y) (CeilDiv((x), (y)) * (y)) /** * Round x down to nearest multiple of y @@ -162,7 +163,7 @@ typedef unsigned __int128 U128; * @param y denominator * @return x rounded down nearest multiple of y */ -#define RoundDown(x, y) ((x) / (y) * (y)) +#define RounDownToMultiple(x, y) ((x) / (y) * (y)) #define Clamp(val, min, max) (((val) < (min)) ? (min) : ((val) > (max)) ? (max) : (val)) #ifdef __GNUC__ diff --git a/src/main.c b/src/main.c index 83ef671..54c6623 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ void arena_dump(const Arena *arena) { + return; MemoryBlock *block = arena->current; puts("@@@@@@@@@@"); while (block) { @@ -21,7 +22,10 @@ void arena_dump(const Arena *arena) static void arena_test_1(void) { ArenaParams arena_params = {}; - arena_params.size = 1024+MemoryBlock_Header_Size; + U32 arena_size = MB(1) + MemoryBlock_Header_Size; + U32 commit_size = KB(8); + arena_params.size = arena_size; + arena_params.commit_size = commit_size; Arena arena; arena_init(&arena, &arena_params); arena_dump(&arena); @@ -43,22 +47,28 @@ static void arena_test_1(void) static void arena_test_2(void) { ArenaParams arena_params = {}; - arena_params.size = 1024+MemoryBlock_Header_Size; + U32 arena_size = MB(1) + MemoryBlock_Header_Size; + U32 commit_size = KB(16); + U32 push_size = KB(32); + arena_params.size = arena_size; + arena_params.commit_size = commit_size; Arena arena; arena_init(&arena, &arena_params); void *ptr; - ptr = arena_push(&arena, 1024, 8); - MemoryZero(ptr, 1024); + ptr = arena_push(&arena, push_size, 8); + MemoryZero(ptr, push_size); printf("Allocated ptr %p\n", ptr); arena_dump(&arena); ArenaCheckPoint checkpoint = arena_temp_begin(&arena); - ptr = arena_push(&arena, 4096, 8); - MemoryZero(ptr, 4096); + ptr = arena_push(&arena, MB(2), 8); + MemoryZero(ptr, MB(2)); - ptr = arena_push(&arena, 4096, 8); - MemoryZero(ptr, 4096); + ptr = arena_push(&arena, commit_size * 3, 8); + MemoryZero(ptr, commit_size * 3 ); + ptr = arena_push(&arena, commit_size , 8); + MemoryZero(ptr, commit_size ); ArenaCheckPoint checkpoint2 = arena_temp_begin(&arena); ptr = arena_push(&arena, 64, 8); @@ -69,9 +79,11 @@ static void arena_test_2(void) arena_temp_end(&checkpoint); arena_pop_to(&arena, 1088); arena_dump(&arena); - ptr = arena_push(&arena, 1, 1); + ptr = arena_push(&arena, 1, KB(16)); ((U8*)ptr)[0] = 0x37; - ptr = arena_push(&arena, 1, 1); + ptr = arena_push(&arena, 1, KB(16)); + ((U8*)ptr)[0] = 0x73; + ptr = arena_push(&arena, 1, KB(4)); ((U8*)ptr)[0] = 0x73; arena_pop_to(&arena, 1089); arena_dump(&arena); @@ -82,6 +94,6 @@ static void arena_test_2(void) int main(int argc, char *argv[]) { - arena_test_1(); + //arena_test_1(); arena_test_2(); } diff --git a/src/platform.c b/src/platform.c index aadb494..f8198db 100644 --- a/src/platform.c +++ b/src/platform.c @@ -1,30 +1,18 @@ #include "base_core.h" +#include "platform.h" #include #include #include #include +#define IsPageAligned(x) (IsAlignedTo(x, platform_page_size())) + U32 platform_page_size(void) { return getpagesize(); } -static INLINE size_t AlignToPageSize(size_t size) -{ - U32 page_size = platform_page_size(); - size_t aligned_size = AlignUpPow2(size, page_size); - - return aligned_size; -} - -static INLINE size_t AlignToPageStart(size_t size) -{ - U32 page_size = platform_page_size(); - size_t aligned_size = AlignDownPow2(size, page_size); - - return aligned_size; -} void *platform_malloc(size_t size) { @@ -39,13 +27,10 @@ void platform_free(void *mem) void *memory_allocate(size_t size) { - if (size == 0) { - size = 1; - } + Assert(IsPow2(size)); - size_t aligned_size = AlignToPageSize(size); - void *mem = mmap(0, aligned_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - TRACEF("memory_allocate size=%zu aligned_size=%zu mem=%p\n", size, aligned_size, mem); + void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + TRACEF("memory_allocate size=%zu mem=%p\n", size, mem); if (mem == MAP_FAILED) { mem = NULL; } @@ -54,36 +39,46 @@ void *memory_allocate(size_t size) void memory_free(void *mem, size_t size) { - size_t aligned_size = AlignToPageSize(size); - TRACEF(" memory_free size=%zu aligned_size=%zu mem=%p\n", size, aligned_size, mem); - - int rc = munmap(mem, aligned_size); + TRACEF(" memory_free size=%zu mem=%p\n", size, mem); + Assert(IsPageAligned((uintptr_t)mem)); + Assert(IsPageAligned(size)); + int rc = munmap(mem, size); Assert(rc == 0); + +} +void *memory_reserve(size_t size) +{ + Assert(IsPageAligned(size)); + void *mem = mmap(0, size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + TRACEF("memory_reserve size=%zu mem=%p\n", size, mem); + if (mem == MAP_FAILED) { + mem = NULL; + } + return mem; } void memory_commit(void *mem, size_t size) { - size_t aligned_size = AlignToPageSize(size); - void *aligned_mem = (void *)AlignToPageStart((uintptr_t)mem); - TRACEF("memory_commit size=%zu aligned_size=%zu ptr=%p aligned_mem=%p\n", size, aligned_size, mem, aligned_mem); + Assert(IsPageAligned((uintptr_t)mem)); + Assert(IsPageAligned(size)); + TRACEF("memory_commit size=%zu mem=%p\n", size, mem); int rc; - rc = mprotect(aligned_mem, aligned_size, PROT_READ | PROT_WRITE); + rc = mprotect(mem, size, PROT_READ | PROT_WRITE); Assert(rc == 0); - rc = madvise(aligned_mem, aligned_size, MADV_WILLNEED); + rc = madvise(mem, size, MADV_WILLNEED); Assert(rc == 0); } void memory_decommit(void *mem, size_t size) { - size_t aligned_size = AlignToPageSize(size); - void *aligned_mem = (void *)AlignToPageStart((uintptr_t)mem); - TRACEF("memory_commit size=%zu aligned_size=%zu ptr=%p aligned_mem=%p\n", size, aligned_size, mem, aligned_mem); - + TRACEF("memory_decommit size=%zu mem=%p\n", size, mem); + Assert(IsPageAligned((uintptr_t)mem)); + Assert(IsPageAligned(size)); int rc; - rc = mprotect(aligned_mem, aligned_size, PROT_NONE); + rc = mprotect(mem, size, PROT_NONE); Assert(rc == 0); - rc = madvise(aligned_mem, aligned_size, MADV_DONTNEED); + rc = madvise(mem, size, MADV_DONTNEED); Assert(rc == 0); } diff --git a/src/platform.h b/src/platform.h index 574ca04..0c18d06 100644 --- a/src/platform.h +++ b/src/platform.h @@ -2,9 +2,28 @@ #include void *platform_malloc(size_t size); -void platform_free(void *mem); U32 platform_page_size(void); void *memory_allocate(size_t size); -void *memory_free(void *mem, size_t size); +void *memory_reserve(size_t size); +void memory_commit(void *mem, size_t size); +void memory_decommit(void *mem, size_t size); +void memory_free(void *mem, size_t size); + + +static INLINE size_t AlignToPageEnd(size_t size) +{ + U32 page_size = platform_page_size(); + size_t aligned_size = AlignUpPow2(size, page_size); + + return aligned_size; +} + +static INLINE size_t AlignToPageStart(size_t size) +{ + U32 page_size = platform_page_size(); + size_t aligned_size = AlignDownPow2(size, page_size); + + return aligned_size; +}