lisp: split memory into conses and vectors
During garbage collection, we'll iterate through these two vectors. Instead of freeing the memory, we can swap cells in the vector and decrement its size. The idea is we can attach an allocator to the system where we reuse memory instead of just allocating everytime.
This commit is contained in:
@@ -26,7 +26,8 @@ typedef struct
|
||||
/// System context
|
||||
typedef struct
|
||||
{
|
||||
vec_t memory;
|
||||
vec_t conses;
|
||||
vec_t vectors;
|
||||
sym_table_t symtable;
|
||||
} sys_t;
|
||||
|
||||
|
||||
46
src/lisp.c
46
src/lisp.c
@@ -19,31 +19,57 @@ void sys_init(sys_t *sys)
|
||||
void sys_register(sys_t *sys, lisp_t *ptr)
|
||||
{
|
||||
// Simply append it to the list of currently active conses
|
||||
vec_append(&sys->memory, &ptr, sizeof(&ptr));
|
||||
switch (get_tag(ptr))
|
||||
{
|
||||
case TAG_CONS:
|
||||
vec_append(&sys->conses, &ptr, sizeof(&ptr));
|
||||
break;
|
||||
case TAG_VEC:
|
||||
vec_append(&sys->vectors, &ptr, sizeof(&ptr));
|
||||
break;
|
||||
// Shouldn't be registered
|
||||
case TAG_NIL:
|
||||
case TAG_INT:
|
||||
case TAG_SYM:
|
||||
break;
|
||||
case NUM_TAGS:
|
||||
default:
|
||||
FAIL("Unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
u64 sys_cost(sys_t *sys)
|
||||
{
|
||||
return sym_table_cost(&sys->symtable) + sys->memory.capacity;
|
||||
u64 vec_capacity = 0;
|
||||
for (u64 i = 0; i < VEC_SIZE(&sys->vectors, lisp_t *); ++i)
|
||||
{
|
||||
lisp_t *vec = VEC_GET(&sys->vectors, i, lisp_t *);
|
||||
vec_capacity += as_vec(vec)->capacity;
|
||||
}
|
||||
return sym_table_cost(&sys->symtable) + sys->conses.capacity + vec_capacity;
|
||||
}
|
||||
|
||||
void sys_free(sys_t *sys)
|
||||
{
|
||||
static_assert(NUM_TAGS == 5);
|
||||
|
||||
sym_table_free(&sys->symtable);
|
||||
if (sys->memory.size == 0)
|
||||
return;
|
||||
|
||||
// Iterate through each cell of memory currently allocated and free them
|
||||
for (size_t i = 0; i < VEC_SIZE(&sys->memory, lisp_t **); ++i)
|
||||
for (size_t i = 0; i < VEC_SIZE(&sys->conses, lisp_t **); ++i)
|
||||
{
|
||||
lisp_t *allocated = VEC_GET(&sys->memory, i, lisp_t *);
|
||||
lisp_t *allocated = VEC_GET(&sys->conses, i, lisp_t *);
|
||||
lisp_free(allocated);
|
||||
}
|
||||
|
||||
// Free the container
|
||||
vec_free(&sys->memory);
|
||||
// Iterate through each cell of memory currently allocated and free them
|
||||
for (size_t i = 0; i < VEC_SIZE(&sys->vectors, lisp_t **); ++i)
|
||||
{
|
||||
lisp_t *allocated = VEC_GET(&sys->vectors, i, lisp_t *);
|
||||
lisp_free(allocated);
|
||||
}
|
||||
|
||||
// Free the containers
|
||||
vec_free(&sys->conses);
|
||||
vec_free(&sys->vectors);
|
||||
|
||||
// Ensure no one treats this as active in any sense
|
||||
memset(sys, 0, sizeof(*sys));
|
||||
|
||||
@@ -151,44 +151,42 @@ void sys_test(void)
|
||||
TEST_START();
|
||||
sys_t sys = {0};
|
||||
sys_init(&sys);
|
||||
u64 old_memory_size = sys.memory.size;
|
||||
u64 old_memory_size = sys_cost(&sys);
|
||||
|
||||
// Creating integers doesn't affect memory size
|
||||
(void)make_int(2000);
|
||||
TEST(sys.memory.size == old_memory_size,
|
||||
TEST(sys_cost(&sys) == old_memory_size,
|
||||
"Making integers doesn't affect system memory size");
|
||||
|
||||
// Creating symbols won't affect memory size, but does affect the symbol table
|
||||
// Creating symbols does affect memory size and memory table
|
||||
(void)intern(&sys, SV_AUTO("hello world!"));
|
||||
TEST(sys.memory.size == old_memory_size,
|
||||
TEST(sys_cost(&sys) > old_memory_size,
|
||||
"Interning doesn't affect system memory size");
|
||||
TEST(sys.symtable.count > 0, "Interning affects symbol table");
|
||||
old_memory_size = sys_cost(&sys);
|
||||
|
||||
// Creating conses do affect memory size
|
||||
(void)cons(&sys, make_int(1), make_int(2));
|
||||
TEST(sys.memory.size > 0, "Creating conses affects memory size");
|
||||
old_memory_size = sys.memory.size;
|
||||
TEST(sys_cost(&sys) > 0, "Creating conses affects memory size");
|
||||
old_memory_size = sys_cost(&sys);
|
||||
|
||||
(void)cons(&sys, intern(&sys, SV_AUTO("test")), NIL);
|
||||
TEST(sys.memory.size > old_memory_size,
|
||||
TEST(sys_cost(&sys) > old_memory_size,
|
||||
"Creating conses back to back affects memory size");
|
||||
old_memory_size = sys.memory.size;
|
||||
old_memory_size = sys_cost(&sys);
|
||||
|
||||
// Creating vectors does affect memory size
|
||||
(void)make_vec(&sys, 8);
|
||||
TEST(sys.memory.size > old_memory_size,
|
||||
TEST(sys_cost(&sys) > old_memory_size,
|
||||
"Creating vectors (size 8) affects memory size");
|
||||
old_memory_size = sys.memory.size;
|
||||
old_memory_size = sys_cost(&sys);
|
||||
|
||||
(void)make_vec(&sys, 1000);
|
||||
TEST(sys.memory.size > old_memory_size,
|
||||
TEST(sys_cost(&sys) > old_memory_size,
|
||||
"Creating vectors (size 1000) affects memory size");
|
||||
old_memory_size = sys.memory.size;
|
||||
old_memory_size = sys_cost(&sys);
|
||||
|
||||
sys_free(&sys);
|
||||
TEST(sys.memory.size == 0, "sys_free cleans up memory (shallow check)");
|
||||
TEST(sys.symtable.count == 0, "sys_free cleans up symtable (shallow check)");
|
||||
|
||||
TEST_END();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user