perf(ibea): precompute the exp-transformed indicator matrix

`environmental_selection` recomputed `exp(-indicator[worst][i] / scale)`
in its removal loop — the exact value already computed when building the
initial fitness vector. Pre-exponentiate the indicator matrix once; both
the initial fitness sum and every per-removal update then read from it,
turning the O((pool-n) · pool) removal-loop `exp` sweep into additions.

Bit-identical: same input bits -> same `exp` -> same output bits, and
the fitness sum order is preserved. In the compare_profile benchmark
this cut `exp` + its libm kernel from ~6.95B to ~5.40B Ir and
`environmental_selection` self-Ir from 8.89B to 8.73B.
This commit is contained in:
2026-05-14 12:37:41 -06:00
parent 2bd8f8fc11
commit f3088e8353
+13 -3
View File
@@ -280,14 +280,24 @@ fn environmental_selection<D: Clone>(
} }
} }
// Pre-exponentiate the indicator matrix once. Every later use of
// `indicator[j][i]` is `exp(-indicator[j][i] / scale)` — in the initial
// fitness sum and, identically, in the per-removal fitness update — so
// computing it here turns the removal loop's O((pool-n) · pool) `exp`
// calls into plain additions.
let scale = max_abs * kappa;
let exp_terms: Vec<Vec<f64>> = indicator
.into_iter()
.map(|row| row.into_iter().map(|v| (-v / scale).exp()).collect())
.collect();
// Fitness F(i) = -Σ_{j≠i} exp(-indicator[j][i] / (max_abs · kappa)). // Fitness F(i) = -Σ_{j≠i} exp(-indicator[j][i] / (max_abs · kappa)).
// (Higher is better — so a candidate dominated by many is heavily negative.) // (Higher is better — so a candidate dominated by many is heavily negative.)
let scale = max_abs * kappa;
let mut fitness: Vec<f64> = (0..pool.len()) let mut fitness: Vec<f64> = (0..pool.len())
.map(|i| { .map(|i| {
(0..pool.len()) (0..pool.len())
.filter(|&j| j != i) .filter(|&j| j != i)
.map(|j| -(-indicator[j][i] / scale).exp()) .map(|j| -exp_terms[j][i])
.sum() .sum()
}) })
.collect(); .collect();
@@ -310,7 +320,7 @@ fn environmental_selection<D: Clone>(
if !alive[i] || i == worst { if !alive[i] || i == worst {
continue; continue;
} }
fitness[i] += (-indicator[worst][i] / scale).exp(); fitness[i] += exp_terms[worst][i];
} }
alive[worst] = false; alive[worst] = false;
alive_count -= 1; alive_count -= 1;