the nightly benchmark stand: one box, every [benchmark] in the tree, plotted over commits - #3976
Merged
Merged
Conversation
aleksisch
force-pushed
the
aleksisch/benchmarks-stand
branch
9 times, most recently
from
September 15, 2026 15:56
1881c61 to
786e7f9
Compare
`total` counted archetype sizes in an int while Archetype.size is an int64, so the file did not compile and the benchmark had never run. The count is a sum of sizes, so the accumulator takes the width of what it sums rather than the sum being narrowed to it. The entity id the fixture block never reads takes the underscore that says so.
…ns daslang stops reading argv[0] itself daslib/clargs wraps get_command_line_arguments behind three accessors - the post-`--` slice, argv[1..], and get_user_args picking between them by host - and every program in the tree is meant to reach for those rather than the builtin. The one thing it did not answer was the binary a program is running under, which is what a program spawning the same daslang it runs under needs. get_host_binary() is that accessor: argv[0] as an absolute path. The CLI reference names it beside the other three, and the compile-time benchmark group two commits along is its first caller.
…build step and no dependencies utils/internal/bench-stand/site is the page served at /bench/: index.html, app.js, style.css and nothing else - it fetches data.json and status.json from its own directory and renders everything client-side, so publishing a night is a file copy and the box runs no service behind it. caddy.snippet is the route that serves the directory, and the only place that route is written down: it goes into the daslang.io block of the box's Caddyfile, no-cache, so a visitor never reads yesterday's night out of a cache. suite.json rides along: the suite the nights measure - root, excludes, the two lanes and their per-file exclusions, repeats, the per-file timeout, the regression gate's three numbers, and the skips with the reason each one is listed on the page every night. It opens with the latest night: the build's log tail when the build is what failed, otherwise the failures with the message to act on, the lanes that did not run and the skips with their reasons, then the regressions and improvements against their baseline and the noise the gate was measured against. Below that is one chart per benchmark arm, grouped by directory - the minimum ns/op per night as the line, the night's spread as a band, a crosshair snapping to the nearest run with a tooltip listing every lane there, and a click through to the commit; a night the file failed is marked on the axis, and a gap in the line is a night the arm did not measure. The run history closes the page, each row linking its own record. Colors come from the lane through one `--lane` custom property, never from a series' position, so a chart added today is comparable with one read yesterday, and a chart drawing more than one lane carries its legend.
aleksisch
force-pushed
the
aleksisch/benchmarks-stand
branch
from
September 16, 2026 07:36
786e7f9 to
f076b1e
Compare
aleksisch
marked this pull request as ready for review
September 16, 2026 09:12
… is benchctl's two new verbs The stand runs every [benchmark] under benchmarks/ on one box, in an interp and a jit lane, and publishes the numbers over commits. suite.json says what runs, with which limits and which exclusions; a run record per night carries the commit, the machine, the build and one result per file per lane; data.json is what the viewer reads. run_stand.sh is the single pass cron calls, and caddy.snippet is the only place the public route is written down. The tool itself is benchctl. benchctl already stored benchmark output, queried it by commit and compared two sets with a Welch test, so a second tool beside it would have restated that: a second parse of dastest's benchmark lines, a second copy of dastest's stats struct under another name, and its own median. It could not lend any of that out, because benchstat reached its numbers only through the SQL row and so pulled sqlite into anything that wanted a median. benchstat now works on BenchmarkRunStats - the struct dastest itself emits - and bench_table maps a stored row into that shape, so the statistics compile with no database behind them. median and median_i64 live there beside the rest; BenchStatsLine is gone as the duplicate of BenchmarkRunStats that it was; the runner's whole-output classifier, never a parser of the same kind, is classify_run_output. bench_suite, bench_runner, bench_history and the run and report verbs sit under utils/benchctl, which dispatches those two before its own parser sees a flag it does not know, and utils/internal/bench-stand keeps only what the box has. Every module orders its top level types, then private helpers, then its public functions, so the tail of a file is its API.
aleksisch
force-pushed
the
aleksisch/benchmarks-stand
branch
from
September 16, 2026 09:14
f076b1e to
43b9425
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.