Skip to main content

Performance and Benchmark Reports

LoraDB benchmarks are meant to answer two different questions:

QuestionSourceUse it for
Is this build obviously slower?perf-smoke CI workflowPull request review and regression triage
What does this release look like overall?Manual benchmarks workflowRelease notes, dashboards, and capacity planning experiments

The shared output is benchmark-summary.json: a compact JSON file generated from Criterion's bencher output. It gives you a table-friendly view of the current database state without scraping terminal logs.

note

CI runners are noisy. Treat these numbers as a trend signal for the same benchmark on the same workflow, not as a hardware-independent promise.

Current Snapshot

Representative numbers below come from the Criterion benchmark suite used for LoraDB performance tracking. Throughput units are benchmark specific: scans count nodes, traversals count edges or paths, writes count entities or full write operations.

AreaBenchmarkDatasetMean timeThroughputUnitWhat it tells you
Scanmatch/all_nodes/10001,000 nodes285.13 µs3,507,211nodes/secFull label scan plus projection
Countaggregation/count_star/10001,000 rows59.52 µs16,801,625rows/secFast aggregate path without row projection
Traversaltraversal/single_hop_chain/10001,000-node chain527.42 µs1,894,126edges/secBasic relationship iteration
Traversaltraversal/tree_depth3_branch5_traverse155 descendants55.63 µs2,786,462paths/secBounded tree expansion
Orderingordering/order_by_single/10001,000 rows853.55 µs1,171,582rows/secSort cost for a single key
Writewrite/create_single_nodefresh DB8.60 µs116,331ops/secSingle-node write overhead
Writewrite/batch_create_unwind/500500 creates554.79 µs901,238nodes/secBatched UNWIND insertion
Workloadrealistic/social_friend_of_friend_500500 persons399.46 µs2,503queries/secRepresentative two-hop social query

Live Smoke Report

The PR-facing perf-smoke workflow produces a smaller table with a baseline comparison. The table below is loaded from /benchmarks/perf-smoke-summary.json on the live website.

Loading benchmark summary from /benchmarks/perf-smoke-summary.json

The smoke gate fails only when a benchmark is slower than its configured threshold. The default threshold is 3.0x, because shared CI machines vary enough that a tight gate would be noisy instead of useful.

JSON Shape

The summary artifact is designed to be boring to consume. The useful top-level fields are:

FieldTypePurpose
schema_versionnumberIncremented only when the artifact contract changes
generated_atstringISO timestamp for the summary generation time
suitestringWorkflow or explicit suite name, such as perf-smoke
source.githubobjectRepository, SHA, ref, workflow, run id, and actor when produced in GitHub Actions
source.runnerobjectOS, CPU architecture, and CPU count
summaryobjectBenchmark count, group count, fastest and slowest benchmarks
baselineobjectCompared, ok, regressed, new, and missing benchmark counts
groupsarrayPer-group rollups: count, fastest, median, p95, slowest, average
benchmarksarrayOne row per benchmark, ready for table rendering

Each benchmarks[] row contains:

FieldMeaning
nameFull Criterion name, for example perf_smoke/scan_1k
groupFirst path segment, for example perf_smoke
caseRemaining path segments
ns_per_iterCurrent mean time in nanoseconds per iteration
error_nsCriterion reported error in nanoseconds
relative_error_pcterror_ns / ns_per_iter * 100
iterations_per_secondDerived 1_000_000_000 / ns_per_iter
baseline.statusok, regressed, or new when a baseline is supplied
baseline.ratiocurrent / baseline
baseline.thresholdMaximum allowed ratio for the smoke gate

Render A Table

After downloading a workflow artifact, render the benchmark rows into a developer-readable table with jq:

jq -r '
["benchmark","current_ns","baseline_ns","ratio","status"],
(.benchmarks[] | [
.name,
.ns_per_iter,
(.baseline.ns_per_iter // ""),
(.baseline.ratio // ""),
(.baseline.status // "")
])
| @tsv
' benchmark-summary.json

For Markdown:

jq -r '
"| Benchmark | Current ns | Baseline ns | Ratio | Status |",
"|---|---:|---:|---:|---|",
(.benchmarks[] |
"| `\(.name)` | \(.ns_per_iter) | \(.baseline.ns_per_iter // "") | \(.baseline.ratio // "") | \(.baseline.status // "") |"
)
' benchmark-summary.json

Reproduce Locally

Run the smoke suite and produce the same JSON shape:

cargo bench -p lora-database --bench perf_smoke_benchmarks \
-- --output-format bencher > bencher.log

node scripts/summarize-benchmarks.mjs \
--input bencher.log \
--output benchmark-summary.json \
--baseline crates/lora-database/benches/perf_smoke_baseline.json \
--suite perf-smoke-local

node scripts/check-perf-smoke.mjs --input bencher.log

Run the full suite when you want a release-level snapshot:

cargo bench --locked -p lora-database --benches \
-- --output-format bencher > benchmarks.log

node scripts/summarize-benchmarks.mjs \
--input benchmarks.log \
--output benchmark-summary.json \
--suite release-benchmarks

Reading Regressions

SignalMeaningNext step
baseline.regressed_count > 0One or more current timings exceeded thresholdInspect the named rows in baseline.regressions
baseline.new_count > 0A benchmark exists in the run but not the baselineAdd it deliberately with a baseline refresh
baseline.missing_count > 0A baseline entry was not producedCheck for renamed or deleted benchmark cases
High relative_error_pctCriterion saw noisy measurementsRe-run before drawing conclusions
One slow outlier in CIShared runner noise is possibleRe-run the job, then compare trend across runs

See Also