faster gc

This commit is contained in:
2026-02-14 16:46:11 -06:00
parent e75596ce30
commit 5fe05c60d3
13 changed files with 7566 additions and 7521 deletions

View File

@@ -1678,6 +1678,10 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
/* Update context with new block */
size_t new_used = to_free - to_base;
/* Update GC stats */
ctx->gc_count++;
ctx->gc_bytes_copied += new_used;
size_t recovered = old_used > new_used ? old_used - new_used : 0;
ctx->heap_base = to_base;
@@ -1697,19 +1701,23 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
}
#endif
/* If <20% recovered, double next block size for future allocations
But only if allow_grow is set (i.e., GC was triggered due to low space) */
/* If <40% recovered, grow next block size for future allocations.
First poor recovery: double. Consecutive poor: quadruple. */
#ifdef DUMP_GC
int will_grow = 0;
#endif
if (allow_grow && recovered > 0 && old_used > 0 && recovered < old_used / 5) {
size_t doubled = new_size * 2;
if (doubled <= buddy_max_block(&ctx->rt->buddy)) {
ctx->next_block_size = doubled;
if (allow_grow && recovered > 0 && old_used > 0 && recovered < old_used * 2 / 5) {
size_t factor = ctx->gc_poor_streak >= 1 ? 4 : 2;
size_t grown = new_size * factor;
if (grown <= buddy_max_block(&ctx->rt->buddy)) {
ctx->next_block_size = grown;
#ifdef DUMP_GC
will_grow = 1;
#endif
}
ctx->gc_poor_streak++;
} else {
ctx->gc_poor_streak = 0;
}
#ifdef DUMP_GC
@@ -1855,6 +1863,20 @@ JSContext *JS_NewContextRawWithHeapSize (JSRuntime *rt, size_t heap_size) {
/* Initialize per-context execution state (moved from JSRuntime) */
ctx->current_exception = JS_NULL;
/* Initialize constant text pool (avoids overflow pages for common case) */
{
size_t ct_pool_size = 64 * 1024; /* 64KB initial CT pool */
ctx->ct_base = js_malloc_rt (ct_pool_size);
if (!ctx->ct_base) {
js_free_rt (ctx->class_array);
js_free_rt (ctx->class_proto);
js_free_rt (ctx);
return NULL;
}
ctx->ct_free = ctx->ct_base;
ctx->ct_end = ctx->ct_base + ct_pool_size;
}
/* Initialize constant text intern table */
ctx->ct_pages = NULL;
ctx->ct_array = NULL;
@@ -1944,6 +1966,7 @@ void JS_FreeContext (JSContext *ctx) {
/* Free constant text pool and intern table */
ct_free_all (ctx);
if (ctx->ct_base) js_free_rt (ctx->ct_base);
js_free_rt (ctx->ct_hash);
js_free_rt (ctx->ct_array);
@@ -9757,6 +9780,22 @@ static JSValue js_mach_dump_mcode (JSContext *ctx, JSValue this_val, int argc, J
return JS_NULL;
}
/* gc_stats() — return {count, bytes_copied, heap_size, ct_pages} and reset counters */
static JSValue js_gc_stats (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
JSValue obj = JS_NewObject (ctx);
if (JS_IsException (obj)) return obj;
JS_SetPropertyStr (ctx, obj, "count", JS_NewInt64 (ctx, (int64_t)ctx->gc_count));
JS_SetPropertyStr (ctx, obj, "bytes_copied", JS_NewInt64 (ctx, (int64_t)ctx->gc_bytes_copied));
JS_SetPropertyStr (ctx, obj, "heap_size", JS_NewInt64 (ctx, (int64_t)ctx->current_block_size));
/* Count CT overflow pages */
int ct_page_count = 0;
for (CTPage *p = (CTPage *)ctx->ct_pages; p; p = p->next) ct_page_count++;
JS_SetPropertyStr (ctx, obj, "ct_pages", JS_NewInt32 (ctx, ct_page_count));
ctx->gc_count = 0;
ctx->gc_bytes_copied = 0;
return obj;
}
/* mach_compile_mcode_bin(name, mcode_json) - compile mcode IR to serialized binary blob */
static JSValue js_mach_compile_mcode_bin (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
@@ -10849,6 +10888,7 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
js_set_global_cfunc(ctx, "mach_eval_mcode", js_mach_eval_mcode, 3);
js_set_global_cfunc(ctx, "mach_dump_mcode", js_mach_dump_mcode, 3);
js_set_global_cfunc(ctx, "mach_compile_mcode_bin", js_mach_compile_mcode_bin, 2);
js_set_global_cfunc(ctx, "gc_stats", js_gc_stats, 0);
js_set_global_cfunc(ctx, "stone", js_cell_stone, 1);
js_set_global_cfunc(ctx, "length", js_cell_length, 1);
js_set_global_cfunc(ctx, "call", js_cell_call, 3);