Implement component pools

master
En Yi 2024-12-29 16:13:35 +08:00
parent ea4c1216e0
commit 849a9f0059
10 changed files with 326 additions and 16 deletions

View File

@ -5,6 +5,7 @@ cmake_minimum_required(VERSION 3.22.1)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(${PROJECT_NAME} C CXX)
set(CMAKE_C_STANDARD 17)
set(RAYLIB_DIR /usr/local/lib CACHE FILEPATH "directory to Raylib")
if (EMSCRIPTEN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")

View File

@ -6,11 +6,19 @@ add_library(engine
target_include_directories(engine
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${RAYLIB_DIR}/include
)
target_link_directories(engine
PUBLIC
${RAYLIB_DIR}/lib
)
target_link_libraries(engine
PUBLIC
cc
base
raylib
m
)
#if (BUILD_TESTING)

View File

@ -74,10 +74,10 @@ void mem_arena_print()
printf("O1heap Memory Arena Info\n");
printf("--------------------\n");
printf("Capacity: %.3f MB\n", diag.capacity * 1.0 / 1024 / 1024);
printf("Allocated: %lu B\n", diag.allocated);
printf("Peak allocated: %lu B\n", diag.peak_allocated);
printf("Allocated: %.3f MB\n", diag.allocated * 1.0 /1024/1024);
printf("Peak allocated: %.3f MB\n", diag.peak_allocated * 1.0 /1024/1024);
printf("Peak request: %lu\n", diag.peak_request_size);
printf("OOM count: %lu\n", diag.oom_count);
printf("OOM count: %lu\n\n", diag.oom_count);
}
size_t mem_arena_get_allocated(void)

View File

@ -1,6 +1,8 @@
#ifndef _ENGINE_CONF_H
#define _ENGINE_CONF_H
#define MEMORY_ARENA_SIZE_MB 16
// Take care tuning these params. Web build doesn't work
// if memory used too high
#define MAX_SCENES_TO_RENDER 8

View File

@ -1,20 +1,81 @@
#include "memory.h"
//#include "cmc/utl/futils.h"
/* Function implementation */
//#include "cmc/treeset/code.h"
#include "engine_conf.h"
#include <stdio.h>
#include <assert.h>
void init_memory_system(void) {
for (uint32_t i = 0; i < mem_impl.n_components; ++i) {
//mem_impl.comp_mempools[i].free_set = idxSet_new(
// &(struct idxSet_fval){ .cmp = cmc_u32_cmp, NULL}
//);
void init_memory_system(void)
{
mem_arena_init(MEMORY_ARENA_SIZE_MB);
for (uint32_t i = 0; i < mem_impl.n_components; ++i)
{
memset(mem_impl.comp_mempools[i].buffer, 0, mem_impl.comp_mempools[i].elem_size * mem_impl.comp_mempools[i].capacity);
cc_init(&mem_impl.comp_mempools[i].free_set);
for (size_t j = 0; j < mem_impl.comp_mempools[i].capacity; ++j)
{
cc_insert(&mem_impl.comp_mempools[i].free_set, j);
}
}
}
void free_memory_system(void) {
for (uint32_t i = 0; i < mem_impl.n_components; ++i) {
//idxSet_free(mem_impl.comp_mempools[i].free_set);
void free_memory_system(void)
{
for (uint32_t i = 0; i < mem_impl.n_components; ++i)
{
cc_cleanup(&mem_impl.comp_mempools[i].free_set);
}
mem_arena_deinit();
}
void print_memory_info()
{
mem_arena_print();
printf("Component Free Pool\n");
printf("===================\n");
for (size_t i = 0; i < mem_impl.n_components; ++i)
{
printf(
"%lu: %lu\n",
i, cc_size(&mem_impl.comp_mempools[i].free_set)
);
}
printf("===================\n");
}
void* new_component_generic(unsigned int comp_type, unsigned int* idx)
{
assert(comp_type < N_COMPONENTS);
assert(comp_type < mem_impl.n_components);
if (cc_size(&mem_impl.comp_mempools[comp_type].free_set) == 0) return NULL;
*idx = *cc_first(&mem_impl.comp_mempools[comp_type].free_set);
cc_erase(&mem_impl.comp_mempools[comp_type].free_set, *idx);
void* comp = mem_impl.comp_mempools[comp_type].buffer
+ (*idx * mem_impl.comp_mempools[comp_type].elem_size);
memset(comp, 0, mem_impl.comp_mempools[comp_type].elem_size);
return comp;
}
void* get_component_generic(unsigned int comp_type, unsigned int idx)
{
void * comp = NULL;
assert(comp_type < mem_impl.n_components);
if (idx >= mem_impl.comp_mempools->capacity) return NULL;
if (cc_get(&mem_impl.comp_mempools[comp_type].free_set, idx) == NULL)
{
comp = mem_impl.comp_mempools[comp_type].buffer + (idx * mem_impl.comp_mempools[comp_type].elem_size);
}
return comp;
}
void free_component_generic(unsigned int comp_type, unsigned int idx)
{
assert(comp_type < N_COMPONENTS);
// This just free the component from the memory pool
assert(comp_type < mem_impl.n_components);
if (idx >= mem_impl.comp_mempools->capacity) return;
cc_get_or_insert(&mem_impl.comp_mempools[comp_type].free_set, idx);
}

View File

@ -3,20 +3,68 @@
#include <stdint.h>
#define CC_NO_SHORT_NAMES
#include "cc.h"
#include "base.h"
typedef struct memoryPool {
//struct idxSet* free_set;
void * const buffer;
const unsigned long capacity;
const unsigned long elem_size;
cc_oset( uint32_t ) free_set;
} memoryPool;
void init_memory_system(void);
void free_memory_system(void);
void* new_component_generic(unsigned int comp_type, unsigned int* idx);
void* get_component_generic(unsigned int comp_type, unsigned int idx);
void free_component_generic(unsigned int comp_type, unsigned int idx);
void print_memory_info();
extern struct memoryImpl {
memoryPool* comp_mempools;
uint32_t n_components;
} mem_impl;
#define DEFINE_COMPONENT_HEADERS(name, comp_id, type) \
static const unsigned int CID_##name = comp_id; \
static inline type* new_component_##name(unsigned int* idx) { \
return (type*)new_component_generic(comp_id, idx); \
}\
static inline type* get_component_##name(unsigned int idx) { \
return (type*)get_component_generic(comp_id, idx); \
}\
static inline void free_component_##name(unsigned int idx) { \
free_component_generic(comp_id, idx); \
}\
#define DEFINE_COMP_MEMPOOL_BUF(type, cap) \
static type type##_buf[cap]; \
const unsigned long type##_CNT = cap; \
#define BEGIN_DEFINE_COMP_MEMPOOL \
static memoryPool _memPool[] = {
#define ADD_COMP_MEMPOOL(name, type) \
[CID_##name] = (memoryPool){\
.buffer = type##_buf,\
.capacity = type##_CNT,\
.elem_size = sizeof(type),\
0\
}, \
#define END_DEFINE_COMP_MEMPOOL\
}; \
struct memoryImpl mem_impl = { \
.comp_mempools = _memPool, \
.n_components = USER_N_COMPONENTS, \
};\
#define STATIC_ASSERT_COMP_POOL \
static_assert( \
(sizeof(_memPool) / sizeof(_memPool[0])) == USER_N_COMPONENTS, "Insufficnet component pool definition" \
); \
#endif // ENGINE_MEMORY_H

View File

@ -14,3 +14,12 @@ target_link_libraries(ccTest PRIVATE
set_target_properties(ccTest
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/manual)
add_executable(compSample comp_sample.c)
target_link_libraries(compSample PRIVATE
engine
)
set_target_properties(compSample
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/manual)

View File

@ -0,0 +1,31 @@
#include "memory.h"
#include <assert.h>
typedef struct dummyComp {
unsigned int num;
}dummyComp;
DEFINE_COMPONENT_HEADERS(dummy, 0, dummyComp)
DEFINE_COMP_MEMPOOL_BUF(dummyComp, 16)
#define USER_N_COMPONENTS 1
BEGIN_DEFINE_COMP_MEMPOOL
ADD_COMP_MEMPOOL(dummy, dummyComp)
END_DEFINE_COMP_MEMPOOL
int main(void) {
init_memory_system();
STATIC_ASSERT_COMP_POOL;
print_memory_info();
unsigned int idx = 0;
dummyComp* comp = new_component_dummy(&idx);
assert(comp != NULL);
print_memory_info();
dummyComp* getcomp = get_component_dummy(idx);
assert(getcomp == comp);
free_component_dummy(idx);
print_memory_info();
free_memory_system();
}

View File

@ -8,4 +8,16 @@ set_target_properties(MemArenaUnitTest
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/unit)
add_executable(CompPoolUnitTest comp_pool_unit.c)
#target_compile_features(MemPoolTest PRIVATE c_std_99)
target_link_libraries(CompPoolUnitTest
PRIVATE
cmocka
engine
)
set_target_properties(CompPoolUnitTest
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/unit)
add_test(NAME MemArenaUnitTest COMMAND MemArenaUnitTest)
add_test(NAME CompPoolUnitTest COMMAND CompPoolUnitTest)

View File

@ -0,0 +1,138 @@
#include "memory.h"
#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmocka.h>
typedef struct dummyComp {
unsigned int num;
}dummyComp;
DEFINE_COMPONENT_HEADERS(dummy, 0, dummyComp)
DEFINE_COMP_MEMPOOL_BUF(dummyComp, 16)
#define USER_N_COMPONENTS 1
BEGIN_DEFINE_COMP_MEMPOOL
ADD_COMP_MEMPOOL(dummy, dummyComp)
END_DEFINE_COMP_MEMPOOL
/**
* Component Pool Test
*/
static int setup_mem_pool(void** state)
{
(void)state;
init_memory_system();
return 0;
}
static int teardown_mem_pool(void** state)
{
(void)state;
free_memory_system();
return 0;
}
static void test_simple_component_alloc(void **state)
{
// Typical usage
(void)state;
unsigned int idx = 0;
dummyComp* comp = new_component_dummy(&idx);
assert_non_null(comp);
dummyComp* check = get_component_dummy(idx);
assert_ptr_equal(comp, check);
free_component_dummy(idx);
check = get_component_dummy(idx);
assert_null(check);
}
static void test_component_OOB_get(void **state)
{
// OOB indice will result in NULL for get function
(void)state;
dummyComp* comp = get_component_dummy(16);
assert_null(comp);
comp = get_component_dummy(65535);
assert_null(comp);
}
static void test_component_reuse(void **state)
{
// Component pool must reuse idx ASAP
(void)state;
unsigned int idx = 0;
dummyComp* comp = new_component_dummy(&idx);
assert_non_null(comp);
free_component_dummy(idx);
unsigned int new_idx = 0;
new_component_dummy(&new_idx);
assert_int_equal(new_idx, idx);
}
static void test_component_double_free(void **state)
{
// A double free is okay and does not affect anything
// Double freeing the same idx will not cause it to
// be allocated twice
(void)state;
unsigned int idx = 0;
dummyComp* comp = new_component_dummy(&idx);
assert_non_null(comp);
free_component_dummy(idx);
dummyComp* check = get_component_dummy(idx);
assert_null(check);
free_component_dummy(idx);
check = get_component_dummy(idx);
assert_null(check);
unsigned int new_idx = 0;
new_component_dummy(&new_idx);
new_component_dummy(&new_idx);
assert_int_not_equal(new_idx, idx);
}
static void test_component_new_clear(void** state) {
(void)state;
unsigned int idx = 0;
dummyComp* comp = new_component_dummy(&idx);
assert_non_null(comp);
assert_int_equal(comp->num, 0);
comp->num = 16;
dummyComp* check = get_component_dummy(idx);
assert_int_equal(check->num, comp->num);
free_component_dummy(idx);
comp = new_component_dummy(&idx);
assert_non_null(comp);
assert_int_equal(comp->num, 0);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_simple_component_alloc, setup_mem_pool, teardown_mem_pool),
cmocka_unit_test_setup_teardown(test_component_reuse, setup_mem_pool, teardown_mem_pool),
cmocka_unit_test_setup_teardown(test_component_OOB_get, setup_mem_pool, teardown_mem_pool),
cmocka_unit_test_setup_teardown(test_component_new_clear, setup_mem_pool, teardown_mem_pool),
cmocka_unit_test_setup_teardown(test_component_double_free, setup_mem_pool, teardown_mem_pool),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}