The CPU rotation function interpolates the molecular transform at points scattered over its grid, four
cache lines per point. Once the grid is far larger than the caches (835 MB for 4nwv: 16.9e9
interpolations over 17255 orientations) the loop waits on memory, at ~92 ns per interpolation per core
on an Apple M6. MolecularTransform::Prefetch() asks for the rows a point's corners lie on, 4
reflections ahead of the one being computed, through one portable helper (__builtin_prefetch under
GCC/Clang, nothing elsewhere). Same values, same order, so the output is bit-identical.
Only for a transform of 256 MB or more: a smaller one sits mostly in the caches, where the extra
address arithmetic only costs. In a standalone copy of the loop on the M6: 30% slower at 13-90 MB,
even at ~130-175 MB, 1.5x faster at 400 MB, 1.75-1.8x at 835 MB (prefetch distance 4 to 8 equally
good).
M6 Mac mini, 4nwv, alternating runs on a quiet machine, against the previous commit:
- rotation function 139.1/138.8 s -> 79.1/79.3/78.9 s
- wall 400.7/415.7 s -> 338.0/343.1/345.6 s
- --no-gpu: 450.4 s -> 385.4 s (rotation 140.3 -> 84.1 s)
- 9s02 (14k reflections, 13 MB transform, below the threshold): rotation 2.83 s -> 2.98 s, wall
79.4 -> 80.4 s
p.hkl, p_model.cif/pdb, the maps and the MTZ/CIF reflection data are byte-identical in every run
(the files differ only in the git tag and command line in their headers). [MolecularReplacement]
tests pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018My7tqt8a4LyKKjP3FhScT