js-on-sx: harness cache — precompute HARNESS_STUB SX once per run
Root cause: every sx_server worker session used js-eval on the 3.6KB HARNESS_STUB, paying ~15s for tokenize+parse+transpile even though every session does the same thing. Over a full scoreboard with periodic worker restarts that's minutes of wasted work. Fix: transpile once per Python process. Spin up a throwaway sx_server, run (inspect (js-transpile (js-parse (js-tokenize HARNESS_STUB)))), write the resulting SX source to lib/js/.harness-cache/stub.<fingerprint>.sx and a stable-name symlink-ish copy stub.sx. Every worker session then does a single (load .harness-cache/stub.sx) instead of re-running js-eval. Fingerprint: sha256(HARNESS_STUB + lexer.sx + parser.sx + transpile.sx). Transpiler edits invalidate the cache automatically. Runs back-to-back reuse the cache — only the first run after a transpiler change pays the ~15s precompute. Transpile had to gain a $-to-_js_dollar_ name-mangler: the SX tokenizer rejects $ in identifiers, which broke round-tripping via inspect. JS $DONOTEVALUATE → SX _js_dollar_DONOTEVALUATE. Internal JS-on-SX names are unaffected (none contain $). Measured: 300-test wide (Math+Number+String @ 100/cat, --per-test-timeout 5): 593.7s → 288.0s, 2.06x speedup. Scoreboard 114→115/300 (38.3%, noise band). Math 40%, Number 44%, String 30% — same shape as prior. Baselines: 520/522 unit, 148/148 slice — unchanged.
This commit is contained in:
@@ -23,7 +23,46 @@
|
||||
|
||||
;; ── tiny helpers ──────────────────────────────────────────────────
|
||||
|
||||
(define js-sym (fn (name) (make-symbol name)))
|
||||
(define js-has-dollar? (fn (name) (js-has-dollar-loop? name 0 (len name))))
|
||||
(define
|
||||
js-has-dollar-loop?
|
||||
(fn
|
||||
(s i n)
|
||||
(cond
|
||||
((>= i n) false)
|
||||
((= (char-at s i) "$") true)
|
||||
(else (js-has-dollar-loop? s (+ i 1) n)))))
|
||||
|
||||
(define
|
||||
js-mangle-ident
|
||||
(fn
|
||||
(name)
|
||||
(if
|
||||
(js-has-dollar? name)
|
||||
(js-mangle-ident-loop name 0 (len name) "")
|
||||
name)))
|
||||
|
||||
;; ── main dispatcher ───────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-mangle-ident-loop
|
||||
(fn
|
||||
(s i n acc)
|
||||
(cond
|
||||
((>= i n) acc)
|
||||
((= (char-at s i) "$")
|
||||
(js-mangle-ident-loop s (+ i 1) n (str acc "_js_dollar_")))
|
||||
(else (js-mangle-ident-loop s (+ i 1) n (str acc (char-at s i)))))))
|
||||
|
||||
;; ── Identifier lookup ─────────────────────────────────────────────
|
||||
|
||||
;; `undefined` in JS is really a global binding. If the parser emits
|
||||
;; (js-undef) we handle that above. A bare `undefined` ident also maps
|
||||
;; to the same sentinel.
|
||||
(define js-sym (fn (name) (make-symbol (js-mangle-ident name))))
|
||||
|
||||
;; ── Unary ops ─────────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-tag?
|
||||
(fn
|
||||
@@ -34,9 +73,11 @@
|
||||
(= (type-of (first ast)) "symbol")
|
||||
(= (symbol-name (first ast)) tag))))
|
||||
|
||||
;; ── Binary ops ────────────────────────────────────────────────────
|
||||
|
||||
(define js-ast-tag (fn (ast) (symbol-name (first ast))))
|
||||
|
||||
;; ── main dispatcher ───────────────────────────────────────────────
|
||||
;; ── Member / index ────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-transpile
|
||||
@@ -146,11 +187,6 @@
|
||||
(else
|
||||
(error (str "js-transpile: unexpected value type: " (type-of ast)))))))
|
||||
|
||||
;; ── Identifier lookup ─────────────────────────────────────────────
|
||||
|
||||
;; `undefined` in JS is really a global binding. If the parser emits
|
||||
;; (js-undef) we handle that above. A bare `undefined` ident also maps
|
||||
;; to the same sentinel.
|
||||
(define
|
||||
js-transpile-ident
|
||||
(fn
|
||||
@@ -164,8 +200,10 @@
|
||||
((= name "Function") (js-sym "js-function-global"))
|
||||
(else (js-sym name)))))
|
||||
|
||||
;; ── Unary ops ─────────────────────────────────────────────────────
|
||||
;; ── Call ──────────────────────────────────────────────────────────
|
||||
|
||||
;; JS `f(a, b, c)` → `(f a b c)` after transpile. Works for both
|
||||
;; identifier calls and computed callee (arrow fn, member access).
|
||||
(define
|
||||
js-transpile-unop
|
||||
(fn
|
||||
@@ -196,7 +234,7 @@
|
||||
((= op "void") (list (js-sym "quote") :js-undefined))
|
||||
(else (error (str "js-transpile-unop: unsupported op: " op)))))))))
|
||||
|
||||
;; ── Binary ops ────────────────────────────────────────────────────
|
||||
;; ── Array literal ─────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-transpile-binop
|
||||
@@ -259,22 +297,25 @@
|
||||
(js-sym "_a"))))
|
||||
(else (error (str "js-transpile-binop: unsupported op: " op))))))
|
||||
|
||||
;; ── Member / index ────────────────────────────────────────────────
|
||||
;; ── Object literal ────────────────────────────────────────────────
|
||||
|
||||
;; Build a dict by `(dict)` + `dict-set!` inside a `let` that yields
|
||||
;; the dict as its final expression. This keeps keys in JS insertion
|
||||
;; order and allows computed values.
|
||||
(define
|
||||
js-transpile-member
|
||||
(fn (obj key) (list (js-sym "js-get-prop") (js-transpile obj) key)))
|
||||
|
||||
;; ── Conditional ───────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-transpile-index
|
||||
(fn
|
||||
(obj idx)
|
||||
(list (js-sym "js-get-prop") (js-transpile obj) (js-transpile idx))))
|
||||
|
||||
;; ── Call ──────────────────────────────────────────────────────────
|
||||
;; ── Arrow function ────────────────────────────────────────────────
|
||||
|
||||
;; JS `f(a, b, c)` → `(f a b c)` after transpile. Works for both
|
||||
;; identifier calls and computed callee (arrow fn, member access).
|
||||
(define
|
||||
js-transpile-call
|
||||
(fn
|
||||
@@ -320,8 +361,11 @@
|
||||
(js-transpile callee)
|
||||
(js-transpile-args args))))))
|
||||
|
||||
;; ── Array literal ─────────────────────────────────────────────────
|
||||
;; ── Assignment ────────────────────────────────────────────────────
|
||||
|
||||
;; `a = b` on an ident → (set! a b).
|
||||
;; `a += b` on an ident → (set! a (js-add a b)).
|
||||
;; `obj.k = v` / `obj[k] = v` → (js-set-prop obj "k" v).
|
||||
(define
|
||||
js-transpile-new
|
||||
(fn
|
||||
@@ -331,11 +375,6 @@
|
||||
(js-transpile callee)
|
||||
(cons (js-sym "list") (map js-transpile args)))))
|
||||
|
||||
;; ── Object literal ────────────────────────────────────────────────
|
||||
|
||||
;; Build a dict by `(dict)` + `dict-set!` inside a `let` that yields
|
||||
;; the dict as its final expression. This keeps keys in JS insertion
|
||||
;; order and allows computed values.
|
||||
(define
|
||||
js-transpile-array
|
||||
(fn
|
||||
@@ -354,8 +393,6 @@
|
||||
elts))
|
||||
(cons (js-sym "list") (map js-transpile elts)))))
|
||||
|
||||
;; ── Conditional ───────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
js-has-spread?
|
||||
(fn
|
||||
@@ -365,8 +402,9 @@
|
||||
((js-tag? (first lst) "js-spread") true)
|
||||
(else (js-has-spread? (rest lst))))))
|
||||
|
||||
;; ── Arrow function ────────────────────────────────────────────────
|
||||
;; ── End-to-end entry points ───────────────────────────────────────
|
||||
|
||||
;; Transpile + eval a single JS expression string.
|
||||
(define
|
||||
js-transpile-args
|
||||
(fn
|
||||
@@ -385,11 +423,8 @@
|
||||
args))
|
||||
(cons (js-sym "list") (map js-transpile args)))))
|
||||
|
||||
;; ── Assignment ────────────────────────────────────────────────────
|
||||
|
||||
;; `a = b` on an ident → (set! a b).
|
||||
;; `a += b` on an ident → (set! a (js-add a b)).
|
||||
;; `obj.k = v` / `obj[k] = v` → (js-set-prop obj "k" v).
|
||||
;; Transpile a JS expression string to SX source text (for inspection
|
||||
;; in tests). Useful for asserting the exact emitted tree.
|
||||
(define
|
||||
js-transpile-object
|
||||
(fn
|
||||
@@ -451,9 +486,6 @@
|
||||
(append inits (list (js-transpile body))))))))
|
||||
(list (js-sym "fn") param-syms body-tr))))
|
||||
|
||||
;; ── End-to-end entry points ───────────────────────────────────────
|
||||
|
||||
;; Transpile + eval a single JS expression string.
|
||||
(define
|
||||
js-transpile-tpl
|
||||
(fn
|
||||
@@ -465,8 +497,6 @@
|
||||
(else
|
||||
(cons (js-sym "js-template-concat") (js-transpile-tpl-parts parts))))))
|
||||
|
||||
;; Transpile a JS expression string to SX source text (for inspection
|
||||
;; in tests). Useful for asserting the exact emitted tree.
|
||||
(define
|
||||
js-transpile-tpl-parts
|
||||
(fn
|
||||
|
||||
Reference in New Issue
Block a user