the nightly benchmark stand: one box, every [benchmark] in the tree, plotted over commits - #3976
Draft
aleksisch wants to merge 1 commit into
Draft
the nightly benchmark stand: one box, every [benchmark] in the tree, plotted over commits#3976aleksisch wants to merge 1 commit into
aleksisch wants to merge 1 commit into
Conversation
aleksisch
force-pushed
the
aleksisch/benchmarks-stand
branch
3 times, most recently
from
September 9, 2026 10:32
f2d8e68 to
fbd02f0
Compare
…plotted over commits utils/internal/bench-stand is the tool - `run` benchmarks a tree into one run record (dastest --bench per file per lane under a wall-clock and RSS cap, the minimum ns/op over repeats as the value), `report` turns every record into the viewer's data.json plus the markdown summary the workflow posts, and its exit code is the night's verdict: 0 ok, 1 a failed build or file, 2 a regression when asked. A regression is a move past both 10% and 3x the baseline's relative MAD over the median of the previous seven runs, so a noisy box cannot manufacture one. Benchmarks group by their directory under benchmarks/, so a new file joins its group by living in the right folder and no manifest tracks names. A night is only worth reading if a red one says what broke, so every failure carries the thing to act on: the first error line of a compile failure, the functions that failed an assertion, the kill reason with the knob that set the limit, the FATAL line of a non-zero exit, and - for a build that never got as far as a benchmark - the tail of the build log, which the summary leads with and the viewer prints inline. A file killed at a limit keeps that verdict whatever the child printed afterwards, and keeps the arms it had finished. nightly.sh is the box side: it checks out the ref, builds Release (RelWithDebInfo arms the C++ allocation tracker, whose exit-time report makes every clean process exit 1), runs the suite, renders the report, and publishes the static viewer at /bench/. A night whose build fails still publishes - the last passing build's binary renders the red night, and the run record of a night that never benchmarked is written by the tool itself, so the record schema has one writer. status.json closes even when the driver dies, so the workflow's follow always returns. bench-stand-deploy.sh provisions the box and splices caddy.snippet, deriving the routes it checks from the snippet rather than copying them. The workflow reaches the box with a key whose authorized_keys line forces `nightly.sh gate`, which answers only start, follow, status and summary, at most two path-character arguments each; asking for a regression to fail the night rides start's second word, because a forced command carries no environment. There is no self-hosted runner: this repository is public, and one would execute pull-request code on the production web box. REVIEW.das is the checklist's mechanical half - key parity across suite.json, SuiteConfig and the README, a skip that states its reason, sibling requires by bare module name, no markup writing in the viewer, every BENCH_STAND_* knob documented in both places, no route literal copied out of the snippet, and a placement line for every file. benchmarks/core/array/test01.das is skipped in suite.json with its reason - it allocates ~19 GB under the persistent heap and gets OOM-killed; a skip is listed every night, so it stays visible debt. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
aleksisch
force-pushed
the
aleksisch/benchmarks-stand
branch
from
September 9, 2026 11:44
fbd02f0 to
ffa605d
Compare
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.
Why. Nothing measures how daslang's performance moves over commits. The tree holds 69
[benchmark]files and no rail that runs them on one machine every night.What changes.
benchmarks/in the interpreter, JIT and AOT lanes, one file per process under a wall-clock and memory cap./bench/; a nightly workflow drives it over ssh and posts the summary.DAS_AOT_SUITES, so their stubs land intest_aot, and the night builds it.Observable behavior.
Where to look.
bench_runner.daspolices the child,bench_history.dasdecides every verdict,nightly.shis the privileged surface.Validation, claims, ledger
Validation
core/math/exp.das: interp 3.32 / 4.19 / 3.33 ns/op, jit 1.75 / 1.96 / 1.98, aot 2.99 / 1.62 / 3.00. That reproduces the shapeplans/benchmark_followups.mdalready records for scalarexpthrough the vecmath lane - AOT level with or behind the interpreter onexpandexp_std, ahead of the JIT only onexp_est.test_aotlinks with the benchmark stubs in it (275 MB, 1080-odd TUs) and answers the lane's probe.bin/daslang dastest/dastest.das -- --test utils/internal/bench-stand), reached per PR throughrun_utils_tests. The memory-cap arm needs perl, no longer/proc, so it runs on the darwin lane too. On a RelWithDebInfo host the process still exits 1 after the report: the C++ allocation tracker's exit-time leak dump does that to every suite in this repo, this one included.run --filter core/math/ --repeat 1thenreport, both exit 0, 20 series, and the summary renders. That path found two defects now fixed - a meta with no build section was read as a failed build, and the run verb's exit code did not follow the record's status.BENCH_STAND_HOME, against a worktree with a built binary: two clean nights, then a night whose build fails. The failed night published a summary leading with the CMake error, and the failed record was written by the tool. Linux x86_64,BENCH_STAND_BUILD=skip.REVIEW.dascells was negative-controlled: break the invariant once, confirm the finding, restore. Gate green on the branch.preflightfast tier: format, lint (both rails), hash-refs, review-md-tests, md-ascii, ast-verify, ci-das, ci-matrix all pass.review-mdreports nine findings fromutils/REVIEW.das, all aboututils/ast-fuzz/andutils/dasFormatter/- untracked local directories on the author's box, one of them excluded through.git/info/exclude. Control: the same gate on a clean worktree of this branch printsREVIEW.das utils: OK.compile-sweepreports 122 of 761 program roots failing, 50 of themmissing prerequisite 'sqlite', on a build configured-DDAS_SQLITE_DISABLED=ON -DDAS_LLVM_DISABLED=ON; none of the 122 is in this diff.nightly.shcomputed its build parallelism with a bare$(nproc)at load, which is GNU-only, so on the darwin lane every verb of the script died with exit 127 before reaching its work - the count now falls back tosysctl hw.ncpuand then to 4, verified by hidingnprocfromPATH. Andprocess_rss_mbtruncated to whole megabytes, so a process under 1 MB read as 0; it rounds up now, which also makes a cap fire a hair early rather than late. The memory-kill arm itself passed on darwin, so thepsfallback works where the limit matters.codexon PATH.schedulelane cannot fire on a branch, andworkflow_dispatchneeds the file on the default branch, so its first run is after merge - and it reds until the box is provisioned (below).Claims - stated, not tested
-jitexits 1 under the allocation tracker; a break would show as a night with no JIT numbers and no reason recorded beside the lane.nightly.sh gatewith hostileSSH_ORIGINAL_COMMANDvalues; a break would let a key holder run a shell on the web box.-DDAS_LLVM_DISABLED=OFFbuild on the box fetches the prebuilt LLVM the CI linux lanes fetch. Not verified on Debian 13. A break records the JIT lane as unavailable with the reason, and the interpreter lane still produces a night.README.md(a cold build of 20-40 minutes, a suite of 1-2 hours) are estimates from this author's box, not from the VPS.Not attributed to this change
build (windows, 32, Release, none)fails one arm oftests/module_cache/test_deferred_modules.das, about a module whose C++ dependencies are deferred. This diff adds no C++, touches no module-cache code and no file undertests/. I did not obtain a master-side control run, so this is an attribution by content, not by a red-on-master comparison.Not done
sudo bench-stand-deploy.sh provision <key.pub>,sudo bench-stand-deploy.sh caddy, store the private half as theDASWEB_BENCH_KEYenvironment secret ongithub-pages, then dispatch the workflow once.README.mdsection 3 has the commands.benchmarks/decs/bench_from_decs_count.dasdoes not compile (int += int64).benchmarks/core/array/test01.dasallocates ~19 GB and is OOM-killed; it is skipped insuite.jsonwith that reason and shows as skipped every night until fixed. Neither is fixed here.site/app.jshas no test rail. The repo has two that would fit -node --teston the pure helpers, asutils/internal/dasweb-verify/browser/does, and Playwright against the static page, assite/tests/playground/does - and neither is wired up, so the viewer's DOM branches are unproven.bench-stand-deploy.shhas no test: its testable core needs a splice-into-a-fixture entry point first, the shapeutils/internal/dasweb-buildd/roll_toolchain.shuses.