Profiling
Calculon is performance-critical, so optimization is profile-first: measure, change, measure again. Two tools, depending on what you're looking at.
samply (preferred)
A sampling profiler with a real flamegraph and per-thread timeline in the Firefox Profiler UI. Use it for anything multi-threaded (e.g. the parallel matrix) - it shows worker idle/imbalance and inverted call trees that flat text cannot.
cargo install samply # once
# Record a release binary or example end-to-end, then open the UI:
cargo build -p calculon-matrix --example matrix_profile --release
samply record ./target/release/examples/matrix_profile 100 30
# Headless / CI: save now, inspect later.
samply record --save-only -o /tmp/prof.json ./target/release/examples/matrix_profile 100 15
samply load /tmp/prof.json # serve the saved profile in the UI
What to read in the UI:
- Inverted (bottom-up) call tree - heaviest leaf functions first.
- Per-thread timeline -
rayonworkers parked inwait_until_coldmeans idle: a serial bottleneck is starving them (Amdahl), not the parallel region. - Stack chart - where wall time actually goes per phase.
Tip
Expect ~2× wall-clock under the sampler. Compare ratios between runs, not absolute times. Build with debug symbols in release (on by default via the workspace profile) for readable frames.
macOS sample (quick & dirty)
No install needed; good for a single-threaded hotspot glance on a running server. Flat text only.
cargo build -p calculon --release
./target/release/calculon --tile-dir ./test_data/france_tiles &
# warm up
curl -s -o /dev/null -X POST http://localhost:8002/route -H 'Content-Type: application/json' \
-d '{"locations":[{"lat":43.606,"lon":3.857},{"lat":43.612,"lon":3.865}],"costing":"auto"}'
# sample under load
sample $(pgrep -f target/release/calculon) 5 -f /tmp/profile.txt &
for i in $(seq 1 50); do curl -s -o /dev/null -X POST http://localhost:8002/route \
-H 'Content-Type: application/json' \
-d '{"locations":[{"lat":43.606,"lon":3.857},{"lat":43.612,"lon":3.865}],"costing":"auto"}'; done
cat /tmp/profile.txt | rustfilt | grep -E '^\s+[0-9]+ ' | sort -rn | head -40 # needs cargo install rustfilt
Common hotspots to expect:
| Symbol | Meaning |
|---|---|
find_candidates_multi_tile, project_onto_edge |
edge snapping - dominates short routes / matrix setup |
expand_forward, expand_reverse |
A* expansion - dominates long routes |
get_tile, TL_CACHE |
tile cache - should be <5% with the thread-local cache |
sin / cos / asin |
Haversine where DistanceApproximator would do |
Benchmarks
Criterion benches give stable before/after numbers (statistical, not a single run):
cargo bench -p calculon-route --bench routing # needs france_tiles
cargo bench -p calculon-matrix --bench matrix # includes 50×50 / 100×100 grids
cargo bench -p calculon-tiles --bench tiles
cargo bench -p calculon-costing --bench costing
Always run benches before and after a performance change to get a proper comparison with significance, and confirm the hotspot moved with a profiler.