Implement component pools
parent
ea4c1216e0
commit
849a9f0059
|
@ -5,6 +5,7 @@ cmake_minimum_required(VERSION 3.22.1)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
project(${PROJECT_NAME} C CXX)
|
project(${PROJECT_NAME} C CXX)
|
||||||
set(CMAKE_C_STANDARD 17)
|
set(CMAKE_C_STANDARD 17)
|
||||||
|
set(RAYLIB_DIR /usr/local/lib CACHE FILEPATH "directory to Raylib")
|
||||||
|
|
||||||
if (EMSCRIPTEN)
|
if (EMSCRIPTEN)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||||
|
|
|
@ -6,11 +6,19 @@ add_library(engine
|
||||||
target_include_directories(engine
|
target_include_directories(engine
|
||||||
PUBLIC
|
PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${RAYLIB_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_directories(engine
|
||||||
|
PUBLIC
|
||||||
|
${RAYLIB_DIR}/lib
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(engine
|
target_link_libraries(engine
|
||||||
PUBLIC
|
PUBLIC
|
||||||
cc
|
base
|
||||||
|
raylib
|
||||||
|
m
|
||||||
)
|
)
|
||||||
|
|
||||||
#if (BUILD_TESTING)
|
#if (BUILD_TESTING)
|
||||||
|
|
|
@ -74,10 +74,10 @@ void mem_arena_print()
|
||||||
printf("O1heap Memory Arena Info\n");
|
printf("O1heap Memory Arena Info\n");
|
||||||
printf("--------------------\n");
|
printf("--------------------\n");
|
||||||
printf("Capacity: %.3f MB\n", diag.capacity * 1.0 / 1024 / 1024);
|
printf("Capacity: %.3f MB\n", diag.capacity * 1.0 / 1024 / 1024);
|
||||||
printf("Allocated: %lu B\n", diag.allocated);
|
printf("Allocated: %.3f MB\n", diag.allocated * 1.0 /1024/1024);
|
||||||
printf("Peak allocated: %lu B\n", diag.peak_allocated);
|
printf("Peak allocated: %.3f MB\n", diag.peak_allocated * 1.0 /1024/1024);
|
||||||
printf("Peak request: %lu\n", diag.peak_request_size);
|
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)
|
size_t mem_arena_get_allocated(void)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef _ENGINE_CONF_H
|
#ifndef _ENGINE_CONF_H
|
||||||
#define _ENGINE_CONF_H
|
#define _ENGINE_CONF_H
|
||||||
|
|
||||||
|
#define MEMORY_ARENA_SIZE_MB 16
|
||||||
|
|
||||||
// Take care tuning these params. Web build doesn't work
|
// Take care tuning these params. Web build doesn't work
|
||||||
// if memory used too high
|
// if memory used too high
|
||||||
#define MAX_SCENES_TO_RENDER 8
|
#define MAX_SCENES_TO_RENDER 8
|
||||||
|
|
|
@ -1,20 +1,81 @@
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
//#include "cmc/utl/futils.h"
|
#include "engine_conf.h"
|
||||||
/* Function implementation */
|
#include <stdio.h>
|
||||||
//#include "cmc/treeset/code.h"
|
#include <assert.h>
|
||||||
|
|
||||||
void init_memory_system(void) {
|
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(
|
mem_arena_init(MEMORY_ARENA_SIZE_MB);
|
||||||
// &(struct idxSet_fval){ .cmp = cmc_u32_cmp, NULL}
|
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);
|
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) {
|
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);
|
for (uint32_t i = 0; i < mem_impl.n_components; ++i)
|
||||||
|
{
|
||||||
cc_cleanup(&mem_impl.comp_mempools[i].free_set);
|
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>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define CC_NO_SHORT_NAMES
|
#define CC_NO_SHORT_NAMES
|
||||||
#include "cc.h"
|
#include "base.h"
|
||||||
|
|
||||||
typedef struct memoryPool {
|
typedef struct memoryPool {
|
||||||
//struct idxSet* free_set;
|
//struct idxSet* free_set;
|
||||||
|
void * const buffer;
|
||||||
|
const unsigned long capacity;
|
||||||
|
const unsigned long elem_size;
|
||||||
cc_oset( uint32_t ) free_set;
|
cc_oset( uint32_t ) free_set;
|
||||||
} memoryPool;
|
} memoryPool;
|
||||||
|
|
||||||
void init_memory_system(void);
|
void init_memory_system(void);
|
||||||
void free_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 {
|
extern struct memoryImpl {
|
||||||
memoryPool* comp_mempools;
|
memoryPool* comp_mempools;
|
||||||
uint32_t n_components;
|
uint32_t n_components;
|
||||||
} mem_impl;
|
} 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
|
#endif // ENGINE_MEMORY_H
|
||||||
|
|
|
@ -14,3 +14,12 @@ target_link_libraries(ccTest PRIVATE
|
||||||
set_target_properties(ccTest
|
set_target_properties(ccTest
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/manual)
|
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
|
PROPERTIES
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/unit)
|
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 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