From 7c5d2f6b65cdc1da60845ff81c2f0ac36e4041e4 Mon Sep 17 00:00:00 2001 From: CPunch Date: Fri, 25 Aug 2023 19:57:16 -0500 Subject: [PATCH] minor CTable refactoring --- src/cmem.c | 23 +++++++++++++++++++---- src/cmem.h | 5 ++++- src/cobj.c | 14 +++++++------- src/ctable.c | 35 ++++++++++++++++++----------------- src/ctable.h | 3 +++ src/cvm.c | 6 ++++-- 6 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/cmem.c b/src/cmem.c index 4e039c7..efab5fc 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -10,6 +10,17 @@ // realloc wrapper void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize) { +#ifdef GC_DEBUG + if (buf) { + if (newSize == 0) { + printf("freeing %p, reclaiming %ld bytes...\n", buf, oldSize); + } else { + printf("realloc %p, byte difference: %ld\n", buf, newSize - oldSize); + } + } else { + printf("allocating new buffer of size %ld\n", newSize - oldSize); + } +#endif state->allocatedBytes += newSize - oldSize; if (newSize == 0) { // it needs to be freed @@ -59,7 +70,7 @@ static void markTable(CState *state, CTable *tbl) if (tbl->table == NULL) // table is still being initialized return; - int cap = tbl->capacityMask + 1; + int cap = cosmoT_getCapacity(tbl); for (int i = 0; i < cap; i++) { CTableEntry *entry = &tbl->table[i]; markValue(state, entry->key); @@ -67,13 +78,18 @@ static void markTable(CState *state, CTable *tbl) } } -// frees white members from the table +// removes white members from the table static void tableRemoveWhite(CState *state, CTable *tbl) { if (tbl->table == NULL) // table is still being initialized return; - int cap = tbl->capacityMask + 1; + int cap = cosmoT_getCapacity(tbl); + +#ifdef GC_DEBUG + printf("tableRemoveWhite: %p, cap: %d\n", tbl, cap); +#endif + for (int i = 0; i < cap; i++) { CTableEntry *entry = &tbl->table[i]; if (IS_REF(entry->key) && @@ -295,7 +311,6 @@ COSMO_API void cosmoM_collectGarbage(CState *state) printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection " "scheduled at %ld bytes\n", start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); - getchar(); // pauses execution #endif } diff --git a/src/cmem.h b/src/cmem.h index 4725114..c00aa30 100644 --- a/src/cmem.h +++ b/src/cmem.h @@ -73,10 +73,13 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot); // lets the VM know this root is no longer held in a reference and is able to be freed COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot); -// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P) +// wrapper for cosmoM_reallocate so we can track our memory usage static inline void *cosmoM_xmalloc(CState *state, size_t sz) { return cosmoM_reallocate(state, NULL, 0, sz); } +// #define cosmoM_xmalloc(state, sz) \ +// (printf("allocating new buffer at %s:%d of size %ld\n", __FILE__, __LINE__, sz), cosmoM_reallocate(state, NULL, 0, sz)) + #endif diff --git a/src/cobj.c b/src/cobj.c index 2321f4d..7ec281e 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -409,9 +409,9 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v val)) { // if the field doesn't exist in the object, check the proto if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) { - cosmoV_pushValue(state, *val); // push function - cosmoV_pushRef(state, (CObj *)obj); // push object - if (!cosmoV_call(state, 1, 1)) // call the function with the 1 argument + cosmoV_pushValue(state, *val); // push function + cosmoV_pushRef(state, (CObj *)obj); // push object + if (!cosmoV_call(state, 1, 1)) // call the function with the 1 argument return false; *val = *cosmoV_pop(state); // set value to the return value of __index return true; @@ -529,10 +529,10 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) { if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) { - cosmoV_pushValue(state, *val); // push function - cosmoV_pushRef(state, (CObj *)object); // push object - cosmoV_pushValue(state, key); // push key - if (!cosmoV_call(state, 2, 1)) // call the function with the 2 arguments + cosmoV_pushValue(state, *val); // push function + cosmoV_pushRef(state, (CObj *)object); // push object + cosmoV_pushValue(state, key); // push key + if (!cosmoV_call(state, 2, 1)) // call the function with the 2 arguments return false; *val = *cosmoV_pop(state); // set value to the return value of __index return true; diff --git a/src/ctable.c b/src/ctable.c index f9008bb..8c9a0ff 100644 --- a/src/ctable.c +++ b/src/ctable.c @@ -34,6 +34,7 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap) tbl->capacityMask = startCap - 1; tbl->count = 0; tbl->tombstones = 0; + tbl->tombThreshold = 32; tbl->table = NULL; // to let out GC know we're initalizing tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap); @@ -47,7 +48,7 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap) void cosmoT_addTable(CState *state, CTable *from, CTable *to) { CTableEntry *entry; - int cap = from->capacityMask + 1; + int cap = cosmoT_getCapacity(from); for (int i = 0; i < cap; i++) { entry = &from->table[i]; @@ -60,7 +61,7 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to) void cosmoT_clearTable(CState *state, CTable *tbl) { - cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1)); + cosmoM_freearray(state, CTableEntry, tbl->table, cosmoT_getCapacity(tbl)); } static uint32_t getObjectHash(CObj *obj) @@ -113,10 +114,10 @@ static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CVa if (IS_NIL(entry->val)) { // it's empty! if we found a tombstone, return that so it'll be reused return tomb != NULL ? tomb : entry; - } else { - // its a tombstone! - tomb = entry; } + + // its a tombstone! + tomb = entry; } else if (cosmoV_equal(state, entry->key, key)) { return entry; } @@ -141,7 +142,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin return; CTableEntry *entries = cosmoM_xmalloc(state, size); - oldCap = tbl->capacityMask + 1; + oldCap = cosmoT_getCapacity(tbl); newCount = 0; // set all nodes as NIL : NIL @@ -176,10 +177,10 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) { // if count > 8 and active entries < tombstones if (tbl->count > MIN_TABLE_CAPACITY && - (tbl->count - tbl->tombstones < tbl->tombstones || - tbl->tombstones > 50)) { // TODO: 50 should be a threshhold - resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, - false); // shrink based on active entries to the next pow of 2 + (tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > tbl->tombThreshold)) { + // shrink based on active entries to the next pow of 2 + resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); + tbl->tombThreshold = tbl->count / 4; return true; } @@ -190,7 +191,7 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key) { // make sure we have enough space allocated - int cap = tbl->capacityMask + 1; + int cap = cosmoT_getCapacity(tbl); if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) { // grow table int newCap = cap * GROW_FACTOR; @@ -198,8 +199,7 @@ COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key) } // insert into the table - CTableEntry *entry = - findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask + CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); if (IS_NIL(entry->key)) { if (IS_NIL(entry->val)) // is it empty? @@ -255,9 +255,10 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32 { if (tbl->count == 0) return 0; // sanity check - uint32_t indx = - hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we - // can use bitwise & to perform a MUCH faster mod operation + + // since we know the capacity will *always* be a power of 2, we + // can use bitwise & to perform a MUCH faster mod operation + uint32_t indx = hash & tbl->capacityMask; // keep looking for an open slot in the entries array while (true) { @@ -280,7 +281,7 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32 void cosmoT_printTable(CTable *tbl, const char *name) { printf("==== [[%s]] ====\n", name); - int cap = tbl->capacityMask + 1; + int cap = cosmoT_getCapacity(tbl); for (int i = 0; i < cap; i++) { CTableEntry *entry = &tbl->table[i]; if (!(IS_NIL(entry->key))) { diff --git a/src/ctable.h b/src/ctable.h index 157cffb..ce57214 100644 --- a/src/ctable.h +++ b/src/ctable.h @@ -18,9 +18,12 @@ typedef struct CTable int count; int capacityMask; // +1 to get the capacity int tombstones; + int tombThreshold; CTableEntry *table; } CTable; +#define cosmoT_getCapacity(tbl) ((tbl)->capacityMask + 1) + COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap); COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl); COSMO_API int cosmoT_count(CTable *tbl); diff --git a/src/cvm.c b/src/cvm.c index 1e5ac96..88f9b4b 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -640,7 +640,7 @@ int _tbl__next(CState *state, int nargs, CValue *args) CObjTable *table = (CObjTable *)cosmoV_readRef(val); // while the entry is invalid, go to the next entry - int cap = table->tbl.capacityMask + 1; + int cap = cosmoT_getCapacity(&table->tbl); CTableEntry *entry; do { entry = &table->tbl.table[index++]; @@ -1037,7 +1037,9 @@ int cosmoV_execute(CState *state) cosmoV_pop(state); // pop the object from the stack cosmoV_pushValue(state, val); cosmoV_pushRef(state, (CObj *)obj); - if (!cosmoV_call(state, 1, 1)) // we expect 1 return value on the stack, the iterable object + if (!cosmoV_call( + state, 1, + 1)) // we expect 1 return value on the stack, the iterable object return -1; StkPtr iObj = cosmoV_getTop(state, 0);