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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user