perf: 81% RSS reduction — el_release, arena scoping, streaming codegen, libcurl stub

Chain of optimizations from swarm rounds 4-7:
- Flat stride-2 token list: eliminate per-token Map allocation (~112B each × N tokens)
- Systematic el_release() in parser.el: eagerly free intermediate parse result maps
- Per-function and per-statement arena scoping in codegen_streaming()
- Streaming codegen pipeline: parse one fn at a time, emit C, discard AST
- HAVE_CURL guard: elc CLI binary drops libcurl, eliminating SSL/TLS init overhead
- HTML codegen parts-list: O(n) instead of O(n²) string growth for nested templates
- Batch c_escape: str_slice clean runs instead of char-at per byte

Result: 33.4MB → 6.5MB RSS on web/src/main.el (-81%). Self-host: PASS.
This commit is contained in:
Will Anderson
2026-05-05 20:39:38 -05:00
parent ee86736eab
commit 3726f69435
5 changed files with 859 additions and 42 deletions
+7 -5
View File
@@ -20,13 +20,15 @@ import "codegen.el"
import "codegen-js.el"
// compile full pipeline (C target): source string -> C source string
// Uses JIT function-at-a-time streaming: parse one decl emit C discard AST.
// Peak memory is O(one function's AST) instead of O(whole program AST).
fn compile(source: String) -> String {
let tokens: [Any] = lex(source)
let stmts: [Map<String, Any>] = parse(tokens)
// Token list is no longer needed after parsing release it to free memory
// before codegen allocates its own working data on large source files.
el_release(tokens)
codegen(stmts, source)
// Fast pre-scan: collect fn signatures + program kind without building
// full expression ASTs. O(tokens) time, minimal allocation.
let sigs: [Map<String, Any>] = scan_fn_sigs(tokens)
// Stream parse-emit: parse one decl at a time, emit C, discard.
codegen_streaming(tokens, sigs, source)
}
// compile_js full pipeline (JS target, module mode): source string -> JS source string