Implement component pools
parent
ea4c1216e0
commit
849a9f0059
|
@ -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}")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue