From f3088e8353c35a7611643e4fca2a4b88bbe812ae Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Thu, 14 May 2026 12:22:38 -0600 Subject: [PATCH] perf(ibea): precompute the exp-transformed indicator matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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. --- src/algorithms/ibea.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/algorithms/ibea.rs b/src/algorithms/ibea.rs index d414d71..9696195 100644 --- a/src/algorithms/ibea.rs +++ b/src/algorithms/ibea.rs @@ -280,14 +280,24 @@ fn environmental_selection( } } + // 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> = 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)). // (Higher is better — so a candidate dominated by many is heavily negative.) - let scale = max_abs * kappa; let mut fitness: Vec = (0..pool.len()) .map(|i| { (0..pool.len()) .filter(|&j| j != i) - .map(|j| -(-indicator[j][i] / scale).exp()) + .map(|j| -exp_terms[j][i]) .sum() }) .collect(); @@ -310,7 +320,7 @@ fn environmental_selection( if !alive[i] || i == worst { continue; } - fitness[i] += (-indicator[worst][i] / scale).exp(); + fitness[i] += exp_terms[worst][i]; } alive[worst] = false; alive_count -= 1;