buddy refactor

This commit is contained in:
jacob 2025-07-29 20:18:02 -05:00
parent deaa397709
commit 8dfd996cdf
3 changed files with 134 additions and 90 deletions

View File

@ -1,10 +1,9 @@
/* TODO: Elminiate meta arena. Just store levels in first 4096 bytes of buddy arena, and then zone header data at the beginning of each allocation. */ /* TODO: Elminiate meta arena. Just store levels in first 4096 bytes of buddy arena, and then zone header data at the beginning of each allocation. */
/* ========================== * ////////////////////////////////
* Ctx //~ Buddy ctx
* ========================== */
BuddyCtx *buddy_ctx_alloc(u64 reserve) BuddyCtx *AllocBuddyCtx(u64 reserve)
{ {
/* TODO: Determine meta reserve dynamically */ /* TODO: Determine meta reserve dynamically */
Arena *meta_arena = AllocArena(GIBI(64)); Arena *meta_arena = AllocArena(GIBI(64));
@ -14,7 +13,8 @@ BuddyCtx *buddy_ctx_alloc(u64 reserve)
/* TODO: Minimum block size */ /* TODO: Minimum block size */
ctx->levels = PushArray(ctx->meta_arena, BuddyLevel, 64); ctx->levels = PushArray(ctx->meta_arena, BuddyLevel, 64);
for (u64 i = 0; i < 64; ++i) { for (u64 i = 0; i < 64; ++i)
{
BuddyLevel *level = &ctx->levels[i]; BuddyLevel *level = &ctx->levels[i];
level->ctx = ctx; level->ctx = ctx;
level->tier = i; level->tier = i;
@ -24,81 +24,151 @@ BuddyCtx *buddy_ctx_alloc(u64 reserve)
return ctx; return ctx;
} }
void buddy_ctx_release(BuddyCtx *ctx) void ReleaseBuddyCtx(BuddyCtx *ctx)
{ {
ReleaseArena(ctx->data_arena); ReleaseArena(ctx->data_arena);
ReleaseArena(ctx->meta_arena); ReleaseArena(ctx->meta_arena);
} }
/* ========================== * ////////////////////////////////
* Block //~ Block alloc / release
* ========================== */
INTERNAL BuddyBlock *buddy_block_alloc_internal(BuddyCtx *ctx) BuddyBlock *AllocBuddyBlock(BuddyCtx *ctx, u64 size)
{
if (size > 0x00FFFFFFFFFFFFFFULL)
{
/* TODO: Error */
ASSERT(0);
}
/* TODO: Minimum block size */
/* TODO: Faster MSB calculation */
u64 desired_block_size = 1;
u64 desired_level_tier = 0;
while (desired_block_size < size && desired_level_tier < 64)
{
desired_block_size <<= 1;
++desired_level_tier;
}
BuddyBlock *block = GetUnusedBuddyBlock(ctx, &ctx->levels[desired_level_tier]);
return block;
}
void ReleaseBuddyBlock(BuddyBlock *block)
{
block->is_used = 0;
BuddyLevel *level = block->level;
BuddyBlock *parent = block->parent;
BuddyBlock *sibling = block->sibling;
if (!sibling->is_used && parent != 0)
{
/* Merge siblings */
BuddyCtx *ctx = level->ctx;
PopBuddyBlock(ctx, level, block);
PopBuddyBlock(ctx, level, sibling);
/* Release parent */
ReleaseBuddyBlock(parent);
}
else
{
if (level->first_unused_block)
{
block->next = level->first_unused_block;
level->first_unused_block->prev = block;
}
level->first_unused_block = block;
}
}
////////////////////////////////
//~ Block push / pop
//- Push
BuddyBlock *PushBuddyBlock(BuddyCtx *ctx)
{ {
BuddyBlock *block; BuddyBlock *block;
if (ctx->first_free_block) { if (ctx->first_free_block)
{
block = ctx->first_free_block; block = ctx->first_free_block;
ctx->first_free_block = block->next; ctx->first_free_block = block->next;
} else { }
else
{
block = PushStructNoZero(ctx->meta_arena, BuddyBlock); block = PushStructNoZero(ctx->meta_arena, BuddyBlock);
} }
MEMZERO_STRUCT(block); MEMZERO_STRUCT(block);
return block; return block;
} }
INTERNAL void buddy_block_release_internal(BuddyCtx *ctx, BuddyLevel *level, BuddyBlock *block) //- Pop
void PopBuddyBlock(BuddyCtx *ctx, BuddyLevel *level, BuddyBlock *block)
{ {
/* Remove from unused list */ /* Remove from unused list */
{ {
BuddyBlock *prev = block->prev; BuddyBlock *prev = block->prev;
BuddyBlock *next = block->next; BuddyBlock *next = block->next;
if (prev) { if (prev)
{
prev->next = next; prev->next = next;
} else { }
else
{
level->first_unused_block = next; level->first_unused_block = next;
} }
if (next) { if (next)
{
next->prev = prev; next->prev = prev;
} }
} }
block->next = ctx->first_free_block; block->next = ctx->first_free_block;
ctx->first_free_block = block; ctx->first_free_block = block;
} }
INTERNAL BuddyBlock *buddy_block_get_unused(BuddyCtx *ctx, BuddyLevel *level) ////////////////////////////////
//~ Get unused block
BuddyBlock *GetUnusedBuddyBlock(BuddyCtx *ctx, BuddyLevel *level)
{ {
BuddyBlock *block = 0; BuddyBlock *block = 0;
/* TODO: Tier oob check */ /* TODO: Tier oob check */
if (level->first_unused_block) { if (level->first_unused_block)
{
block = level->first_unused_block; block = level->first_unused_block;
level->first_unused_block = block->next; level->first_unused_block = block->next;
if (level->first_unused_block) { if (level->first_unused_block)
{
level->first_unused_block->prev = 0; level->first_unused_block->prev = 0;
} }
block->is_used = 1; block->is_used = 1;
block->next = 0; block->next = 0;
} else { }
if (level->backed) { else
{
if (level->backed)
{
BuddyLevel *parent_level = &ctx->levels[level->tier + 1]; BuddyLevel *parent_level = &ctx->levels[level->tier + 1];
BuddyBlock *parent_block = buddy_block_get_unused(ctx, parent_level); BuddyBlock *parent_block = GetUnusedBuddyBlock(ctx, parent_level);
/* Create left (used) block from parent block */ /* Create left (used) block from parent block */
BuddyBlock *left = buddy_block_alloc_internal(ctx); BuddyBlock *left = PushBuddyBlock(ctx);
left->is_used = 1; left->is_used = 1;
left->level = level; left->level = level;
left->parent = parent_block; left->parent = parent_block;
left->memory = parent_block->memory; left->memory = parent_block->memory;
/* Create right (unused) block from parent block */ /* Create right (unused) block from parent block */
BuddyBlock *right = buddy_block_alloc_internal(ctx); BuddyBlock *right = PushBuddyBlock(ctx);
right->is_used = 0; right->is_used = 0;
right->level = level; right->level = level;
right->parent = parent_block; right->parent = parent_block;
right->memory = left->memory + level->size; right->memory = left->memory + level->size;
if (level->first_unused_block) { if (level->first_unused_block)
{
right->next = level->first_unused_block; right->next = level->first_unused_block;
level->first_unused_block->prev = right; level->first_unused_block->prev = right;
} }
@ -107,24 +177,27 @@ INTERNAL BuddyBlock *buddy_block_get_unused(BuddyCtx *ctx, BuddyLevel *level)
left->sibling = right; left->sibling = right;
right->sibling = left; right->sibling = left;
block = left; block = left;
} else { }
else
{
Arena *arena = ctx->data_arena; Arena *arena = ctx->data_arena;
/* Grow arena */ /* Grow arena */
i64 level_commit_diff = (level->size * 2) - arena->pos; i64 level_commit_diff = (level->size * 2) - arena->pos;
if (level_commit_diff > 0) { if (level_commit_diff > 0)
{
PushArrayNoZero(arena, u8, level_commit_diff); PushArrayNoZero(arena, u8, level_commit_diff);
ASSERT(arena->pos == (level->size * 2)); ASSERT(arena->pos == (level->size * 2));
} }
/* Create left (used) block from existing child block memory */ /* Create left (used) block from existing child block memory */
BuddyBlock *left = buddy_block_alloc_internal(ctx); BuddyBlock *left = PushBuddyBlock(ctx);
left->is_used = 1; left->is_used = 1;
left->level = level; left->level = level;
left->memory = ArenaBase(arena); left->memory = ArenaBase(arena);
/* Create right (unused) block from new arena memory */ /* Create right (unused) block from new arena memory */
BuddyBlock *right = buddy_block_alloc_internal(ctx); BuddyBlock *right = PushBuddyBlock(ctx);
right->is_used = 0; right->is_used = 0;
right->level = level; right->level = level;
right->memory = left->memory + level->size; right->memory = left->memory + level->size;
@ -139,52 +212,3 @@ INTERNAL BuddyBlock *buddy_block_get_unused(BuddyCtx *ctx, BuddyLevel *level)
return block; return block;
} }
INTERNAL void buddy_block_mark_unused(BuddyBlock *block)
{
block->is_used = 0;
BuddyLevel *level = block->level;
BuddyBlock *parent = block->parent;
BuddyBlock *sibling = block->sibling;
if (!sibling->is_used && parent != 0) {
/* Merge siblings */
BuddyCtx *ctx = level->ctx;
buddy_block_release_internal(ctx, level, block);
buddy_block_release_internal(ctx, level, sibling);
buddy_block_mark_unused(parent);
} else {
if (level->first_unused_block) {
block->next = level->first_unused_block;
level->first_unused_block->prev = block;
}
level->first_unused_block = block;
}
}
BuddyBlock *buddy_alloc(BuddyCtx *ctx, u64 size)
{
if (size > 0x00FFFFFFFFFFFFFFULL) {
/* TODO: Error */
ASSERT(0);
}
/* TODO: Minimum block size */
/* TODO: Faster MSB calculation */
u64 desired_block_size = 1;
u64 desired_level_tier = 0;
while (desired_block_size < size && desired_level_tier < 64) {
desired_block_size <<= 1;
++desired_level_tier;
}
BuddyBlock *block = buddy_block_get_unused(ctx, &ctx->levels[desired_level_tier]);
return block;
}
void buddy_release(BuddyBlock *block)
{
buddy_block_mark_unused(block);
}

View File

@ -1,4 +1,8 @@
Struct(BuddyBlock) { ////////////////////////////////
//~ Buddy types
Struct(BuddyBlock)
{
b32 is_used; b32 is_used;
struct BuddyLevel *level; struct BuddyLevel *level;
BuddyBlock *parent; BuddyBlock *parent;
@ -11,7 +15,8 @@ Struct(BuddyBlock) {
u8 *memory; u8 *memory;
}; };
Struct(BuddyLevel) { Struct(BuddyLevel)
{
struct BuddyCtx *ctx; struct BuddyCtx *ctx;
b32 backed; /* Signals whether this level is backed by memory in the ctx arena */ b32 backed; /* Signals whether this level is backed by memory in the ctx arena */
u32 tier; u32 tier;
@ -19,15 +24,30 @@ Struct(BuddyLevel) {
BuddyBlock *first_unused_block; BuddyBlock *first_unused_block;
}; };
Struct(BuddyCtx) { Struct(BuddyCtx)
{
Arena *meta_arena; Arena *meta_arena;
Arena *data_arena; Arena *data_arena;
BuddyLevel *levels; BuddyLevel *levels;
BuddyBlock *first_free_block; BuddyBlock *first_free_block;
}; };
BuddyCtx *buddy_ctx_alloc(u64 reserve); ////////////////////////////////
void buddy_ctx_release(BuddyCtx *ctx); //~ Buddy context operations
BuddyBlock *buddy_alloc(BuddyCtx *ctx, u64 size); BuddyCtx *AllocBuddyCtx(u64 reserve);
void buddy_release(BuddyBlock *block); void ReleaseBuddyCtx(BuddyCtx *ctx);
////////////////////////////////
//~ Buddy block operations
//- Alloc / release
BuddyBlock *AllocBuddyBlock(BuddyCtx *ctx, u64 size);
void ReleaseBuddyBlock(BuddyBlock *block);
//- Push / pop
BuddyBlock *PushBuddyBlock(BuddyCtx *ctx);
void PopBuddyBlock(BuddyCtx *ctx, BuddyLevel *level, BuddyBlock *block);
//- Get unused
BuddyBlock *GetUnusedBuddyBlock(BuddyCtx *ctx, BuddyLevel *level);

View File

@ -149,7 +149,7 @@ N_Host *host_alloc(u16 listen_port)
host->rcv_buffer_write = PushStruct(host->arena, N_RcvBuffer); host->rcv_buffer_write = PushStruct(host->arena, N_RcvBuffer);
host->rcv_buffer_read->arena = AllocArena(GIBI(64)); host->rcv_buffer_read->arena = AllocArena(GIBI(64));
host->rcv_buffer_write->arena = AllocArena(GIBI(64)); host->rcv_buffer_write->arena = AllocArena(GIBI(64));
host->buddy = buddy_ctx_alloc(GIBI(64)); host->buddy = AllocBuddyCtx(GIBI(64));
host->channels = PushDry(host->channel_arena, struct host_channel); host->channels = PushDry(host->channel_arena, struct host_channel);
@ -168,7 +168,7 @@ void host_release(N_Host *host)
{ {
P_ReleaseSock(host->sock); P_ReleaseSock(host->sock);
buddy_ctx_release(host->buddy); ReleaseBuddyCtx(host->buddy);
ReleaseArena(host->rcv_buffer_write->arena); ReleaseArena(host->rcv_buffer_write->arena);
ReleaseArena(host->rcv_buffer_read->arena); ReleaseArena(host->rcv_buffer_read->arena);
ReleaseArena(host->channel_arena); ReleaseArena(host->channel_arena);
@ -368,7 +368,7 @@ INTERNAL struct host_msg_assembler *host_msg_assembler_alloc(struct host_channel
/* Allocate msg data using buddy allocator since the assembler has /* Allocate msg data using buddy allocator since the assembler has
* arbitrary lifetime and data needs to stay contiguous for random * arbitrary lifetime and data needs to stay contiguous for random
* access as packets are received */ * access as packets are received */
ma->buddy_block = buddy_alloc(host->buddy, chunk_bitmap_size + chunk_data_size); ma->buddy_block = AllocBuddyBlock(host->buddy, chunk_bitmap_size + chunk_data_size);
ma->chunk_bitmap = ma->buddy_block->memory; ma->chunk_bitmap = ma->buddy_block->memory;
MEMZERO(ma->chunk_bitmap, chunk_bitmap_size); MEMZERO(ma->chunk_bitmap, chunk_bitmap_size);
ma->chunk_data = ma->chunk_bitmap + chunk_bitmap_size; ma->chunk_data = ma->chunk_bitmap + chunk_bitmap_size;
@ -405,7 +405,7 @@ INTERNAL void host_msg_assembler_release(struct host_msg_assembler *ma)
{ {
struct host_channel *channel = ma->channel; struct host_channel *channel = ma->channel;
N_Host *host = channel->host; N_Host *host = channel->host;
buddy_release(ma->buddy_block); ReleaseBuddyBlock(ma->buddy_block);
/* Release from channel list */ /* Release from channel list */
{ {