finished free list allocator (linked list) and added small test

This commit is contained in:
Matthew 2025-04-18 12:37:35 +10:00
parent f57018317a
commit 7602982c93
10 changed files with 345 additions and 35 deletions

View File

@ -57,7 +57,7 @@ clang_out="-o"
gcc_common="${include_flags} ${render_flag} -DCOMPILER_GCC -std=c99 -fuse-ld=mold -g -Wno-unknown-warning-option -Wall -Wno-missing-braces -Wno-unused-function -Wno-attributes -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-compare-distinct-pointer-types -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf -DVMA_STATIC_VULKAN_FUNCTIONS=0" gcc_common="${include_flags} ${render_flag} -DCOMPILER_GCC -std=c99 -fuse-ld=mold -g -Wno-unknown-warning-option -Wall -Wno-missing-braces -Wno-unused-function -Wno-attributes -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-compare-distinct-pointer-types -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf -DVMA_STATIC_VULKAN_FUNCTIONS=0"
gcc_debug="$compiler -g -O0 -DBUILD_DEBUG=1 ${gcc_common}" gcc_debug="$compiler -g -O0 -DBUILD_DEBUG=1 ${gcc_common}"
gcc_release="$compiler -O2 ${gcc_common} ${render_flag}" gcc_release="$compiler -O2 ${gcc_common} ${render_flag}"
gcc_test="$compiler -O2 -DBUILD_TEST=1 ${gcc_common} ${render_flag}" gcc_test="$compiler -O0 -DBUILD_TEST=1 ${gcc_common} ${render_flag}"
gcc_link="-lpthread -lm -lrt -ldl ${render_link} -lstdc++" gcc_link="-lpthread -lm -lrt -ldl ${render_link} -lstdc++"
gcc_out="-o" gcc_out="-o"

View File

