diff --git a/crates/sds-core/src/stands.rs b/crates/sds-core/src/stands.rs index 5c6a30c..f9da000 100644 --- a/crates/sds-core/src/stands.rs +++ b/crates/sds-core/src/stands.rs @@ -4246,6 +4246,15 @@ mod tests { fn the_collapse_is_worth_measuring() { use crate::pathfind::{search, Mode, Walker, MEK_MAX_ELEVATION_CHANGE}; + /// The enemy's movement allowance, which sets `M`. + /// + /// Eight is the run allowance that puts this fixture at the size of + /// `examples/perf`'s 8v8 - a few thousand positions rather than a few + /// hundred - and the size matters to what is being measured: + /// [`SideMemo`]'s working set is four slots a position, so a fixture a + /// third the size is one whose tables fit a cache the real ones do not. + const THEIR_MP: i32 = 8; + let message = corpus_board("open"); let move_board = MoveBoard::new(&message); let los = LosCache::new(&message, Rules::default()); @@ -4290,11 +4299,16 @@ mod tests { // one facing per hex and the incoming collapse - which is a collapse // over exactly those - has nothing left to fold. let stands = reach_of(our_start, 0, 6); + // The enemy at a run allowance, which is what makes `M` the whole + // envelope rather than a point. The memo's working set is `positions x + // 4`, so a fixture with a tenth of the positions would be a fixture + // whose tables fit a cache the real one does not: see `THEIR_MP`. + let _ = &stands; let foes: Vec> = their_units .iter() .map(|unit| Foe { who: Combatant::mek(unit, guns(), 4), - may_be: reach_of(Coord::new(unit.x, unit.y), 3, 5) + may_be: reach_of(Coord::new(unit.x, unit.y), 3, THEIR_MP) .iter() .map(|reach| Presence { stand: reach.stand, diff --git a/plan/candidates.md b/plan/candidates.md index 808e115..eb2bf9d 100644 --- a/plan/candidates.md +++ b/plan/candidates.md @@ -2515,12 +2515,30 @@ the branch and `LineTable` now carries the two bearings beside the two lines - they are angles between hexes and depend on no facing, so they collapse exactly the way a line does. That flipped the sign. -**Measured: 1.05 on the median and 1.04-1.09 on the least, over 101 alternating -runs a side, repeated.** The A/B is inside one process - `sweep(.., collapse)` -answers both ways, and `score_stands_uncollapsed` is the reference the -equivalence test uses - so code layout cannot move one arm without the other. -`volley::gather` was deliberately not used as a control: it has moved 26% on -layout with its own source untouched. +**Measured: 1.106, 1.097 and 1.143 on the median, over 101 alternating runs a +side each, on a fixture of 1.83 million exchanges.** The A/B is inside one +process - `sweep(.., collapse)` answers both ways, and +`score_stands_uncollapsed` is the reference the equivalence test uses - so code +layout cannot move one arm without the other. It is CPU time from +`/proc/thread-self/schedstat` rather than wall clock, for the reason recorded +above under "Wall clock is not measurable on this machine". `volley::gather` was +deliberately not used as a control: it has moved 26% on layout with its own +source untouched. + +**The size of the fixture is part of the measurement.** The memo's working set +is four slots a position, so at a few hundred positions it fits a cache the real +one does not. On a fixture a third of `examples/perf`'s the same change measured +1.05; at the 8v8 size it measures 1.10. `the_collapse_is_worth_measuring` runs +the enemy at a run allowance for exactly that reason, and a smaller fixture +would flatter the collapse's cache behaviour rather than the collapse. + +**The between-process run said nothing, and its own controls are how we know.** +Six alternating runs of `examples/perf` on this branch against its base put +`score_stands` at a median of 1.19 *slower* - and put `threat map`, untouched, at +0.669 and `parse one observation`, untouched, at 1.130. Two controls 45% apart +in opposite directions is a machine moving under the measurement, not a change: +another agent was running the same benchmark at the time. Between-process +figures are not available on this box under load; the in-process A/B is. `stands::tests::the_collapsed_sweep_answers_exactly_what_the_uncollapsed_one_does` runs both loops over a corpus of six facings and two arrivals a hex on both