From d0953c883db4e803422a80ead01dd2942b11a720 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Sun, 25 Jul 2021 15:28:50 -0700 Subject: [PATCH] arena experiment; switch from function pointer overrides to macro overrides --- arena_findings.txt | 36 +++++++++++ project.4coder | 1 + source/md.c | 153 ++++++++++++++++++++++----------------------- source/md.h | 23 +------ 4 files changed, 112 insertions(+), 101 deletions(-) create mode 100644 arena_findings.txt diff --git a/arena_findings.txt b/arena_findings.txt new file mode 100644 index 0000000..10dcafd --- /dev/null +++ b/arena_findings.txt @@ -0,0 +1,36 @@ +1. Avoid function pointer based overrides, prefer macro based overrides + Macros allow you to define one override per compilation context, but + that one override has better range, than the kinds of overrides you + can do with function pointers. You can always use macros to plug in + a dynamic dispatch system, but you can't use function pointers to + plug in high speed light weight systems. + [ This same idea should for thread contexts ] + +2. It's starting to seem like the header technique is the way to go. + It solves a couple of problems for the library, which won't know + the size of the arena struct when it is written. And there are good + enough bridge techniques for libraries that have non-header arenas, + to get up and running, and fixing the remaining issues or fully + transitioning to header technique shouldn't be too hard. + +3. It seems like it will be better to count on pre-briding at least + the types and default controls (before the header) so that we can + get type checking on various arena and array of arenas parameters. + [ This idea was less fully explored, it may still turn out to be + better to stick with post-bridging ]. + +[ ] Could there be an easy bridge from a malloc/free style allocator + in the codebase to an arena style in the library? + +[ ] Figure out the header technique problem: + Arena min position to implement clear? + Init instead of New, pass in memory for thread context? + +[ ] Test integrating two libraries into the same codebase + +[ ] Thread Context/Get Scratch cooperative mode + +[ ] What if only the slow path ever triggered a function call? + And we put everything in the common path into macros? + +[ ] MD_ArenaPushAlignDefault(arena->pos, arena->align, arena->cap); diff --git a/project.4coder b/project.4coder index cef639b..7a6e7a5 100644 --- a/project.4coder +++ b/project.4coder @@ -54,3 +54,4 @@ command_list = fkey_command[1] = "build"; fkey_command[2] = "build_with_clang"; + diff --git a/source/md.c b/source/md.c index 0a22c86..3fd00ce 100644 --- a/source/md.c +++ b/source/md.c @@ -8,8 +8,17 @@ ** #define MD_IMPL_Commit ** #define MD_IMPL_Decommit ** #define MD_IMPL_Release -** #define MD_IMPL_ArenaNew -** #define MD_IMPL_ArenaRelease +** +** #define MD_IMPL_Arena +** #define MD_IMPL_ArenaNew (MD_u64) -> MD_IMPL_Arena* +** #define MD_IMPL_ArenaRelease (MD_IMPL_Arena*) -> void +** #define MD_IMPL_ArenaGetPos (MD_IMPL_Arena*) -> MD_u64 +** #define MD_IMPL_ArenaGetCap (MD_IMPL_Arena*) -> MD_u64 +** #define MD_IMPL_ArenaPush (MD_IMPL_Arena*, MD_u64) -> void* +** #define MD_IMPL_ArenaPopTo (MD_IMPL_Arena*, MD_u64) -> void +** #define MD_IMPL_ArenaPushAlign (MD_IMPL_Arena*, MD_u64) -> void +** #define MD_IMPL_ArenaSetAutoAlign (MD_IMPL_Arena*, MD_u64) -> void +** #define MD_IMPL_ArenaMinPos (MD_IMPL_Arena*) -> MD_u64 ** ** #define MD_SCRATCH_SIZE ** @@ -260,7 +269,6 @@ MD_LINUX_FileIterIncrement(MD_Arena *arena, MD_FileIter *opaque_it, MD_String8 p typedef struct MD_ArenaDefault MD_ArenaDefault; struct MD_ArenaDefault{ - MD_ArenaFunc *func; MD_u64 pos; MD_u64 cmt; MD_u64 cap; @@ -268,70 +276,16 @@ struct MD_ArenaDefault{ }; MD_StaticAssert(sizeof(MD_ArenaDefault) <= MD_ArenaDefault_HeaderSize, arena_def_size_check); -#define MD_IMPL_ArenaNew MD_ArenaDefaultNew -#define MD_IMPL_ArenaRelease MD_ArenaDefaultRelease - -static MD_IntPtr -MD_ArenaDefaultImpl(MD_Arena *arena_opq, MD_ArenaOperation op, MD_u64 v){ - MD_ArenaDefault *arena = (MD_ArenaDefault*)arena_opq; - MD_u8 *buf = (MD_u8*)arena_opq; - MD_IntPtr result = MD_ZERO_STRUCT; - switch (op){ - case MD_ArenaOperation_GetPos: - { - result.u64 = arena->pos; - }break; - - case MD_ArenaOperation_GetCap: - { - result.u64 = arena->cap; - }break; - - case MD_ArenaOperation_Push: - { - if (arena->pos + v <= arena->cap){ - MD_u64 pos = arena->pos; - MD_u64 pos_clamped = ((pos > MD_ArenaDefault_HeaderSize) ? pos : MD_ArenaDefault_HeaderSize); - MD_u64 new_pos = pos_clamped + v; - MD_u64 align_m1 = arena->align - 1; - MD_u64 new_pos_aligned = (new_pos + align_m1)&(~align_m1); - MD_u64 new_pos_clamped = ((arena->cap < new_pos_aligned) ? arena->cap : new_pos_aligned); - result.ptr = buf + pos; - arena->pos = new_pos_aligned; - - if (new_pos_clamped > arena->cmt){ - MD_u64 cmt_amt_raw = new_pos_clamped - arena->cmt; - MD_u64 cmt_align_m1 = MD_ArenaDefault_CommitSize - 1; - MD_u64 cmt_amt = (cmt_amt_raw + cmt_align_m1)&(~cmt_align_m1); - MD_IMPL_Commit(buf + arena->cmt, cmt_amt); - arena->cmt += cmt_amt; - } - } - }break; - - case MD_ArenaOperation_PopTo: - { - MD_u64 pos_clamped = ((v > MD_ArenaDefault_HeaderSize) ? v : MD_ArenaDefault_HeaderSize); - arena->pos = pos_clamped; - }break; - - case MD_ArenaOperation_PushAlign: - { - MD_u64 pos = arena->pos; - MD_u64 pos_clamped = ((pos > MD_ArenaDefault_HeaderSize) ? pos : MD_ArenaDefault_HeaderSize); - MD_u64 align_m1 = arena->align - 1; - MD_u64 new_pos_aligned = (pos_clamped + align_m1)&(~align_m1); - MD_u64 new_pos_clamped = ((arena->cap < new_pos_aligned) ? arena->cap : new_pos_aligned); - arena->pos = new_pos_clamped; - }break; - - case MD_ArenaOperation_SetAutoAlign: - { - arena->align = v; - }break; - } - return(result); -} +#define MD_IMPL_Arena MD_ArenaDefault +#define MD_IMPL_ArenaNew MD_ArenaDefaultNew +#define MD_IMPL_ArenaRelease MD_ArenaDefaultRelease +#define MD_IMPL_ArenaGetPos(a) ((a)->pos) +#define MD_IMPL_ArenaGetCap(a) ((a)->cap) +#define MD_IMPL_ArenaPush MD_ArenaDefaultPush +#define MD_IMPL_ArenaPopTo MD_ArenaDefaultPopTo +#define MD_IMPL_ArenaPushAlign MD_ArenaDefaultPushAlign +#define MD_IMPL_ArenaSetAutoAlign(a,b) ((a)->align = (b)) +#define MD_IMPL_ArenaMinPos(a) MD_ArenaDefault_HeaderSize static MD_Arena* MD_ArenaDefaultNew(MD_u64 cap){ @@ -340,7 +294,6 @@ MD_ArenaDefaultNew(MD_u64 cap){ MD_IMPL_Commit(mem, cmt); MD_ArenaDefault *arena = (MD_ArenaDefault*)mem; - arena->func = MD_ArenaDefaultImpl; arena->pos = MD_ArenaDefault_HeaderSize; arena->cmt = cmt; arena->cap = cap; @@ -355,12 +308,54 @@ MD_ArenaDefaultRelease(MD_Arena *arena_opq){ MD_IMPL_Release(arena, cap); } +static void* +MD_ArenaDefaultPush(MD_ArenaDefault *arena, MD_u64 size){ + void *result = 0; + MD_u8 *buf = (MD_u8*)arena; + if (arena->pos + size <= arena->cap){ + MD_u64 pos = arena->pos; + MD_u64 pos_clamped = ((pos > MD_ArenaDefault_HeaderSize) ? pos : MD_ArenaDefault_HeaderSize); + MD_u64 new_pos = pos_clamped + size; + MD_u64 align_m1 = arena->align - 1; + MD_u64 new_pos_aligned = (new_pos + align_m1)&(~align_m1); + MD_u64 new_pos_clamped = ((arena->cap < new_pos_aligned) ? arena->cap : new_pos_aligned); + result = buf + pos; + arena->pos = new_pos_aligned; + + if (new_pos_clamped > arena->cmt){ + MD_u64 cmt_amt_raw = new_pos_clamped - arena->cmt; + MD_u64 cmt_align_m1 = MD_ArenaDefault_CommitSize - 1; + MD_u64 cmt_amt = (cmt_amt_raw + cmt_align_m1)&(~cmt_align_m1); + MD_IMPL_Commit(buf + arena->cmt, cmt_amt); + arena->cmt += cmt_amt; + } + } + return(result); +} + +static void +MD_ArenaDefaultPopTo(MD_ArenaDefault *arena, MD_u64 pos){ + MD_u64 pos_clamped = ((pos > MD_ArenaDefault_HeaderSize) ? pos : MD_ArenaDefault_HeaderSize); + arena->pos = pos_clamped; +} + +static void +MD_ArenaDefaultPushAlign(MD_ArenaDefault *arena, MD_u64 boundary){ + MD_u64 pos = arena->pos; + MD_u64 pos_clamped = ((pos > MD_ArenaDefault_HeaderSize) ? pos : MD_ArenaDefault_HeaderSize); + MD_u64 align_m1 = boundary - 1; + MD_u64 new_pos_aligned = (pos_clamped + align_m1)&(~align_m1); + MD_u64 new_pos_clamped = ((arena->cap < new_pos_aligned) ? arena->cap : new_pos_aligned); + arena->pos = new_pos_clamped; +} + #endif //~///////////////////////////////////////////////////////////////////////////// //////////////////////// MD Library Implementation ///////////////////////////// //////////////////////////////////////////////////////////////////////////////// +// TODO(allen): update these #if !defined(MD_IMPL_ArenaNew) # error Missing implementation for MD_IMPL_ArenaNew #endif @@ -417,48 +412,48 @@ MD_MemoryCopy(void *dest, void *src, MD_u64 size) //~ Arena Functions MD_FUNCTION_IMPL void* -MD_ArenaPush(MD_Arena *arena, MD_u64 v){ - MD_IntPtr result = arena->func(arena, MD_ArenaOperation_Push, v); - return(result.ptr); +MD_ArenaPush(MD_Arena *arena, MD_u64 size){ + void *result = MD_IMPL_ArenaPush((MD_IMPL_Arena*)arena, size); + return(result); } MD_FUNCTION_IMPL MD_ArenaTemp MD_ArenaBeginTemp(MD_Arena *arena){ - MD_IntPtr pos = arena->func(arena, MD_ArenaOperation_GetPos, 0); MD_ArenaTemp result = MD_ZERO_STRUCT; result.arena = arena; - result.pos = pos.u64; + result.pos = MD_IMPL_ArenaGetPos((MD_IMPL_Arena*)arena); return(result); } MD_FUNCTION_IMPL void MD_ArenaEndTemp(MD_ArenaTemp temp){ - temp.arena->func(temp.arena, MD_ArenaOperation_PopTo, temp.pos); + MD_IMPL_ArenaPopTo((MD_IMPL_Arena*)temp.arena, temp.pos); } MD_FUNCTION_IMPL void MD_ArenaSetAlign(MD_Arena *arena, MD_u64 v){ - arena->func(arena, MD_ArenaOperation_SetAutoAlign, v); + MD_IMPL_ArenaSetAutoAlign((MD_IMPL_Arena*)arena, v); } MD_FUNCTION_IMPL void MD_ArenaPushAlign(MD_Arena *arena, MD_u64 v){ - arena->func(arena, MD_ArenaOperation_PushAlign, v); + MD_IMPL_ArenaPushAlign((MD_IMPL_Arena*)arena, v); } MD_FUNCTION_IMPL void MD_ArenaClear(MD_Arena *arena){ - arena->func(arena, MD_ArenaOperation_PopTo, 0); + MD_u64 pos = MD_IMPL_ArenaMinPos((MD_IMPL_Arena*)arena); + MD_IMPL_ArenaPopTo((MD_IMPL_Arena*)arena, pos); } MD_FUNCTION_IMPL MD_Arena* MD_ArenaNew(MD_u64 cap){ - return(MD_IMPL_ArenaNew(cap)); + return((MD_Arena*)MD_IMPL_ArenaNew(cap)); } MD_FUNCTION_IMPL void MD_ArenaRelease(MD_Arena *arena){ - MD_IMPL_ArenaRelease(arena); + MD_IMPL_ArenaRelease((MD_IMPL_Arena*)arena); } //~ Thread Context Functions diff --git a/source/md.h b/source/md.h index e1158fa..9186c90 100644 --- a/source/md.h +++ b/source/md.h @@ -260,28 +260,7 @@ typedef double MD_f64; //~ Abstract Arena -typedef union MD_IntPtr MD_IntPtr; -union MD_IntPtr{ - MD_u64 u64; - void *ptr; -}; - -typedef struct MD_Arena MD_Arena; - -typedef enum MD_ArenaOperation{ - MD_ArenaOperation_GetPos, - MD_ArenaOperation_GetCap, - MD_ArenaOperation_Push, - MD_ArenaOperation_PopTo, - MD_ArenaOperation_PushAlign, - MD_ArenaOperation_SetAutoAlign, -} MD_ArenaOperation; - -typedef MD_IntPtr MD_ArenaFunc(MD_Arena *arena, MD_ArenaOperation op, MD_u64 v); - -struct MD_Arena{ - MD_ArenaFunc *func; -}; +typedef void MD_Arena; //~ Arena Helpers