@ -1,7 +1,7 @@
// ::Allocator::Globals:: // ::Allocator::Globals::
Allocator g_alloc; Allocator g_alloc;
read_only FLNode FL_NIL_NODE = {}; read_only FLNode FL_NIL_NODE = {0};
// ::Allocator::Util::Header:: // ::Allocator::Util::Header::
@ -95,6 +95,8 @@ static Arena * CreateArenaDebug(rawptr buffer, usize length, u32 init_line_no)
// ::Allocator::Arena::End:: // ::Allocator::Arena::End::
// ::Allocator::GlobalAlloc::Start:: // ::Allocator::GlobalAlloc::Start::
static void InitAllocator(usize init_size, usize grow_size) static void InitAllocator(usize init_size, usize grow_size)
@ -152,34 +154,71 @@ static void AllocGrow()
} }
// ::Allocator::FreeList::End:: // ::Allocator::GlobalAlloc::End::
static void FLAllocInit(FLAlloc *alloc, usize size)
// ::Allocator::FreeList::Start::
static void FreeListInit(FLAlloc *alloc, usize size)
{ {
alloc->data = MemAllocZeroed(size); alloc->lists = MemAllocZeroed(sizeof(FreeList *) * 16);
alloc->size = size; alloc->list_count = 1;
alloc->list_capacity = 16;
alloc->ticket = 0;
alloc->next_ticket = 0;
alloc->nil = &FL_NIL_NODE; alloc->nil = &FL_NIL_NODE;
FLAllocFreeAll(alloc); alloc->grow_size = size;
_FreeListInit(&alloc->lists[0], size);
FreeListFreeAll(alloc);
} }
static void FLAllocFreeAll(FLAlloc *alloc) static void _FreeListInit(FreeList **alloc, usize size)
{ {
FLNode *node = (FLNode *)alloc->data; *alloc = (FreeList *)MemAllocZeroed(size);
node->size = alloc->size; (*alloc)->data = (rawptr)(((uintptr)*alloc) + ((uintptr)sizeof(FreeList)));
node->next = alloc->nil; (*alloc)->size = size;
(*alloc)->used = sizeof(FreeList);
alloc->used = 0;
alloc->head = node;
} }
static FLNode *FreeListSearch(FLAlloc *alloc, usize size, u32 alignment, u32 *out_padding, FLNode **prev_node) static void FreeListFreeAll(FLAlloc *alloc)
{
u32 ticket = __atomic_fetch_add(&alloc->ticket, 1, __ATOMIC_SEQ_CST);
while (ticket != alloc->next_ticket);
if (alloc->list_count > 1)
{
for (u32 i = 1; i < alloc->list_count; i++)
{
MemFree(alloc->lists[i], alloc->lists[i]->size);
alloc->lists[i] = NULL;
}
alloc->list_count = 1;
}
FLNode *node = (FLNode *)alloc->lists[0]->data;
node->size = alloc->lists[0]->size;
node->next = &FL_NIL_NODE;
alloc->lists[0]->head = node;
alloc->lists[0]->used = sizeof(FreeList);
__atomic_thread_fence(__ATOMIC_RELEASE);
__atomic_fetch_add(&alloc->next_ticket, 1, __ATOMIC_SEQ_CST);
}
static FLNode *FreeListSearch(FreeList *alloc, usize size, usize alignment, usize *out_padding, FLNode **prev_node)
{ {
FLNode *node = alloc->head; FLNode *node = alloc->head;
FLNode *prev = alloc->nil; FLNode *prev = &FL_NIL_NODE;
usize padding = 0; usize padding = 0;
while (node != alloc->nil) while (node != &FL_NIL_NODE)
{ {
padding = CalcPaddingWithHeader((uintptr)node, (uintptr)alignment, sizeof(FLNode)); padding = CalcPaddingWithHeader((uintptr)node, (uintptr)alignment, sizeof(FLNode));
usize required_size = size + padding; usize required_size = size + padding;
@ -199,12 +238,194 @@ static FLNode *FreeListSearch(FLAlloc *alloc, usize size, u32 alignment, u32 *ou
return node; return node;
} }
static rawptr FreeListAlloc(FLAlloc *alloc, usize size, u32 alignment) /*
* NOT SAFE TO CALL OUTSIDE OF FreeListAlloc
*/
static void FreeListGrow(FLAlloc *alloc, usize alloc_size)
{
alloc->list_count += 1;
u32 i = alloc->list_count;
if (i >= alloc->list_capacity)
{
alloc->list_capacity += 16;
rawptr new_mem = MemAlloc(sizeof(FreeList *) * alloc->list_capacity);
MemCpy(new_mem, alloc->lists, i);
MemFree(alloc->lists, sizeof(FreeList *) * i);
alloc->lists = new_mem;
}
usize grow_size = alloc->grow_size;
if (alloc_size > grow_size)
grow_size += alloc_size;
_FreeListInit(&alloc->lists[i], grow_size);
FLNode *node = (FLNode *)alloc->lists[i]->data;
node->size = alloc->lists[i]->size;
node->next = alloc->nil;
alloc->lists[i]->head = node;
}
static rawptr _FreeListAllocAlign(FreeList *alloc, usize size, u32 alignment)
{ {
usize padding = 0; usize padding = 0;
FLNode *prev_node = alloc->nil; FLNode *prev_node = &FL_NIL_NODE;
FLAllocHeader *header;
if (size < sizeof(FLNode))
size = sizeof(FLNode);
if (alignment < 8)
alignment = 8;
FLNode *node = FreeListSearch(alloc, size, alignment, &padding, &prev_node);
if (node == &FL_NIL_NODE)
Assert(0, "FreeListAllocAlign failed to allocate, oom");
return NULL; usize alignment_padding = padding - sizeof(FLAllocHeader);
usize required_space = size + padding;
usize remaining = node->size - required_space;
if (remaining > 0)
{
FLNode *new_node = (FLNode *)(((c8 *)node) + required_space);
new_node->size = remaining;
FreeListInsert(&alloc->head, node, new_node);
}
FreeListRemove(&alloc->head, prev_node, node);
header = (FLAllocHeader *)(((c8 *)node) + alignment_padding);
header->size = required_space;
header->padding = alignment_padding;
alloc->used += required_space;
return (rawptr)(((c8 *)header) + sizeof(FLAllocHeader));
} }
static rawptr FreeListAlloc(FLAlloc *alloc, usize size)
{
return FreeListAllocAlign(alloc, size, DEFAULT_ALIGNMENT);
}
static rawptr FreeListAllocAlign(FLAlloc *alloc, usize size, u32 alignment)
{
u32 ticket = __atomic_fetch_add(&alloc->ticket, 1, __ATOMIC_SEQ_CST);
while (ticket != alloc->next_ticket);
FreeList *fl = NULL;
for (u32 i = 0; i < alloc->list_count; i++)
{
usize remaining = alloc->lists[i]->size - alloc->lists[i]->used;
if (size < remaining)
{
fl = alloc->lists[i];
break;
}
}
if (fl == NULL)
{
FreeListGrow(alloc, size);
fl = alloc->lists[alloc->list_count-1];
}
rawptr ptr = _FreeListAllocAlign(fl, size, alignment);
__atomic_thread_fence(__ATOMIC_RELEASE);
__atomic_add_fetch(&alloc->next_ticket, 1, __ATOMIC_SEQ_CST);
return ptr;
}
static void _FreeListFree(FreeList *alloc, rawptr ptr)
{
FLAllocHeader *header = cast(FLAllocHeader *, u8ptr(ptr) - sizeof(FLAllocHeader));
FLNode *free_node = cast(FLNode *,header);
free_node->size = header->size + header->padding;
free_node->next = &FL_NIL_NODE;
FLNode *prev_node = &FL_NIL_NODE;
FLNode *node = alloc->head;
while (node != &FL_NIL_NODE)
{
if (ptr < node)
{
FreeListInsert(&alloc->head, prev_node, free_node);
break;
}
prev_node = node;
node = node->next;
}
alloc->used -= free_node->size;
FreeListCoalescence(alloc, prev_node, free_node);
}
static void FreeListFree(FLAlloc *alloc, rawptr ptr)
{
if (ptr == NULL) return;
u32 ticket = __atomic_fetch_add(&alloc->ticket, 1, __ATOMIC_SEQ_CST);
while (ticket != alloc->next_ticket);
for (u32 i = 0; i < alloc->list_count; i++)
{
uintptr ptr_addr = uintptr(ptr);
uintptr start = uintptr(alloc->lists[i]->data);
uintptr end = uintptr(alloc->lists[i]->data) + uintptr(alloc->lists[i]->size);
if (ptr_addr >= start && ptr_addr < end)
{
_FreeListFree(alloc->lists[i], ptr);
break;
}
}
}
static void FreeListCoalescence(FreeList *alloc, FLNode *prev_node, FLNode *free_node)
{
if (free_node->next != &FL_NIL_NODE && (rawptr)(((c8 *)free_node) + free_node->size) == free_node->next)
{
free_node->size += free_node->next->size;
FreeListRemove(&alloc->head, free_node, free_node->next);
}
if (prev_node->next != &FL_NIL_NODE && (rawptr)(((c8 *)prev_node) + prev_node->size) == free_node)
{
prev_node->size += free_node->next->size;
FreeListRemove(&alloc->head, prev_node, free_node);
}
}
static void FreeListRemove(FLNode **head, FLNode *prev_node, FLNode *del_node)
{
if (prev_node == &FL_NIL_NODE)
*head = del_node->next;
else
prev_node->next = del_node->next;
}
static void FreeListInsert(FLNode **head, FLNode *prev_node, FLNode *new_node)
{
if (prev_node == &FL_NIL_NODE)
{
if (*head != &FL_NIL_NODE)
new_node->next = *head;
else
*head = new_node;
}
else
{
new_node->next = prev_node->next;
prev_node->next = new_node;
}
}
// ::Allocator::FreeList::End::

View File

@ -58,7 +58,7 @@ static void Free(rawptr ptr);
typedef struct FLAllocHeader typedef struct FLAllocHeader
{ {
usize block_size; usize size;
usize padding; usize padding;
} FLAllocHeader; } FLAllocHeader;
@ -70,16 +70,35 @@ typedef struct FLNode
usize size; usize size;
} FLNode; } FLNode;
typedef struct FLAlloc typedef struct FreeList
{ {
rawptr data; rawptr data;
FLNode *head; FLNode *head;
FLNode *nil;
usize size; usize size;
usize used; usize used;
} FreeList;
typedef struct FLAlloc
{
FreeList **lists;
u32 list_count;
u32 list_capacity;
u32 volatile ticket;
u32 volatile next_ticket;
FLNode *nil;
usize grow_size;
} FLAlloc; } FLAlloc;
static void FLAllocInit(FLAlloc *alloc, usize size); static void _FreeListInit(FreeList **alloc, usize size);
static void FLAllocFreeAll(FLAlloc *alloc); static void FreeListInit(FLAlloc *alloc, usize size);
static FLNode *FreeListSearch(FLAlloc *alloc, usize size, u32 alignment, u32 *out_padding, FLNode **prev_node); static rawptr _FreeListAllocAlign(FreeList *alloc, usize size, u32 alignment);
static rawptr FreeListAlloc(FLAlloc *alloc, usize size, u32 alignment); static rawptr FreeListAllocAlign(FLAlloc *alloc, usize size, u32 alignment);
static rawptr FreeListAlloc(FLAlloc *alloc, usize size);
static void _FreeListFree(FreeList *alloc, rawptr ptr);
static void FreeListFree(FLAlloc *alloc, rawptr ptr);
static void FreeListFreeAll(FLAlloc *alloc);
static void FreeListGrow(FLAlloc *alloc, usize alloc_size);
static FLNode *FreeListSearch(FreeList *alloc, usize size, usize alignment, usize *out_padding, FLNode **prev_node);
static void FreeListCoalescence(FreeList *alloc, FLNode *prev_node, FLNode *free_node);
static void FreeListRemove(FLNode **head, FLNode *prev_node, FLNode *del_node);
static void FreeListInsert(FLNode **head, FLNode *prev_node, FLNode *new_node);

View File

@ -108,7 +108,7 @@ int main(int argc, char **argv)
WaitForWindowEvent(inputs); WaitForWindowEvent(inputs);
GameContext ctx = {}; GameContext ctx = {0};
InitializeGame(renderer_arena, &ctx, game_arena); InitializeGame(renderer_arena, &ctx, game_arena);

View File

@ -31,8 +31,8 @@ const FileMapping g_Texture_File_Map[] = {
{ .file_name = "purplemon.png", .ix = PATTERMON_PURPLOID }, { .file_name = "purplemon.png", .ix = PATTERMON_PURPLOID },
}; };
c8 *g_Shader_File_Names[SHADER_ASSET_MAX] = {}; c8 *g_Shader_File_Names[SHADER_ASSET_MAX] = {0};
c8 *g_Texture_File_Names[TEXTURE_ASSET_MAX] = {}; c8 *g_Texture_File_Names[TEXTURE_ASSET_MAX] = {0};
// ::Packer::Globals::End:: // ::Packer::Globals::End::
@ -380,7 +380,7 @@ int main(int argc, c8 **argv)
FILE *file = fopen("assets.sgp", "w+"); FILE *file = fopen("assets.sgp", "w+");
Assert(file != NULL, "File is null"); Assert(file != NULL, "File is null");
FileHeader header = {}; FileHeader header = {0};
InitHeader(&header); InitHeader(&header);
PackFiles(arena, &header); PackFiles(arena, &header);

