frying my brain coming up with a decent compromise on arena usage...

This commit is contained in:
2025-02-05 15:46:58 -05:00
parent 268271e4a7
commit a41bd8a5f1
7 changed files with 231 additions and 230 deletions
+23 -122
View File
@@ -11,134 +11,35 @@
//- rjf: arena creation/destruction
#if 0
internal Arena*
arena_alloc_(ArenaParams *params)
{
// rjf: round up reserve/commit sizes
U64 reserve_size = params->reserve_size;
U64 commit_size = params->commit_size;
if(params->flags & ArenaFlag_LargePages)
{
reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size);
}
else
{
reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->page_size);
}
// rjf: reserve/commit initial block
void *base = params->optional_backing_buffer;
if(base == 0)
{
if(params->flags & ArenaFlag_LargePages)
{
base = os_reserve_large(reserve_size);
os_commit_large(base, commit_size);
}
else
{
base = os_reserve(reserve_size);
os_commit(base, commit_size);
}
}
// rjf: panic on arena creation failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(base == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
// rjf: extract arena header & fill
Arena *arena = (Arena *)base;
arena->current = arena;
arena->flags = params->flags;
arena->cmt_size = (U32)params->commit_size;
arena->res_size = params->reserve_size;
arena->base_pos = 0;
arena->pos = ARENA_HEADER_SIZE;
arena->cmt = commit_size;
arena->res = reserve_size;
AsanPoisonMemoryRegion(base, commit_size);
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
return arena;
}
#endif
Arena*
arena_alloc_(ArenaParams* params)
{
// rjf: round up reserve/commit sizes
U64 reserve_size = params->reserve_size;
U64 commit_size = params->commit_size;
B32 is_virtual = false;
// This composite arena has the ability to decide a default behavior for allocation
if(params->flags & ArenaFlag_LargePages)
{
reserve_size = align_pow2(reserve_size, os_get_system_info()->large_page_size);
commit_size = align_pow2(commit_size, os_get_system_info()->large_page_size);
}
else
{
reserve_size = align_pow2(reserve_size, os_get_system_info()->page_size);
commit_size = align_pow2(commit_size, os_get_system_info()->page_size);
}
void* base = alloc(params->backing, params->block_size);
// rjf: reserve/commit initial block
void *base = params->optional_backing_buffer;
if(base == 0)
{
if(params->flags & ArenaFlag_LargePages)
{
base = os_reserve_large(reserve_size);
os_commit_large(base, commit_size);
}
else
{
base = os_reserve(reserve_size);
os_commit(base, commit_size);
}
}
// rjf: panic on arena creation failure
// rjf: panic on arena creation failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(base == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
if(unlikely(base == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
// rjf: extract arena header & fill
Arena *arena = (Arena *)base;
arena->current = arena;
arena->flags = params->flags;
arena->cmt_size = (U32)params->commit_size;
arena->res_size = params->reserve_size;
arena->base_pos = 0;
arena->pos = ARENA_HEADER_SIZE;
arena->cmt = commit_size;
arena->res = reserve_size;
AsanPoisonMemoryRegion(base, commit_size);
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
return arena;
}
// rjf: extract arena header & fill
Arena* arena = (Arena*) base;
arena->current = arena;
arena->backing = params->backing;
arena->pos = size_of(Arena);
arena->block_size = params->block_size;
arena->flags = params->flags;
// Note(Ed): INLINED
// internal void
// arena_release(Arena *arena)
// {
// for(Arena *n = arena->current, *prev = 0; n != 0; n = prev)
// {
// prev = n->prev;
// os_release(n, n->res);
// }
// }
asan_unpoison_memory_region(base, sizeof(Arena));
return arena;
}
//- rjf: arena push/pop core functions
@@ -247,16 +148,16 @@ arena_pop(Arena *arena, U64 amt)
//- rjf: temporary arena scopes
internal Temp
internal TempArena
temp_begin(Arena *arena)
{
U64 pos = arena_pos(arena);
Temp temp = {arena, pos};
U64 pos = arena_pos(arena);
TempArena temp = {arena, pos};
return temp;
}
internal void
temp_end(Temp temp)
temp_end(TempArena temp)
{
arena_pop_to(temp.arena, temp.pos);
}
+14 -19
View File
@@ -19,17 +19,19 @@
typedef U32 ArenaFlags;
enum
{
ArenaFlag_NoChain = ( 1 << 0),
ArenaFlag_LargePages = ( 1 << 1),
// Don't chain this arena
ArenaFlag_NoChain = (1 << 0),
// Only works if backing is virtual memory, will allocate a new backing VArena when the current block exhausts
// Otherwise will assume backing can chain multiple block_size arenas until failure.
ArenaFlag_ChainVirtual = (1 << 1),
};
typedef struct ArenaParams ArenaParams;
struct ArenaParams
{
ArenaFlags flags;
U64 reserve_size;
U64 commit_size;
void* optional_backing_buffer;
AllocatorInfo backing;
ArenaFlags flags;
U64 block_size; // If chaining VArenas set this to the reserve size
};
/* NOTE(Ed): This is a combination of several concepts into a single interface:
@@ -44,18 +46,14 @@ struct ArenaParams
typedef struct Arena Arena;
struct Arena
{
AllocatorInfo backing;
Arena* prev; // previous arena in chain
Arena* current; // current arena in chain
ArenaFlags flags;
U32 cmt_size;
U64 res_size;
U64 base_pos;
AllocatorInfo backing;
U64 pos;
U64 cmt;
U64 res;
U64 block_size;
ArenaFlags flags;
};
static_assert(sizeof(Arena) <= ARENA_HEADER_SIZE, "sizeof(Arena) <= MD_ARENA_HEADER_SIZE");
static_assert(size_of(Arena) <= ARENA_HEADER_SIZE, "sizeof(Arena) <= ARENA_HEADER_SIZE");
typedef struct TempArena TempArena;
struct TempArena
@@ -67,12 +65,12 @@ struct TempArena
////////////////////////////////
//~ rjf: Arena Functions
MD_API void* arena_allocator_proc(void* allocator_data, AllocType type, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags);
MD_API void* arena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags);
//- rjf: arena creation/destruction
MD_API Arena* arena_alloc_(ArenaParams *params);
#define arena_alloc(...) arena_alloc_( & ( ArenaParams) { .reserve_size = MB(64), .commit_size = KB(64), __VA_ARGS__ } )
#define arena_alloc(...) arena_alloc_( & ( ArenaParams) { .backing = {}, .reserve_size = MB(64), .commit_size = KB(64), __VA_ARGS__ } )
void arena_release(Arena *arena);
@@ -109,9 +107,6 @@ arena_release(Arena* arena)
for (Arena* n = arena->current, *prev = 0; n != 0; n = prev)
{
prev = n->prev;
// os_release(n, n->res);
alloc_free(arena->backing, n);
}
}
+7 -6
View File
@@ -4,6 +4,7 @@
# include "linkage.h"
# include "macros.h"
# include "platform.h"
# include "base_types.h"
#endif
@@ -200,19 +201,19 @@ void* mem_move( void* destination, void const* source, SSIZE byte_count )
{
if ( to_uptr(src_ptr) % size_of( SSIZE ) == to_uptr(dest_ptr) % size_of( SSIZE ) )
{
while ( pcast( UPTR, dest_ptr) % size_of( ssize ) )
while ( pcast( UPTR, dest_ptr) % size_of( SSIZE ) )
{
if ( ! byte_count-- )
return destination;
*dest_ptr++ = *src_ptr++;
}
while ( byte_count >= size_of( ssize ) )
while ( byte_count >= size_of( SSIZE ) )
{
* rcast(SSIZE*, dest_ptr) = * rcast(SSIZE const*, src_ptr);
byte_count -= size_of( ssize );
dest_ptr += size_of( ssize );
src_ptr += size_of( ssize );
byte_count -= size_of( SSIZE );
dest_ptr += size_of( SSIZE );
src_ptr += size_of( SSIZE );
}
}
for ( ; byte_count; byte_count-- )
@@ -301,7 +302,7 @@ void* mem_set( void* destination, U8 fill_byte, SSIZE byte_count )
byte_count -= align_offset;
{
u64 fill_doubleword = ( scast( U64, fill_word) << 32 ) | fill_word;
U64 fill_doubleword = ( scast( U64, fill_word) << 32 ) | fill_word;
while ( byte_count > 31 )
{
* rcast( U64*, dest_ptr + 0 ) = fill_doubleword;
+98 -51
View File
@@ -4,6 +4,7 @@
# include "../os/os.h"
#endif
#ifdef MD_HEAP_ANALYSIS
#define GEN_HEAP_STATS_MAGIC 0xDEADC0DE
typedef struct _heap_stats _heap_stats;
@@ -47,8 +48,9 @@ struct _heap_alloc_info
SSIZE size;
void* physical_start;
};
#endif
void* heap_allocator_proc( void* allocator_data, AllocType type, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags )
void* heap_allocator_proc( void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags )
{
void* ptr = nullptr;
// unused( allocator_data );
@@ -62,7 +64,7 @@ void* heap_allocator_proc( void* allocator_data, AllocType type, SSIZE size, SSI
ssize track_size = max( alloc_info_size, alignment ) + alloc_info_remainder;
switch ( type )
{
case EAllocation_FREE :
case EAllocatorMode_FREE :
{
if ( ! old_memory )
break;
@@ -72,7 +74,7 @@ void* heap_allocator_proc( void* allocator_data, AllocType type, SSIZE size, SSI
old_memory = alloc_info->physical_start;
}
break;
case EAllocation_ALLOC :
case EAllocatorMode_ALLOC :
{
size += track_size;
}
@@ -82,76 +84,83 @@ void* heap_allocator_proc( void* allocator_data, AllocType type, SSIZE size, SSI
}
#endif
switch ( type )
switch ( mode )
{
#if defined( COMPILER_MSVC ) || ( defined( COMPILER_GCC ) && defined( OS_WINDOWS ) ) || ( defined( COMPILER_TINYC ) && defined( OS_WINDOWS ) )
case EAllocType_ALLOC :
#if defined( COMPILER_MSVC ) || ( defined( COMPILER_GCC ) && defined( OS_WINDOWS ) ) || ( defined( COMPILER_TINYC ) && defined( OS_WINDOWS ) )
case AllocatorMode_ALLOC:
{
ptr = _aligned_malloc( size, alignment );
if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO )
zero_size( ptr, size );
break;
case EAllocType_FREE :
}
break;
case AllocatorMode_FREE:
_aligned_free( old_memory );
break;
case EAllocType_RESIZE :
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
break;
#elif defined( OS_LINUX ) && ! defined( CPU_ARM ) && ! defined( COMPILER_TINYC )
case AllocatorMode_FREE_ALL:
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
#elif defined( OS_LINUX ) && ! defined( CPU_ARM ) && ! defined( COMPILER_TINYC )
case EAllocation_ALLOC :
{
ptr = aligned_alloc( alignment, ( size + alignment - 1 ) & ~( alignment - 1 ) );
{
ptr = aligned_alloc( alignment, ( size + alignment - 1 ) & ~( alignment - 1 ) );
if ( flags & GEN_ALLOCATOR_FLAG_CLEAR_TO_ZERO )
{
zero_size( ptr, size );
}
if ( flags & GEN_ALLOCATOR_FLAG_CLEAR_TO_ZERO )
{
zero_size( ptr, size );
}
break;
}
break;
case EAllocation_FREE :
{
free( old_memory );
}
break;
{
free( old_memory );
}
break;
case EAllocation_RESIZE :
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
#else
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
#else
case EAllocType_ALLOC :
{
posix_memalign( &ptr, alignment, size );
{
posix_memalign( &ptr, alignment, size );
if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO )
{
zero_size( ptr, size );
}
if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO )
{
zero_size( ptr, size );
}
break;
}
break;
case EAllocType_FREE :
{
free( old_memory );
}
break;
{
free( old_memory );
}
break;
case EAllocType_RESIZE :
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
#endif
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
#endif
case EAllocType_FREE_ALL :
case AllocatorMode_FREE_ALL:
break;
case AllocatorMode_QueryType:
return (void*) Heap
}
#ifdef GEN_HEAP_ANALYSIS
@@ -172,6 +181,44 @@ void* heap_allocator_proc( void* allocator_data, AllocType type, SSIZE size, SSI
void* vm_allocator_proc(void* allocator_data, AllocType type, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags)
{
void* result = nullptr;
// if(params->flags & ArenaFlag_LargePages)
// {
// base = os_reserve_large(reserve_size);
// os_commit_large(base, commit_size);
// }
// else
// {
// base = os_reserve(reserve_size);
// os_commit(base, commit_size);
// }
return result;
}
VMemory vm_alloc(VMemoryParams params)
{
// rjf: round up reserve/commit sizes
U64 reserve_size = params->reserve_size;
U64 commit_size = params->commit_size;
if(params->flags & ArenaFlag_LargePages)
{
reserve_size = align_pow2(reserve_size, os_get_system_info()->large_page_size);
commit_size = align_pow2(commit_size, os_get_system_info()->large_page_size);
}
else
{
reserve_size = align_pow2(reserve_size, os_get_system_info()->page_size);
commit_size = align_pow2(commit_size, os_get_system_info()->page_size);
}
// Allocate virtual memory
is_virtual = true;
VMemory vm = ;
// TODO(Ed): Move this to vmem?
asan_poison_memory_region(base, params->commit_size);
}
+85 -30
View File
@@ -19,18 +19,29 @@
#define MD__HAS_ZERO( x ) ( ( ( x ) - MD__ONES ) & ~( x ) & MD__HIGHS )
#endif
typedef U32 AllocType;
enum AllocType enum_underlying(U32)
// Return value of allocator_type
typedef U32 AllocatorType;
enum
{
EAllocType_ALLOC,
EAllocType_FREE,
EAllocType_FREE_ALL,
EAllocType_RESIZE,
AllocatorType_Heap = 0, // Genreal heap allocator
AllocatorType_VArena = 1, // Arena allocator backed by virtual address space
AllocatorType_FArena = 2, // Fixed arena backed back by a fixed size block of memory (usually a byte slice)
AllocatorType_Arena = 3, // Composite arena used originally by RAD Debugger & Metadesk
};
typedef void*(AllocatorProc)( void* allocator_data, AllocType type, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags );
typedef U32 AllocatorMode;
enum AllocatorMode enum_underlying(U32)
{
AllocatorMode_ALLOC,
AllocatorMode_FREE,
AllocatorMode_FREE_ALL,
AllocatorMode_RESIZE,
AllocatorMode_QueryType,
};
typedef struct AllocatorInfo;
typedef void*(AllocatorProc)( void* allocator_data, AllocatorMode type, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags );
typedef struct AllocatorInfo AllocatorInfo;
struct AllocatorInfo
{
AllocatorProc* proc;
@@ -50,6 +61,9 @@ enum AllocFlag
# define MD_DEFAULT_ALLOCATOR_FLAGS ( ALLOCATOR_FLAG_CLEAR_TO_ZERO )
#endif
// Allows the user to retrieve which type of allocator is being used.
AllocatorType allocator_type(AllocatorInfo a);
//! Allocate memory with default alignment.
void* alloc( AllocatorInfo a, SSIZE size );
@@ -83,6 +97,7 @@ void* resize_align( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size,
//! Use this if you don't need a "fancy" resize allocation
void* default_resize_align( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size, SSIZE alignment );
#ifdef MD_HEAP_ANALYSIS
/* heap memory analysis tools */
/* define GEN_HEAP_ANALYSIS to enable this feature */
/* call zpl_heap_stats_init at the beginning of the entry point */
@@ -91,8 +106,9 @@ MD_API void heap_stats_init( void );
MD_API SSIZE heap_stats_used_memory( void );
MD_API SSIZE heap_stats_alloc_count( void );
MD_API void heap_stats_check( void );
#endif
MD_API void* heap_allocator_proc( void* allocator_data, AllocType type, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags );
MD_API void* heap_allocator_proc( void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags );
#ifndef heap
//! The heap allocator backed by operating system's memory manager.
@@ -109,44 +125,83 @@ MD_API void* heap_allocator_proc( void* allocator_data, AllocType type, SSIZE si
#define mfree( ptr ) free( heap(), ptr )
#endif
typedef U32 VMemoryFlags;
/* Virtual Memory Arena
This is separate from the composite arena used by HMH/Casey Muratori/RJF
This arena stricly manages one reservation of the process's virtual address space.
Segregating this from composite Arena style causes more moremoy to be used for "allocator headers", however it allows
users of a library to have greater control over the allocation strategy used from their side instead of the library itself.
Like with the composite Arena, the VArena has its struct as the header of the reserve of memory.
*/
typedef U32 VArenaFlags;
enum
{
VMemoryFlag_NoChain = (1 << 0),
VMemoryFlag_LargePages = (1 << 1),
VArenaFlag_LargePages = (1 << 0),
};
typedef struct VMemory_Params;
struct VMemory_Params
typedef struct VArenaParams VArenaParams;
struct VArenaParams
{
VMemoryFlags flags;
U64 reserve_size;
U64 commit_size;
// void* optional_backing_buffer;
U64 base_addr;
VArenaFlags flags;
U64 reserve_size;
U64 commit_size;
};
typedef struct VMemory;
struct VMemory
typedef struct VArena VArena;
struct VArena
{
U32 cmt_size;
U32 res_size;
VArenaFlags flags;
U64 base_pos;
U64 cmt;
U64 res;
U64 res_size;
U64 cmt_size;
};
AllocatorInfo vm_allocator(VMemory* vm) {
AllocatorInfo info = { vm_allocator_proc, vm }
return info
AllocatorInfo vm_allocator(VArena* vm) {
AllocatorInfo info = { vm_allocator_proc, vm };
return info;
}
MD_API void* vm_allocator_proc(void* allocator_data, AllocType type, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags);
VArena* varena_alloc (VArenaParams params);
void varena_commit (VArena vm, SSIZE commit_size);
VArenaParams varena_free (VArena vm);
SSIZE varena_page_size(SSIZE* alignment_out);
MD_API void* varena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags);
typedef struct ByteSlice ByteSlice;
struct ByteSlice
{
U8* data;
SSIZE len;
};
// Fixed size arena
typedef struct FArena FArena;
struct FArena
{
ByteSlice slice;
SSIZE pos;
};
FArena farena_from_byteslice(ByteSlice slice);
MD_API void* farena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags);
// Inlines
inline
AllocatorType allocator_type(AllocatorInfo a) {
return (AllocatorType) a.proc(a.data, AllocatorMode_QueryType, 0, 0, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS);
}
inline
void* alloc_align( AllocatorInfo a, SSIZE size, SSIZE alignment ) {
return a.Proc( a.Data, EAllocType_ALLOC, size, alignment, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
return a.proc( a.data, AllocatorMode_ALLOC, size, alignment, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
}
inline
@@ -157,12 +212,12 @@ void* alloc( AllocatorInfo a, SSIZE size ) {
inline
void allocator_free( AllocatorInfo a, void* ptr ) {
if ( ptr != nullptr )
a.Proc( a.Data, EAllocType_FREE, 0, 0, ptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
a.proc( a.data, AllocatorMode_FREE, 0, 0, ptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
}
inline
void free_all( AllocatorInfo a ) {
a.Proc( a.Data, EAllocType_FREE_ALL, 0, 0, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
a.proc( a.data, AllocatorMode_FREE_ALL, 0, 0, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
}
inline
@@ -172,7 +227,7 @@ void* resize( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size ) {
inline
void* resize_align( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size, SSIZE alignment ) {
return a.Proc( a.Data, EAllocType_RESIZE, new_size, alignment, ptr, old_size, MD_DEFAULT_ALLOCATOR_FLAGS );
return a.proc( a.data, AllocatorMode_RESIZE, new_size, alignment, ptr, old_size, MD_DEFAULT_ALLOCATOR_FLAGS );
}
inline
-1
View File
@@ -36,4 +36,3 @@ MD_NS_END
// metagen