perf(age_moea): score only the splitting front in environmental_selection

`prox` and `nearest` were computed for every member of `combined`, but
the scoring loop only ever reads the entries for the splitting front
(`remaining`). Fill just those, skipping the `lp_norm` / `lp_distance`
work — and the `powf` calls inside them — for the rest of `combined`.

Bit-identical: the skipped entries were never read. In the
compare_profile benchmark this cut `pow` + its libm kernel from ~16.2B
to ~14.3B Ir and `environmental_selection` self-Ir from 2.13B to 1.82B.

Round 3 whole-program: 173,803,642,945 -> 169,440,644,233 (-2.51%).
This commit is contained in:
2026-05-14 12:37:42 -06:00
parent 0ea09bdf82
commit 66b4d9fa6b
+11 -6
View File
@@ -306,12 +306,17 @@ fn environmental_selection<D: Clone>(
// once per (remaining, pick) pair instead of per (remaining, all-keep).
let mut keep = selected.clone();
let mut remaining: Vec<usize> = splitting.clone();
let prox: Vec<f64> = (0..combined.len())
.map(|i| lp_norm(&translated[i], p))
.collect();
let mut nearest: Vec<f64> = (0..combined.len())
.map(|i| nearest_neighbor_distance(i, &translated, &keep, p))
.collect();
// `prox` and `nearest` are only ever read for splitting-front members
// (the `remaining` set) — the scoring loop never touches the entries
// for `selected` or discarded members. Filling only the `remaining`
// entries skips `lp_norm` / `lp_distance` work on the rest of
// `combined`; bit-identical, since those entries were never used.
let mut prox: Vec<f64> = vec![0.0; combined.len()];
let mut nearest: Vec<f64> = vec![f64::INFINITY; combined.len()];
for &i in &remaining {
prox[i] = lp_norm(&translated[i], p);
nearest[i] = nearest_neighbor_distance(i, &translated, &keep, p);
}
while keep.len() < n {
// Pick the remaining candidate with the largest score.
let mut best_idx: Option<usize> = None;