# Hand-off: enable serving-mode JIT for ~3–4× request CPU > From the **sx-vm-extensions** loop (2026-06-28). The serving-mode JIT is merged > to `architecture` and is the host's real perf win — it just needs switching on. > No further engine work is required from your side. ## TL;DR Run the host server on the merged `architecture` binary with **`SX_SERVING_JIT=1`** in its environment. Expected: **~3–4× lower per-request CPU** (measured ~9 ms → ~2.7 ms on the `/feed` pipeline). Already verified correct: full host conformance is **181/181 under `SX_SERVING_JIT=1`**. ## What changed (already merged to architecture) The bytecode JIT now works in the persistent/epoch serving mode, **opt-in via the `SX_SERVING_JIT` env var (default OFF)**. Default-off means zero change until you opt in — nothing regressed for any loop. Merge commit on `architecture`: `089ed88f` (rebuild the shared binary from architecture to pick it up). The JIT is safe for the host's request pipeline because: - The pipeline (dream router + feed/relations/blog handlers + JSON + render-to-html) is pure SX with **no `call/cc`**; the only continuation-style code is `guard` (Dream's `dream-catch-with` / `wrap-errors`), which the JIT **auto-detects and runs interpreted** (recursive `PUSH_HANDLER` scan). So error handling stays correct; everything else JITs. - Proven end-to-end: combined host+JIT binary, full conformance under `SX_SERVING_JIT=1` = **181/181, all 10 suites green** (handler 14, middleware 9, sxtp 39, router 6, feed 14, relations 22, blog 27, page 8, server 13, ledger 29). ## How to enable 1. Rebuild the shared binary from `architecture` (it carries the merge): `cd hosts/ocaml && dune build bin/sx_server.exe` 2. Launch the host server process with `SX_SERVING_JIT=1` set in its environment (whatever wrapper/serve path you use — `lib/host/serve.sx` / the http-listen entry). Default-off means you must set it explicitly. 3. One-time cost: JIT compiles hot functions on first call (~+1 s at startup / first requests). Amortized immediately for a long-lived server. ## Measurements (this is the evidence) In-process, full request pipeline (`host/native-handler (host/make-app …)` → `/feed`, 2000 requests, in-memory persist backend): | | per-request CPU | total 2000 reqs | |---|---|---| | CEK (default, no JIT) | ~9 ms | ~15–20 s | | **JIT (`SX_SERVING_JIT=1`)** | **~2.7 ms** | **~5–6 s** | JIT is also markedly *less* variable run-to-run. The cost is the pipeline (routing + feed normalize/stream + handler + JSON), not rendering — `render-to-html` alone is only ~50 µs/render and is already fast. ## What was ruled out (don't chase these) The original kickoff framed the slowness as "interpreted Smalltalk (`content/html`) in ~2 s". **The host does not load `lib/smalltalk` or `lib/content`** — that was a different subsystem. We measured and confirmed: - The host's render path is `render-to-html` (SX markup → HTML), already fast. - The proposed big engine projects — **VM continuation-escape** and a **compile-to-closures Smalltalk interpreter** — would *not* help the host (wrong subsystem) and are **not needed**. (Scoping kept in the vm-extensions loop under `plans/vm-continuation-escape.md` / `plans/smalltalk-dispatch-perf.md` if a Smalltalk-backed workload ever needs them.) ## Caveat — this is CPU only The ~3–4× is the in-process CPU path (which JIT controls). It does **not** touch network/IO latency. If your production TTFB is dominated by a non-in-memory `persist` backend, cross-service fetches, TLS/connection setup, or the known homepage SSR-stepper issue, profile those separately — JIT won't move them. To find your real split, break a live TTFB into: request parse → route → handler (+ persist read) → render → serialize → network. The in-memory measurement above says the *code path* is ~2.7 ms under JIT; anything beyond that in production is infrastructure, not the SX engine. ## One known residual (not host-affecting, for awareness) The serving hook re-runs a JIT'd function on the CEK if it fails mid-execution (correct result, but could duplicate side effects for an impure function that fails mid-run). The host conformance is clean (181/181), so nothing triggers it on your paths today. The clean general fix (propagate-don't-rerun) is deferred in the vm-extensions loop. ## Correction (host loop, 2026-06-28) The premise above ("~2s interpreted-Smalltalk render") is STALE: the blog moved off content-on-sx Smalltalk to `render-to-html` long ago (render-page ~2ms). The actual post-page unresponsiveness was NOT CPU/render — it was the DURABLE READ COUNT: host/blog--relation-blocks did ~7 `kv-keys` performs per page (each host/blog-out/in re-scanned the KV). Collapsing to one shared kv-keys read fixed it (~1s -> ~0.02s; commit 0a2f1a61). So serving-JIT was NOT the fix here. Serving-JIT may still be a worthwhile general speedup (the ~3-4× CPU claim, and the Datalog `instances-of` on /tags is CPU-bound), but it requires running the host on the merged `architecture` binary — this worktree's binary has no SX_SERVING_JIT gate. Treat it as an optional future win, not the perf blocker.