View File

@ -17,7 +17,7 @@ static Renderer renderer = {
} }
}; };
static u32 vk_thread_indices[10] = {}; static u32 vk_thread_indices[10] = {0};
#if __linux__ #if __linux__
pthread_cond_t cond; pthread_cond_t cond;

View File

@ -209,8 +209,8 @@ static b32 UploadToBuffer(RenderBuffer **buffers, rawptr *ptrs, u32 count, u8 th
VkDevice device = renderer.vk.device; VkDevice device = renderer.vk.device;
VkQueue queue = renderer.vk.queues.transfer_queue; VkQueue queue = renderer.vk.queues.transfer_queue;
VmaAllocator alloc = renderer.vk.alloc; VmaAllocator alloc = renderer.vk.alloc;
rawptr mapped_buffers[16] = {}; rawptr mapped_buffers[16] = {0};
RenderBuffer staging_buffers[16] = {}; RenderBuffer staging_buffers[16] = {0};
u32 copy_count = 0; u32 copy_count = 0;

View File

@ -1,5 +1,48 @@
void RunTests() void RunTests()
{ {
TestFreeListAlloc();
}
void TestFreeListAlloc()
{
FLAlloc alloc = {0};
usize alloc_size = KB(256);
FreeListInit(&alloc, alloc_size);
u8 *value = (u8 *)(uintptr(alloc.lists[0]->data) + uintptr(alloc_size-sizeof(FreeList)-2));
*value = 255;
Assert(*value == 255, "data set is invalid");
u32 u32_count = 2000;
u32 f64_count = 3000;
u32 *u32_arr = FreeListAlloc(&alloc, sizeof(u32) * u32_count);
f64 *f64_arr = FreeListAlloc(&alloc, sizeof(f64) * f64_count);
for (u32 i = 0; i < u32_count; i++)
{
u32_arr[i] = 3 * i;
}
for (u32 i = 0; i < f64_count; i++)
{
f64_arr[i] = f64(i);
}
for (u32 i = 0; i < u32_count; i++)
{
Assert(u32_arr[i] == 3 * i, "u32 memory incorrect");
}
for (u32 i = 0; i < f64_count; i++)
{
Assert(f64_arr[i] == f64(i), "f64 memory incorrect");
}
FreeListFree(&alloc, u32_arr);
for (u32 i = 0; i < f64_count; i++)
{
Assert(f64_arr[i] == f64(i), "f64 memory incorrect post free");
}
} }

View File

@ -1,3 +1,5 @@
#pragma once #pragma once
void RunTests(); void RunTests();
void TestFreeListAlloc();

View File

@ -24,6 +24,31 @@ typedef struct Arena Arena;
#define Str8L(x) (Str8){ .len = StrLen(x), .value = (u8 *)x } #define Str8L(x) (Str8){ .len = StrLen(x), .value = (u8 *)x }
#define MakeString(x) #x #define MakeString(x) #x
#define i8(x) ((i8)(x))
#define i16(x) ((i16)(x))
#define i32(x) ((i32)(x))
#define i64(x) ((i64)(x))
#define u8(x) ((u8)(x))
#define u16(x) ((u16)(x))
#define u32(x) ((u32)(x))
#define u64(x) ((u64)(x))
#define c8(x) ((c8)(x))
#define c8ptr(x) ((c8 *)(x))
#define u8ptr(x) ((u8 *)(x))
#define i8ptr(x) ((i8 *)(x))
#define intptr(x) ((intptr)(x))
#define uintptr(x) ((uintptr)(x))
#define f32(x) ((f32)(x))
#define f64(x) ((f64)(x))
#define rawptr(x) ((rawptr)(x))
#define cast(T, x) ((T)(x))
// ::Util::Defines::Header:: // ::Util::Defines::Header::
#define HM_MAX_SYMBOLS 256 #define HM_MAX_SYMBOLS 256