Two costs in the per-image loop, each measured before it was touched.
Geometry refinement is the largest item in that loop - about half to two thirds of
its processor time on the datasets where the loop matters, and all of it on the host.
It runs three solves per image, and each one spends four fifths of itself inside the
solver at barely two iterations: the cost is not convergence, it is what every
residual evaluation does. The residual carried the blocks it does not refine as dual
numbers, so each evaluation recomputed the two detector rotations, the whole
orthogonalisation matrix, three cross products and the cell volume - all of them
constant for the image - through the derivative machinery, several million times per
run. Split the observed and predicted sides so the un-refined blocks pass as plain
doubles, evaluate the cell side once when the functor is built, and let the rotator
take a point whose type differs from the angle's. A dual number times a double is a
dual number times a dual number whose derivatives are zero, so the arithmetic is the
same one with the zeros removed.
Integration cleared the owner and mask images for the whole frame before every image.
On a large detector that is more than three hundred megabytes of writes to reset
pixels of which about one in twenty-five is ever marked, and it cost most of what the
integration kernels themselves cost. The marking kernel gained an unmarking mode - one
kernel, so the two cannot drift apart - and the engine clears whichever way is cheaper
for the frame in front of it, with a flag to force the full clear the first time and
after anything threw. The size test is not decoration: without it, clearing box by box
is slower than the memset on a small detector with many predictions, which is what the
measurement said before it was added.
Faster on thirteen of thirteen matched pairs: refinement by a quarter to a third,
whole-run wall by one to eight per cent depending on how much of the run is the loop.
The two changes pay in opposite regimes - refinement where the loop is processor-bound,
the clear where the detector is large enough for the card to be the constraint.
Every reflection file over seven datasets is byte-identical, and the solver did not
merely land in the same place: it took the same path, agreeing digit for digit on
iteration, residual and Jacobian evaluation counts. A new test runs two mismatched
frames through one engine and compares against a fresh one, which is what a mark left
behind would break.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016NNnL26LAvruQ9eLUUWvrJ