Skip to content
Prismio logo
Prismio
Developers

Run, extend, and read the benchmark suite

Execute the Prismio benchmark harness, inspect its JSON, add equivalent language arms, and record unsupported capabilities honestly.

Last verified

The suites live under benchmarks/: hosted/ (Prismio, C++ and Rust, timed) and freestanding/ (Prismio, C and Rust on bare metal under QEMU, counted in instructions; see The freestanding benchmark suite). Each has a benchmarks.json catalog. run.py builds the three dispatchers of the hosted suite, validates results, samples workloads, loads freestanding/suite.py for the other suite, and writes one JSON file plus an HTML rendering with a tab per suite. The rest of this page is the hosted suite unless it says otherwise.

Command surface

bash
prismio bench --runs 9
prismio bench --only binary_trees --runs 15
python3 benchmarks/run.py --list
prismio bench --suite hosted --runs 9
prismio bench --suite freestanding --only fibonacci --scale 4

--suite hosted|freestanding|all (default all) chooses what runs; the freestanding suite is skipped with a note when its tools (clang, a linker, qemu-system-aarch64, rustc with rust-src) are missing. --scale N multiplies the size of every freestanding workload and --runs bounds how many times each freestanding arm is booted (at most three, since the count is exact). --output writes the combined file, and running one suite leaves the other's report in it as it was.

prismio bench measures the project compiler, and refuses to run if src/, std/ or runtime/ is newer than it (--allow-stale-compiler overrides). Every arm is rebuilt and timed on every run, with CPU time beside wall time; --reuse-reference-builds brings back the cached C++ and Rust builds for a quick run-time-only check. The C++ arm uses LTO when the machine's linker can (it falls back to none, and the report records the command). Called directly, benchmarks/run.py takes --compiler or PRISMIO, and --compiler overrides PRISMIO. --llvm-bin chooses the LLVM/Clang directory. --only is repeatable and select_benchmarks() rejects unknown names. --skip-build requires all three suite executables already under benchmarks/build; use it only when source and compiler inputs have not changed. --output changes the JSON path and the sibling HTML destination. --open opens the finished report but does not alter its contents.

build_all() invokes the Prismio, C++, and Rust builds. run_command() always uses the repository root as its working directory and captures output. Build failures include the expanded command, stdout, and stderr. During measurement, execute() invokes a dispatcher, parses its two required fields, and adds a harness-side wall_ns.

The loop order is run-first, language-second. This is limited interleaving: it reduces long-term drift compared with timing every sample of one language first, but does not randomize order. For a close result, repeat with reversed or randomized external sequencing and run A/A before assigning a cause.

Reading JSON

The result file's own schema_version is 4: it holds generated_at, artifacts and a suites object with one report per suite (hosted, freestanding), and each report keeps the schema it was written with. The hosted report, described here, is at suites.hosted and is schema 3. It records schema_version, generated_at, runs, build_commands, compile_ns, binary_bytes, parity, noise_model, environment, benchmarks, and artifacts. environment is probed on every run and holds the processor, core count, memory, OS, power state, target triple, the toolchain versions (Prismio with the profile it was built in, clang, rustc, LLVM) and the source commit with whether the tree was dirty. Paths in build_commands and artifacts are repository-relative. Implemented benchmark records add one languages object per arm:

json
{
  "elapsed_ns_median": 1234,
  "wall_ns_median": 5678,
  "elapsed_ns_samples": [1200, 1234, 1300]
}

Each implemented, non-elimination record also carries a verdict against C++ and against Rust: outcome (win, parity or loss), the median ratio, the best_ratio, the tolerance it was judged against and the noise measured. Whether a difference is a difference is decided there, not by a percentage in the reader; see Telling a difference from noise.

elapsed_ns_median is the algorithm interval reported by the dispatcher. wall_ns_median includes process launch and harness overhead, although raw wall samples are not currently retained. Compile times are nanoseconds for the suite build, not per-workload compilation. Unsupported records contain their reason and no fabricated languages object.

Adding a workload

Add the catalog record, implement the same algorithm in the Prismio, C++, and Rust category modules, wire each suite dispatcher, choose a deterministic integer checksum, and document the timed boundary. The Prismio category files live under benchmarks/hosted/prismio, the C++ implementation is split across the files in CPP_SOURCES under benchmarks/hosted/cpp, and Rust lives under benchmarks/hosted/rust. The string name in all three dispatchers must exactly match the manifest name.

Before a long run, use --only <name> --runs 1. Deliberately perturb one arm's checksum and confirm the harness rejects it; then restore it. Check that the output file fixture is used equivalently when the workload writes data. Finally run several samples and inspect both the raw JSON and the generated report.html.

If Prismio cannot express the intended standard operation, add a complete unsupported record to benchmarks/hosted/UNSUPPORTED.md. State the missing language or library feature and why a local substitute would invalidate comparison.

Review the generated result diff before committing. A changed checksum, build command, workload count, unsupported reason, or sample shape is a semantic change to the evidence, not report noise. Do not hand-edit medians: main() derives them with statistics.median from the recorded samples.