๐ VelesDB Benchmarking Guide
August 8, 2026 ยท View on GitHub
This guide explains how to run reproducible benchmarks for VelesDB.
๐ฅ๏ธ System Preparation
Windows
-
Power Plan: Set to High Performance
powercfg /setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c -
Close background applications:
- Web browsers (especially Chrome/Edge with many tabs)
- IDE indexing (VS Code, IntelliJ)
- Windows Update
- OneDrive sync
-
Disable Windows Defender real-time scanning (temporarily):
# Run as Administrator Set-MpPreference -DisableRealtimeMonitoring $true # Re-enable after benchmarking: # Set-MpPreference -DisableRealtimeMonitoring $false
Linux
-
CPU Governor: Set to performance mode
sudo cpupower frequency-set -g performance # Or for all cores: echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor -
Disable CPU frequency scaling:
sudo cpupower frequency-set -d 3.5GHz -u 3.5GHz # Adjust to your CPU -
Pin process to specific cores (optional):
taskset -c 0-3 cargo bench ...
๐ Running Benchmarks
Basic Commands
# Run all benchmarks
cargo bench
# Run specific benchmark
cargo bench --bench simd_benchmark
# Run with specific filter
cargo bench --bench overhead_benchmark -- cosine
# Skip plot generation (faster)
cargo bench -- --noplot
Best Practices
-
Always use release mode (automatic with
cargo bench) -
Run benchmarks sequentially, not in parallel:
# โ Good: Sequential cargo bench --bench simd_benchmark cargo bench --bench filter_benchmark # โ Bad: Parallel (causes resource contention) cargo bench --bench simd_benchmark & cargo bench --bench filter_benchmark & -
Let the system warm up - Criterion handles warm-up automatically
-
Run 3 times and take median for important measurements
-
Close resource-intensive apps before benchmarking
โ๏ธ Criterion Configuration
Default Configuration
VelesDB benchmarks use Criterion with these defaults:
- Sample size: 100 iterations
- Warm-up time: 3 seconds
- Measurement time: 5 seconds
Custom Configuration
use criterion::{criterion_group, Criterion};
fn bench_with_config(c: &mut Criterion) {
let mut group = c.benchmark_group("my_benchmark");
group.sample_size(200); // More samples for stability
group.warm_up_time(Duration::from_secs(5));
group.measurement_time(Duration::from_secs(10));
// ... benchmarks ...
group.finish();
}
๐ Interpreting Results
Understanding Output
cosine_similarity/768d time: [76.12 ns 76.36 ns 76.64 ns]
change: [-76.37% -75.89% -75.42%] (p = 0.00 < 0.05)
Performance has improved.
- [76.12 ns 76.36 ns 76.64 ns]: Lower bound, estimate, upper bound
- change: Comparison to previous run (if available)
- p value: Statistical significance (< 0.05 = significant)
Acceptable Variance
| Metric | Acceptable | Warning |
|---|---|---|
| Coefficient of variation | < 5% | > 10% |
| Outliers | < 5% | > 15% |
Outliers
Criterion reports outliers:
- mild: Slightly outside expected range
- severe: Significantly outside expected range
A few outliers (< 5%) are normal. More indicates system instability.
๐ง Available Benchmarks
| Benchmark | Description | Command |
|---|---|---|
simd_benchmark | SIMD kernel comparison | cargo bench --bench simd_benchmark |
overhead_benchmark | API overhead analysis | cargo bench --bench overhead_benchmark |
filter_benchmark | Metadata filtering | cargo bench --bench filter_benchmark |
column_filter_benchmark | Column Store vs JSON | cargo bench --bench column_filter_benchmark |
search_benchmark | Distance functions | cargo bench --bench search_benchmark |
hnsw_benchmark | HNSW index operations | cargo bench --bench hnsw_benchmark |
recall_benchmark | Search quality metrics (Recall@k) | cargo bench --bench recall_benchmark |
velesql_benchmark | VelesQL parsing | cargo bench --bench velesql_benchmark |
๐ Benchmark Checklist
Before running benchmarks:
- Close browser and heavy applications
- Set power plan to High Performance
- Check CPU temperature (avoid thermal throttling)
- Ensure no background updates running
- Use
--releasemode (automatic withcargo bench)
After running benchmarks:
- Check for excessive outliers
- Verify coefficient of variation < 5%
- Compare with baseline if available
- Document results in
docs/BENCHMARKS.md
๐ Troubleshooting
High Variance
Symptoms: Large confidence intervals, many outliers
Solutions:
- Close background applications
- Disable CPU frequency scaling
- Increase sample size
- Run on dedicated hardware
Inconsistent Results
Symptoms: Results vary significantly between runs
Solutions:
- Wait for system to stabilize after boot
- Check for thermal throttling
- Pin to specific CPU cores
- Use
--noplotto reduce I/O
Slow Compilation
Solutions:
# Use incremental compilation
export CARGO_INCREMENTAL=1
# Use faster linker (Linux)
# Add to ~/.cargo/config.toml:
# [target.x86_64-unknown-linux-gnu]
# linker = "clang"
# rustflags = ["-C", "link-arg=-fuse-ld=lld"]
๐ช ARM64 Benchmarks (EPIC-054)
CI/CD Integration
ARM64 benchmarks run automatically via GitHub Actions on:
- Push to
main(SIMD/index path changes) - Pull requests modifying SIMD code
Workflow: .github/workflows/bench-arm64.yml
Running ARM64 Benchmarks Locally
# On ARM64 hardware (Apple Silicon, AWS Graviton, etc.)
cargo bench -p velesdb-core --bench search_benchmark
# Cross-compilation (requires target installed)
rustup target add aarch64-unknown-linux-gnu
cargo bench --target aarch64-unknown-linux-gnu
Comparing Architectures
Use the comparison script to analyze x86_64 vs ARM64 performance:
# PowerShell
.\scripts\compare-arch-benchmarks.ps1 `
-x86ResultsPath ".\benchmark-results\x86_64" `
-arm64ResultsPath ".\benchmark-results\arm64" `
-OutputFormat markdown
Interpreting ARM64 Results
| Status | Meaning | Action |
|---|---|---|
| ๐ข ARM64 FASTER | ARM64 >10% faster | Document win |
| ๐ด x86 FASTER | x86_64 >10% faster | Investigate NEON codegen |
| ๐ก PARITY | Within ยฑ10% | Acceptable |
ARM64-Specific Considerations
- NEON vs AVX2: ARM NEON has 128-bit vectors vs AVX2's 256-bit
- Prefetch:
stdarch_aarch64_prefetchnot yet stable (rust#117217) - FMA: ARM64 has native FMA instructions
- Memory alignment: 16-byte alignment for NEON (vs 32 for AVX2)
Performance Phase Gate Protocol
When implementing performance optimizations across multiple phases, use the
perf_phase_gate.py script to ensure no regressions slip through.
Workflow Per Phase
# 1. BEFORE starting implementation: capture baseline
python scripts/perf_phase_gate.py capture --phase 1 --stage before
# 2. Implement the optimization
# ...
# 3. AFTER implementation: full gate check (captures + compares + recall)
python scripts/perf_phase_gate.py gate --phase 1
# 4. View summary of all phases
python scripts/perf_phase_gate.py summary
What the Gate Checks
perf_phase_gate.py is a manual tool: it is not wired into any CI
workflow, so it never blocks a PR on its own. Run it locally and act on a
failed gate before pushing.
| Check | Threshold | Fails the local gate? |
|---|---|---|
| Search latency regression | > 5% | Yes |
| Insert throughput regression | > 10% | Yes |
| SIMD kernel regression | > 3% | Yes |
| Recall@10 drop | Any | Yes |
| Recall Rust tests | Must pass | Yes |
Phase IDs
| ID | Optimization |
|---|---|
| 1 | Software Pipelining |
| 2 | RaBitQ SIMD Popcount |
| 3 | RaBitQ HNSW Integration |
| 4 | PDX Columnar Layout |
| 5A | Cross-Layer Distance Cache |
| 5B | Fused Batch Distance |
| 5C | Auto-EF Tuning |
| 6 | Trigram SIMD |
Results Storage
Phase results are stored in benchmarks/phase_results/:
phase_1_before.jsonโ baseline before Phase 1phase_1_after.jsonโ results after Phase 1- The reference baseline is
benchmarks/baseline_local_perf_optim.json
Rules
- Never skip the "before" capture โ without it, comparison is impossible
- Run benchmarks sequentially โ never in parallel (resource contention)
- Close background apps before capturing (see System Preparation above)
- Gate must pass before merging any optimization phase
- Re-baseline after a phase is merged: the "after" becomes the next "before"
References
- Criterion.rs Documentation
- Rust Performance Book
- BENCHMARKS.md - Current benchmark results