Repository navigation
Race: a fourth lane, Lean's own interpreter on QED64 (wasm64), and pluggable lanes - #8
Merged
Merged
Conversation
The lanes move to a registry (bench/race/lanes.mjs), so a lane can be added as a module of its own, optional or asynchronous. Snapshot of work in progress; not yet tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
bench/race/lanes.mjs is the registry of lanes (data only): key, name, the lane its ratio is against, what it needs, whether it is optional (off unless asked for, with ?lanes= or a box under the race) or asynchronous (its app answers from a Worker: the referee awaits window.__idle() after each click, and nothing else is clicked meanwhile). A lane's page may report its own figures (window.__lane: downloads, start phases, Lean / transport / DOM time), which the panels, the results and window.__race show. The three current lanes behave and measure as before; tests/browser/race.mjs takes --lanes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
Snapshot of stage 1 in progress: the lock of QED64's runtime, Init pack, Std digests and Node 24; the verified fetcher; the LeanUI shim for Lean's interpreter; the Node host for lean.wasm. Not yet tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
Snapshot of stage 1 in progress: LeanUIQed/Host.lean (events on stdin, update buffers on stdout), run/AppsRun.lean, the native reference build (lakefile), and tools/bake.sh. Not yet tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
Snapshot of stage 1 in progress. Not yet tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
qed64/ runs the race's apps (examples/Bench.lean, examples/Life.lean) in
Lean's own IR interpreter inside QED64's wasm64 build of Lean 4.35
(lean.wasm), as a module of its own like vir/ (qed64/README.md).
- qed64.lock.json pins every download by size and sha256: the runtime's
chunks, QED64's Init pack, the digests of QED64's build of the 8 Std
modules, the licences and Node 24. tools/fetch.mjs downloads and verifies
them into qed64/.cache (or $LEANUI_QED64_CACHE); tools/node.sh fetches
the pinned Node.
- tools/shim.py writes LeanUI's core for the interpreter: the two inline-C
helpers as Lean definitions and the HashMap import narrowed to
Std.Data.HashMap.Basic; it refuses unless each edit applies once.
- LeanUIQed/Host.lean (with the p ping) and run/AppsRun.lean; lakefile.toml
builds the compiled reference hosts against LeanUI as it is.
- tools/wasm-lean.mjs hosts lean.wasm under Node: run (one lean command),
make (compiles in dependency order, in parallel) and lane (a session as
the page boots one). lane/core.js holds what the browser Worker will
share: the command line, the stdin ring, the reply parser.
- tools/bake.sh compiles Std (every pinned facet must be QED64's) and
LeanUI, the apps and the host, and bakes the header snapshot from Init's
and Std's .olean files alone (with every facet it would be 352 MB, not
114 MB); the .deps sidecar, gzip, lean.wasm's .br and .gz; STAMPs skip
what has not changed. build.sh assembles dist/qed64/{rt,bench,life}/.
- test/node.mjs: every update byte-identical to the compiled reference in
8 scenarios, 20 pings, create 10,000 rows with 0.49 MiB pthread stacks.
Std's .olean.private is not compared: lean.wasm writes it a few hundred
bytes apart from the native build QED64's packs come from, and only
`import all` reads it; the other four facets of all 8 modules match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
…ages
qed64/lane/qed64.worker.js runs lean.wasm in a classic Worker, Emscripten's
main runtime thread, as startLane does in Node: its own shared memory, the
snapshot fetched and unpacked with DecompressionStream while lean.wasm
compiles, the .deps sidecar and the run file in MEMFS, the stdin ring, the
per-byte stdout tap, the exit hook, the 1 s mailbox kick, and the snapshot's
MEMFS copy deleted after the mount. Each reply carries Lean's time and the
Worker's, and a reply held up by more than 500 ms counts as a stall.
qed64/lane/leanui-qed64.js is the page side: QedTransport, asynchronous like
RemoteTransport, with idle(), and LeanUIQed64.start, which shows the start's
progress, takes the floor (20 pings) and gives the race what lanes.mjs asks
for: bytes (the Worker's downloads, 42.0 MB), phases and stats(). If Lean
stops, idle() rejects, the container gets leanui:error and the Worker is
terminated. lane/core.js gains the message framing.
qed64/pages/{bench,life}.html are vir's pages for this lane, with an icon
link. build.sh assembles dist/qed64/{bench,life}/ (index.html, leanui.js,
leanui-qed64.js, qed64.json with the hashed worker) and the two stylesheets
one level up, as in the race.
Checked by hand in Playwright's Chromium 141 and Firefox 142, served --coop:
Life after 100 steps shows board bdbe4aa6 with 53 alive; Bench's create 1k,
update, swap, select, remove, append, clear, create 10k and clear show the
wasm64 build's rows after each; __idle and __lane are set, no stalls, no
console errors; a missing snapshot and Lean exiting both stop the page with
the reason. test/node.mjs still passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
Snapshot in progress: the qed64 entry in bench/race/lanes.mjs, its copy in bench/race/build.sh, and the runner and race test changes. Not yet tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
The race gets its fourth lane, qed64 (bench/race/lanes.mjs): optional, offered as "Add Lean's interpreter on QED64 (about 42 MB)", asynchronous, and only where the browser has Memory64 and the page is cross-origin isolated. - bench/race/build.sh copies dist/qed64 into dist/race when it is built: the runtime once, as qed64-rt/ (hard links, with lean.wasm's .br and .gz), and the pages as life/qed64/ and table/qed64/, pointing at it. Without it, a notice; QED64=require makes that an error, before dist/race is touched. - The referee empties a lane's panel before loading its page again and waits its unloadMs (2.5 s for qed64), because lean.wasm's pthreads outlive their page by about 2 s; tests/browser/race.mjs does the same between its races, and checks that qed64's Worker fetched more than 30 MB itself. - tests/browser/run-all.sh builds the lane with --build (LEANUI_QED64=0 skips it), serves the race from a second, cross-origin isolated server, and runs it with --lanes vanilla,wasm64,vir,qed64 whenever dist/race has the lane (LEANUI_QED64=require makes its absence a failure). - The race page's footer says how an asynchronous lane is timed; docs/BENCHMARKS.md has the lane's numbers and their Lean / transport / DOM split (bench/race/ci/race-qed64-*.json); qed64/README.md, DESIGN.md, DEPLOY.md and README.md mention it. Checked on the CI machine (Playwright's Chromium 141 and Firefox 142): the three-lane race passes as before; with the fourth lane, Life and the table in Chromium and Life in Firefox pass, with no stalls. In Chromium the lane takes 1.11x lean-vir's time in Life and 1.00x on the table (geometric mean), and Lean's own time is 80-97% of each click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
Snapshot in progress: the lane's caches and Node test in CI, the site's immutable /race/qed64-rt route, stage.sh keeping the lane's precompressed files, and docs. Not yet tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
CI builds, tests and races the QED64 lane on every push and pull request, and
the site serves it at /race/ with the other lanes.
- .github/workflows/ci.yml: the test job sets LEANUI_QED64=require, so the race
must have the lane (run-all.sh passes it on to bench/race/build.sh). The elan
cache key and install include qed64/lean-toolchain. qed64/.cache is restored
and saved explicitly in two parts: per lock (qed64.lock.json and
lean-toolchain: the downloads, Std compiled by lean.wasm, lean.wasm's .br and
.gz, about 620 MB unpacked) and per source (the rest of the bake and
qed64/.lake, about 70 MB, restored from the newest build of the lock; bake.sh's
stamp decides what to redo). A new step runs qed64/test/node.mjs on the Node 24
that qed64/tools/node.sh pins. The bench-labelled race runs with
--lanes vanilla,wasm64,vir,qed64. The container smoke test fetches the lane's
qed64.json, its lean.wasm (brotli, immutable) and its snapshot (HEAD), and the
post-deploy check adds the lane's lean.wasm to the files that must arrive as
the server's own .br.
- examples/SiteServer.lean: /race/qed64-rt is served cross-origin isolated, like
/race, with year-long immutable caching (its names change with their content).
- deploy/cloudflare/stage.sh: the pass that deletes every .br and .gz leaves out
dist/race/qed64-rt (its apps.<h>.snap.gz is the snapshot itself), and the
brotli 11 pass leaves out its lean.wasm, which keeps qed64/build.sh's siblings.
- qed64/build.sh names the licences LICENSE-lean4.txt and LICENSE-qed64.txt
(text/plain), and the race page's footer links them.
- Docs: DEPLOY.md (the caches, the Node test, the stage.sh exception, what the
container holds and why, its memory under the lane's downloads, the hand
deploy), TOOLCHAIN.md (qed64's toolchain next to vir's), qed64/README.md
("In CI and on the site"), README.md and BENCHMARKS.md (a line each).
Checked here: actionlint 1.7.12 (no new findings; shellcheck reports the same
two warnings as before); qed64/build.sh and bench/race/build.sh; the CI cache
paths alone give a no-op lane build; qed64/test/node.mjs passes (28.5 s);
leanui-site built natively; stage.sh, then the image built and run with docker
and the smoke test's curls against it, including the new ones; the four-lane
race passed in Chromium against the container and in Firefox (Life) against the
staged site. The staged site grows from 28 MB to 193 MB and site-container.tgz
from 11 MB to 86 MB. Eight simultaneous downloads of the lane took the
container from 128 MiB to 363 MiB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
- The Worker drops its last reference to the snapshot's bytes once they are in MEMFS, so the unlink after the mount frees the 114 MB. - A lane page that fails to start sets window.__failed, and the race says "did not start" at once instead of waiting out the 300 s boot limit. - QED64=0 leaves the lane out of the race even if dist/qed64 is there. - The race footer's QED64 credits and licence links show only when the lane was built. - qed64/README.md: the run file and the keyed ratio range. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd
FawadHa1der
marked this pull request as ready for review
October 26, 2026 22:34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds a fourth contender to the race: the same Lean apps, run by Lean's own interpreter inside QED64's wasm64 build of Lean 4.35, in a Web Worker. It also makes the race's lanes modules. The new lane lives in
qed64/, apart from everything else, likevir/. It is optional: off unless a visitor ticks "Add Lean's interpreter on QED64 (about 42 MB)" under the race, or asks for it with?lanes=.What the lane is
lean.wasmrunslean --runon a small host program (qed64/LeanUIQed/Host.lean). The host reads events from stdin and writes LeanUI's update buffers to stdout, through a shared-memory ring and a stdout tap in the Worker. The page applies the buffers with the unchangedruntime/leanui.js.lean.wasm.lean.wasm106 MB, 11.0 MB with brotli) and one header snapshot of the apps (114 MB, 30.9 MB gzipped). That is 41.9 MB per session, cached afterwards.qed64/qed64.lock.json(sha256 per chunk, two sources). The Std modules, the shimmed LeanUI and the snapshot are built bylean.wasmitself. Nothing large is committed.Pluggable lanes (
bench/race/lanes.mjs)The race reads its lanes from a registry. A lane can be:
window.__idle()after each click, and clicks nothing else meanwhile, so the time still runs from the click to the page updated.A lane can also report its own figures: downloads, start phases, and Lean / transport / DOM time. The three existing lanes behave and measure as before.
Checks
qed64/test/node.mjs(Node 24): the lane's updates are byte-identical to a compiled reference host in all 8 scenarios. It also runs the 10,000-row create under a 0.49 MiB pthread stack, and 20 empty pings.tests/browser/race.mjs --lanes vanilla,wasm64,vir,qed64:QED64=0is honoured;Numbers
CI machine, to be re-measured on the reference Mac.
Chromium, ms per click (vanilla / wasm64 / lean-vir / QED64):
The site
/race/qed64-rt/is served immutable and cross-origin isolated, like/race/.stage.shkeeps the lane's prebuilt brotli and gzip files.docs/DEPLOY.mdnotes this. An edge cache in the Worker is a possible follow-up.🤖 Generated with Claude Code
https://claude.ai/code/session_016R1ctPHanJvc74mwoJrYgd