MeasureBatchDeltaCCHalf re-formed the reference and re-measured every open
candidate on every round of its removal loop - including the rounds where the
picked candidate was only marked spent (not corroborated / no longer
convicted), which change nothing. On a 7200-frame sweep that is most of its
cost. All changes below keep every sum in its original order, so every
delta-CC1/2 is bit-identical:
- candidates are re-measured only after frames are actually removed; a
spent candidate leaves the others' measurements standing. The curve is
read off the first round of candidates (the width-1 ones are the batches),
and the redundant build_totals after the loop is gone.
- measure_all measures ranges that start on the same frame as one chain,
shortest first, carrying on the per-reflection sums - the same additions
in the same order, but shared frames are walked once.
- the usable observations are laid out once in frame order as
{group, I, info}, and the per-reflection sums are one struct per
reflection, so a measurement streams contiguous memory instead of three
indirect loads and four arrays per observation.
- locate measures the (up to) four edge moves of a step side by side and
takes them in the original order; the candidate's own measurement and the
conviction's are reused rather than repeated.
Verified with a temporary in-process check that re-ran every measurement
through the old code (sparse 324+825, cytc 68+68 measurements, all
bit-identical), and p.hkl/p.mtz/p_P1.mtz/p_unmerged.mtz plus the logged
delta-CC1/2 lines byte-identical to rc173 on myob/cytc/lyso (GPU), cytc and
sparse (CPU, sparse removes 288 and 72 frames) and 8a1a (vs the r4int
baseline binary).
Measured (perf, loaded box): 8a1a delta-CC1/2 20.1+7.4 s -> 2.2+1.7 s wall
(123 -> 13 CPU-s), run 129.9 -> 107.1 s; lyso (CPU build) 0.76+0.30 ->
0.16+0.11 s; cytc (CPU build) 0.15+0.25 -> 0.10+0.16 s.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1G8gJVAy6gp1K5Dz3NE5C