MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

optimize garbage collection cooldown thresholds and allocation thresholds

+107 -23
+1
include/modules/buffer.h
··· 6 6 7 7 void init_buffer_module(void); 8 8 void cleanup_buffer_module(void); 9 + size_t buffer_get_external_memory(void); 9 10 10 11 typedef struct { 11 12 uint8_t *data;
+1
include/modules/collections.h
··· 60 60 61 61 void init_collections_module(void); 62 62 void cleanup_collections_module(void); 63 + size_t collections_get_external_memory(void); 63 64 64 65 void collections_gc_reserve_roots(GC_OP_VAL_ARGS); 65 66 void collections_gc_update_roots(jsoff_t (*fwd_off)(void *ctx, jsoff_t old), GC_OP_VAL_ARGS);
+1
include/runtime.h
··· 22 22 23 23 const char *code_arena_alloc(const char *code, size_t len); 24 24 void code_arena_reset(void); 25 + size_t code_arena_get_memory(void); 25 26 26 27 #endif
+14 -9
src/gc.c
··· 508 508 509 509 size_t js_gc_compact(ant_t *js) { 510 510 if (!js || js->brk == 0) return 0; 511 - if (js->brk < 2 * 1024 * 1024) return 0; 511 + if (js->brk < 512 * 1024) return 0; 512 512 513 513 time_t now = time(NULL); 514 514 if (now != (time_t)-1 && gc_last_run_time != 0) { 515 515 double elapsed = difftime(now, gc_last_run_time); 516 516 double cooldown; 517 - if (js->brk > 128 * 1024 * 1024) cooldown = 2.0; 518 - else if (js->brk > 64 * 1024 * 1024) cooldown = 4.0; 519 - else if (js->brk > 16 * 1024 * 1024) cooldown = 6.0; 520 - else cooldown = 8.0; 521 - if (elapsed >= 0.0 && elapsed < cooldown && js->gc_alloc_since < js->brk) return 0; 517 + 518 + if (js->brk > 64 * 1024 * 1024) cooldown = 0.5; 519 + else if (js->brk > 16 * 1024 * 1024) cooldown = 1.0; 520 + else if (js->brk > 4 * 1024 * 1024) cooldown = 2.0; 521 + else cooldown = 4.0; 522 + 523 + if (elapsed >= 0.0 524 + && elapsed < cooldown 525 + && js->gc_alloc_since < js->brk / 4 526 + ) return 0; 522 527 } 523 528 524 529 mco_coro *running = mco_running(); ··· 618 623 } 619 624 620 625 void js_maybe_gc(ant_t *js) { 621 - jsoff_t thresh = js->brk / 2; 622 - if (thresh < 4 * 1024 * 1024) thresh = 4 * 1024 * 1024; 623 - if (thresh > 64 * 1024 * 1024) thresh = 64 * 1024 * 1024; 626 + jsoff_t thresh = js->brk / 4; 627 + if (thresh < 512 * 1024) thresh = 512 * 1024; 628 + if (thresh > 16 * 1024 * 1024) thresh = 16 * 1024 * 1024; 624 629 625 630 if (js->gc_alloc_since > thresh || js->needs_gc) { 626 631 js->needs_gc = false;
+12
src/modules/buffer.c
··· 1798 1798 1799 1799 ta_arena_offset = 0; 1800 1800 } 1801 + 1802 + size_t buffer_get_external_memory(void) { 1803 + size_t total = ta_arena ? ta_arena_offset : 0; 1804 + 1805 + for (size_t i = 0; i < buffer_registry_count; i++) { 1806 + if (buffer_registry[i]) 1807 + total += sizeof(ArrayBufferData) + buffer_registry[i]->capacity; 1808 + } 1809 + total += buffer_registry_cap * sizeof(ArrayBufferData *); 1810 + 1811 + return total; 1812 + }
+14
src/modules/builtin.c
··· 18 18 #include "runtime.h" 19 19 #include "internal.h" 20 20 #include "modules/builtin.h" 21 + #include "modules/buffer.h" 22 + #include "modules/collections.h" 21 23 22 24 static struct { 23 25 struct js *js; ··· 99 101 100 102 js_set(js, result, "arenaUsed", js_mknum((double)js_getbrk(js))); 101 103 js_set(js, result, "arenaSize", js_mknum((double)js->size)); 104 + 105 + size_t buffer_mem = buffer_get_external_memory(); 106 + size_t code_mem = code_arena_get_memory(); 107 + size_t collections_mem = collections_get_external_memory(); 108 + size_t external_total = buffer_mem + code_mem + collections_mem; 109 + 110 + jsval_t ext = js_newobj(js); 111 + js_set(js, ext, "buffers", js_mknum((double)buffer_mem)); 112 + js_set(js, ext, "code", js_mknum((double)code_mem)); 113 + js_set(js, ext, "collections", js_mknum((double)collections_mem)); 114 + js_set(js, ext, "total", js_mknum((double)external_total)); 115 + js_set(js, result, "external", ext); 102 116 103 117 if (js->cstk != NULL) { 104 118 volatile char marker;
+30
src/modules/collections.c
··· 1102 1102 set_registry_cap = 0; 1103 1103 } 1104 1104 1105 + size_t collections_get_external_memory(void) { 1106 + size_t total = 0; 1107 + 1108 + total += map_registry_cap * sizeof(map_registry_entry_t); 1109 + for (size_t i = 0; i < map_registry_count; i++) { 1110 + total += sizeof(map_entry_t *); 1111 + if (map_registry[i].head) { 1112 + map_entry_t *entry, *tmp; 1113 + HASH_ITER(hh, *map_registry[i].head, entry, tmp) { 1114 + total += sizeof(map_entry_t); 1115 + if (entry->key) total += strlen(entry->key) + 1; 1116 + } 1117 + } 1118 + } 1119 + 1120 + total += set_registry_cap * sizeof(set_registry_entry_t); 1121 + for (size_t i = 0; i < set_registry_count; i++) { 1122 + total += sizeof(set_entry_t *); 1123 + if (set_registry[i].head) { 1124 + set_entry_t *entry, *tmp; 1125 + HASH_ITER(hh, *set_registry[i].head, entry, tmp) { 1126 + total += sizeof(set_entry_t); 1127 + if (entry->key) total += strlen(entry->key) + 1; 1128 + } 1129 + } 1130 + } 1131 + 1132 + return total; 1133 + } 1134 + 1105 1135 #undef CLEANUP_REGISTRY
+7
src/runtime.c
··· 92 92 code_arena_current = NULL; 93 93 } 94 94 95 + size_t code_arena_get_memory(void) { 96 + size_t total = 0; 97 + for (code_block_t *b = code_arena_head; b; b = b->next) 98 + total += sizeof(code_block_t) + b->capacity; 99 + return total; 100 + } 101 + 95 102 struct ant_runtime *ant_runtime_init(ant_t *js, int argc, char **argv, struct arg_file *ls_p) { 96 103 runtime = (struct ant_runtime){ 97 104 .js = js,
+11 -3
src/types/ant.d.ts
··· 53 53 totalBytes: number; 54 54 } 55 55 56 + interface AntExternalMemory { 57 + buffers: number; 58 + code: number; 59 + collections: number; 60 + total: number; 61 + } 62 + 56 63 interface AntStatsResult { 57 64 arenaUsed: number; 65 + arenaSize: number; 66 + external: AntExternalMemory; 58 67 cstack: number; 59 - gcHeapSize: number; 60 - gcFreeBytes: number; 61 - gcUsedBytes: number; 68 + rss?: number; 69 + virtualSize?: number; 62 70 } 63 71 64 72 interface AntRaw {
+16 -11
tests/bench_gc.js
··· 77 77 78 78 console.log('=== GC Benchmark ===\n'); 79 79 80 - let initial = Ant.stats(); 81 - console.log('Initial state:'); 82 - console.log(' arenaUsed:', fmt(initial.arenaUsed)); 83 - console.log(' rss:', fmt(initial.rss)); 84 - console.log(''); 80 + function showStats(label, s) { 81 + console.log(`${label}:`); 82 + console.log(` arenaUsed: ${fmt(s.arenaUsed)}`); 83 + console.log(` arenaSize: ${fmt(s.arenaSize)}`); 84 + console.log(` external.buffers: ${fmt(s.external.buffers)}`); 85 + console.log(` external.code: ${fmt(s.external.code)}`); 86 + console.log(` external.collections: ${fmt(s.external.collections)}`); 87 + console.log(` external.total: ${fmt(s.external.total)}`); 88 + console.log(` cstack: ${fmt(s.cstack)}`); 89 + console.log(` rss: ${fmt(s.rss)}`); 90 + console.log(''); 91 + } 92 + 93 + showStats('Initial state', Ant.stats()); 85 94 86 95 bench('Many small objects (1k objects)', manySmallObjects, 5); 87 96 bench('Deep object graph (1000 levels)', deepObjectGraph, 5); ··· 89 98 bench('String array (5000 strings)', stringArray, 5); 90 99 bench('Mixed workload', mixedWorkload, 5); 91 100 92 - let final = Ant.stats(); 93 - console.log('Final state:'); 94 - console.log(' arenaUsed:', fmt(final.arenaUsed)); 95 - console.log(' arenaSize:', fmt(final.arenaSize)); 96 - console.log(' rss:', fmt(final.rss)); 97 - console.log(''); 101 + showStats('Final state', Ant.stats()); 102 + 98 103 console.log('Note: GC runs automatically at safe points.'); 99 104 console.log('Use Ant.gc() as a hint to request collection.');