The profiling campaign ranks functions on instruction count (Ir) only,
so callgrind's cache simulation is pure overhead — it roughly doubles
each run's wall time. Pass `--cache-sim=no` via the gungraun
`LibraryBenchmarkConfig` to halve the measure->optimize loop's latency.
Adds benches/compare_profile.rs: a gungraun library_benchmark that runs
the entire `compare` workload once at seed 0 under callgrind. It path-
includes the shared examples/_shared/compare_workload.rs module and calls
the new profile_workload() entry point, which invokes all 82 algorithm
runners and folds every result into a checksum so nothing is elided.
gungraun reports the whole-program instruction count and diffs it against
the previous run; the saved callgrind.out
(target/gungraun/heuropt/compare_profile/.../callgrind.full_compare_workload.out)
carries the per-function breakdown for callgrind_annotate. This is the
measurement harness for the function-level optimization campaign.
Round-0 baseline: 357,060,633,544 Ir. The shared module also carries the
example's presentation layer, so the bench gets a commented
`#![allow(dead_code)]` -- but the runner functions are deliberately not
allow-listed, so a runner missing from profile_workload still warns (that
check already caught one omission).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expand benches/hot_paths.rs so the instruction-count harness exercises the
whole library. Adds four groups — permutation_ops_group (all 10 permutation
operators at n=30/100), variation_ops_group (BitFlip, Levy, BoundedGaussian,
ClampToBounds, ProjectToSimplex), combinatorial_group (TSP/JSS/knapsack
end-to-end plus AntColonyTsp), and multi_fidelity_group (Hyperband) — and
folds tabu_search_short and umda_short into single_objective_group. All 33
algorithms and 20 operators are now on the benchmarking surface (69 benches).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire `gungraun` 0.18 as a dev-dependency and a `benches/` directory
with instruction-count benchmarks for the algorithmic hot paths.
Why gungraun and not criterion: heuropt's hot paths are deterministic
numerical loops where wall-clock noise dominates real differences.
gungraun runs each benchmark under valgrind/callgrind once and reports
exact instruction counts — stable across machines and CI runners,
detects sub-microsecond regressions cleanly.
Benchmarks added:
- pareto::non_dominated_sort (the inner loop of every Pareto MOEA)
- pareto::crowding_distance (NSGA-II survival selection)
- metrics::hypervolume_nd (HSO recursion, used by SMS-EMOA)
- internal::cholesky (BO's per-step posterior factorization)
- algorithms::nsga2 single generation (end-to-end smoke check)
- algorithms::cma_es single generation (eigendecomposition cost)
Tracked size only — these aren't part of the regular CI matrix because
they need valgrind installed. Run with `cargo bench` locally.
Wired via the standard `[[bench]]` Cargo entries with `harness = false`
so gungraun's main_macro does the dispatch.