Single source of truth for benchmark operation (human + agent).
The benchmark workflow is intentionally reduced to three run types:
MICRO(Google Benchmark micro suite)MACRO-SMALL(macro suite at 10k rows)MACRO-LARGE(macro suite at 500k rows)
Legacy compare/leaderboard/report-latest flows are removed. Reporting and comparison are handled by benchmark/report.py.
Current macro dataset catalog contains 15 profiles, including string-heavy reference workloads
(event_log, iot_fleet, financial_orders) and the 84-column measurement_campaign profile
for Static vs Flexible parity investigation.
Macro runs execute mode families across storage (flexible|static)
and codec (dense|zoh|delta) depending on CLI filters.
cmake --preset ninja-release
cmake --build --preset ninja-release-build -j$(nproc) --target bench_macro_datasets bench_micro_typesUse the macro executable directly when you want fast local iteration on profile/scenario selection.
# Show CLI overview, options, and examples
build/ninja-release/bin/bench_macro_datasets --help
# Run one profile with selected scenarios
build/ninja-release/bin/bench_macro_datasets \
--profile=rtl_waveform \
--rows=10000 \
--scenario=baseline,sparse_columns_k1
# Force narrow-terminal summary output
build/ninja-release/bin/bench_macro_datasets \
--size=S \
--summary=compact \
--output=macro_small_results.jsonSummary modes:
--summary=full(default): full metrics table--summary=compact: terminal-friendly compact table (fits narrow terminals)
Discovery helpers:
--listprints all profile names--list-scenariosprints all scenario IDs
Timing modes:
- Default: timed read loops include per-row validation (regenerate expected row + compare). Good for regression A/B within this suite — but the numbers understate pure decode speed several-fold.
--no-validate: pure decode throughput (no expected-row work in the timed loop). Use for absolute claims and cross-format comparisons.- Note: ZoH/Delta modes use
generateTimeSeriesdata while CSV/Dense use the volatilegeneratedata — the compression-vs-CSV column therefore compares against a CSV of a different dataset (footnoted in generated reports).
Quantified on the reference machine (Ryzen 9 9950X3D) — full study in
docs/archive/NOISE_FLOOR_2026-07-12.md:
| Regime | Read noise (median CV) | Write noise | Use for |
|---|---|---|---|
| 8-way parallel sweep | 5.5–8.5 % (p90 to 20 %) | 7–9 % (p90 to 23 %) | coverage smoke, regressions > 15–20 % |
| Solo, pinned, interleaved A/B | ~1–2 % | ~5–7 % (binary-layout luck) | verdicts in the ±10 % band |
Rules of thumb:
- Judge on per-mode medians across profiles × scenarios, never on single metrics (single-metric deltas below the p90 CV are noise).
- Reads are the reliable metric; writes carry ±5–7 % binary-layout noise even between semantically identical builds.
- Prefer M/L sizes for gating (S is 1.5–2× noisier); run ≥ 3 repetitions and use medians (warm-up median penalty is ≤ 3 %, but ~10 % of metrics show a 10–23 % slower first repetition).
- Parallel-sweep numbers are 7–14 % below solo numbers — never mix regimes without same-regime anchors.
- Micro suite: gate only on aggregate ops (≥ ~10 ns: Visit, Serialize,
CsvWriteRow); the sub-ns
Get_*/Set_*accessors swing ±30 % run-to-run.
Standalone tool for cross-product comparison of all file codecs × row codecs × layout types
(Flexible vs Static). Uses interleaved iterations to neutralize thermal throttling.
Not driven by run.py — intended for targeted investigation.
# Build
cmake --build --preset ninja-release-build --target bench_codec_compare -j$(nproc)
# Full parity run (84-column measurement_campaign profile)
./build/ninja-release/bin/bench_codec_compare \
--rows=50000 --iterations=5 --profile=measurement_campaign \
--storage=both --json=tmp/parity_investigation.json
# Flexible-only (skip Static candidates)
./build/ninja-release/bin/bench_codec_compare \
--rows=10000 --iterations=3 --profile=mixed_generic \
--storage=flexible
# Static-only
./build/ninja-release/bin/bench_codec_compare \
--rows=10000 --iterations=3 --profile=measurement_campaign \
--storage=staticOptions:
| Flag | Default | Description |
|---|---|---|
--rows=N |
10000 | Rows per write/read cycle |
--iterations=N |
5 | Interleaved iterations per candidate |
--profile=NAME|all |
all |
Single profile or all profiles |
--storage=both|flexible|static |
both |
Layout filter |
--json=PATH |
(none) | Write JSON results to file |
Static candidates appear with (S) suffix in output and are auto-skipped
for profiles without a compile-time LayoutStatic definition.
When JSON output is produced via the orchestrated report pipeline, report.py
generates a Static Parity table pairing Flexible/Static results per codec
and flagging anomalies where Static is slower than Flexible.
All benchmark workflows are accessible through a single entry point:
python3 benchmark/run.py <subcommand> [options]| Command | Purpose |
|---|---|
wip |
Run benchmarks on the current working-tree build (default workflow) |
baseline |
Clone a git ref, build, and benchmark into a clean baseline directory |
compare |
Generate a markdown comparison report between two run directories |
interleaved |
Run head-to-head interleaved pairs and produce a comparison |
--type=MICRO,MACRO-SMALL,MACRO-LARGE(comma-separated, defaultMACRO-SMALL)--repetitions=<N>(default1)--pin=NONE|CPU<id>(defaultNONE, exampleCPU2)--git=<label>(defaultWIP, controls results bucket naming)--results=<path>(defaultbenchmark/results/<hostname>/<git>)--no-build/--no-report--languages=python,csharp(optional language lanes)--macro-profile=<name>(pass-through tobench_macro_datasets --profile)--macro-scenario=<csv>(pass-through tobench_macro_datasets --scenario)--macro-storage=both|flexible|static(defaultboth)--macro-codec=both|dense|zoh|delta|primary(defaultboth)primary= dense + zoh + delta (skip CSV-only)
--detail(show per-profile breakdown table in operator summary)
The terminal operator summary includes:
- Wall-clock timing: total benchmark execution time per run type
- Profile coverage:
N/Mcolumn shows how many profiles contributed to each mode's median (Static modes only run on profiles with no-copy generators:mixed_generic,sparse_events) - Noise flags: a
~marker appears when stdev exceeds the median (CV > 1.0), indicating high across-profile variance — consider re-running with--pinor more--repetitions - Per-profile breakdown (
--detail): write rows/s for every profile × mode combination
# Default developer run
python3 benchmark/run.py wip
# Micro benchmark pinned to CPU2 (target <5 min)
python3 benchmark/run.py wip --type=MICRO --pin=CPU2
# Full campaign (all three types)
python3 benchmark/run.py wip --type=MICRO,MACRO-SMALL,MACRO-LARGE
# Isolated macro family: flexible + dense only
python3 benchmark/run.py wip \
--type=MACRO-SMALL \
--macro-storage=flexible \
--macro-codec=dense
# Isolated macro family: static + ZoH only
python3 benchmark/run.py wip \
--type=MACRO-SMALL \
--macro-storage=static \
--macro-codec=zoh
# Delta codec only (flexible + static)
python3 benchmark/run.py wip \
--type=MACRO-SMALL \
--macro-codec=delta
# All BCSV codecs, skip CSV (dense + zoh + delta)
python3 benchmark/run.py wip \
--type=MACRO-SMALL \
--macro-codec=primary
# Include Python language lane in the same run directory/report
python3 benchmark/run.py wip --type=MACRO-SMALL --languages=python
# Clean baseline run bucket
python3 benchmark/run.py wip --type=MACRO-LARGE --git=cleanUse this flow for reproducible comparison between a clean commit baseline and current workspace changes.
# 1) Purge old results
rm -rf benchmark/results && mkdir -p benchmark/results
# 2) Clean baseline from a fresh clone at the selected git ref
python3 benchmark/run.py baseline \
--git-ref HEAD \
--types MICRO,MACRO-SMALL,MACRO-LARGE \
--repetitions 5 \
--temp-root /tmp/bcsv \
--results-root benchmark/results/$(hostname) \
--pin NONE
# 3) Current workspace run (WIP) compared against latest clean baseline
python3 benchmark/run.py wip \
--type=MICRO,MACRO-SMALL,MACRO-LARGE \
--repetitions=5 \
--pin=NONE \
--git=WIP \
--results=benchmark/results/$(hostname)/WIP \
--no-buildFor alternating baseline/candidate campaigns (for example 5 interleaved pairs), generate a single pair-wise + aggregate markdown summary:
python3 benchmark/interleaved_compare.py \
--baseline-root benchmark/results/$(hostname)/clean_interleaved \
--candidate-root benchmark/results/$(hostname)/wip_interleaved \
--expected-pairs 5 \
--run-type MACRO-SMALLFor strict interleaving semantics (for repetition -> for benchmark type -> run A/B pair),
use the unified CLI's interleaved subcommand:
python3 benchmark/run.py interleaved \
--baseline-bin /tmp/bcsv/<git-hash>/build/ninja-release/bin \
--candidate-bin build/ninja-release/bin \
--baseline-label git-<git-hash>-clean \
--candidate-label WIP \
--types MICRO,MACRO-SMALL \
--repetitions 5 \
--results-root benchmark/results/$(hostname)/interleaved_h2hOr use the individual scripts directly:
python3 benchmark/run_interleaved_pairs.py \
--baseline-bin /tmp/bcsv/<git-hash>/build/ninja-release/bin \
--candidate-bin build/ninja-release/bin \
--baseline-label git-<git-hash>-clean \
--candidate-label WIP \
--types MICRO,MACRO-SMALL \
--repetitions 5Then compare the pair roots:
python3 benchmark/interleaved_compare.py \
--baseline-root benchmark/results/$(hostname)/interleaved_h2h/git-<git-hash>-clean \
--candidate-root benchmark/results/$(hostname)/interleaved_h2h/WIP \
--expected-pairs 5 \
--run-type MACRO-SMALLDefault output:
benchmark/results/<hostname>/interleaved_5x_comparison.md
The script also prints a concise terminal summary (median/min/max deltas per metric) so operators get immediate feedback without opening the markdown file. Scores and comparison matrices are computed only from workloads present in both baseline and candidate runs; excluded workloads are listed explicitly in the report.
Delta conventions used by comparison reports:
- Macro write/read deltas are expressed as execution-time change (derived from throughput). Positive means WIP is slower; negative means WIP is faster.
- Micro deltas are expressed as latency change (
real_timens). Positive means WIP is slower; negative means WIP is faster. - Compression/file-size deltas use
(WIP - baseline) / baseline. Positive means WIP output is larger.
Supported run types for --run-type:
MACRO-SMALLMACRO-LARGEMICRO(latency deltas, usingmicro_results.json)
Temp clone location defaults:
- Linux:
/tmp/bcsv/<git-hash> - Windows:
C:/temp/bcsv/<git-hash>
MACRO-SMALLshould complete in < 3 minutes.MACRO-LARGEshould complete in < 60 minutes.MICROshould run pinned toCPU2and complete in < 5 minutes.
Use isolated runs to avoid mixing strategy families in one result set.
# 1) Dense-only, flexible
python3 benchmark/run.py wip \
--type=MACRO-SMALL \
--macro-storage=flexible \
--macro-codec=dense \
--pin=CPU2
# 2) Dense-only, static
python3 benchmark/run.py wip \
--type=MACRO-SMALL \
--macro-storage=static \
--macro-codec=dense \
--pin=CPU2
# 3) ZoH-only, flexible
python3 benchmark/run.py wip \
--type=MACRO-SMALL \
--macro-storage=flexible \
--macro-codec=zoh \
--pin=CPU2
# 4) ZoH-only, static
python3 benchmark/run.py wip \
--type=MACRO-SMALL \
--macro-storage=static \
--macro-codec=zoh \
--pin=CPU2For hotspot attribution, run profiler passes on a single isolated family at a time.
run.py wip calls benchmark/report.py automatically (unless --no-report).
Manual usage:
# Auto-compare against latest git-clean run if available
python3 benchmark/run.py compare benchmark/results/<hostname>/WIP/<run_timestamp>
# Explicit baseline comparison
python3 benchmark/run.py compare <candidate_run_dir> --baseline <baseline_run_dir>
# Full report with detail tables
python3 benchmark/run.py compare <run_dir> --baseline <baseline_dir> --fullGenerated summary report name:
report_<git_label>_<timestamp>.md
Report content is intentionally concise:
- Host information
- Condensed Performance Matrix (with
~noise markers when CV > 1.0) - Condensed Performance Matrix Comparison
- Baseline staleness warning (when baseline is > 7 days old)
Each run directory contains:
platform.jsonmanifest.jsonmacro_small_results.json(whenMACRO-SMALLselected)macro_large_results.json(whenMACRO-LARGEselected)macro_results.json(compatibility merged macro view)micro_results.json(when micro selected)macro_small_stdout.log,macro_small_stderr.logmacro_large_stdout.log,macro_large_stderr.logmicro_stdout.log,micro_stderr.log(when micro selected)condensed_metrics.jsonreport.mdand timestampedreport_<label>_<timestamp>.md
For --repetitions > 1, raw per-repetition artifacts are stored under repeats/run_<N>/ and top-level
macro_*_results.json / micro_results.json contain median-aggregated results across repetitions.
Skipped results (e.g., Static modes on profiles without no-copy generators) are automatically
stripped from stored JSON files. Only status: "ok" rows are retained; a skipped_count field
records how many were removed.
3rd-party CSV benchmarks are treated as one-time documented reference measurements and are not part of the default orchestrator flow.
Open-data reference mapping and cache/download workflow:
benchmark/REFERENCE_WORKLOADS.mdbenchmark/reference_workloads.jsonbenchmark/fetch_reference_datasets.py
Fetch datasets to non-versioned cache (tmp/reference_datasets):
python3 benchmark/fetch_reference_datasets.pyPython lane is intentionally separate from the C++ orchestrator and writes schema-compatible macro rows:
python3 python/benchmarks/run_pybcsv_benchmarks.py --size=SOutput default:
benchmark/results/<hostname>/python/py_macro_results_<timestamp>.json
Build native C API target, then run the .NET benchmark harness:
cmake --preset ninja-release
cmake --build --preset ninja-release-build -j$(nproc) --target bcsv_c_api
export LD_LIBRARY_PATH="$PWD/build/ninja-release/lib:$LD_LIBRARY_PATH"
dotnet run --project csharp/benchmarks/Bcsv.Benchmarks.csproj -- --size=SOutput default:
benchmark/results/<hostname>/csharp/cs_macro_results_<timestamp>.json