ScaledObservations assembles every integrated partial into rocking events
before the tensor is fitted, and sorted them through a pointer. On the
largest sweep that is 29.1 million parts spread over about 2.5 GB of
88-byte records against a 32 MB L3, and the comparator dereferenced one
of those pointers on every one of roughly 660 million comparisons.
Single-threaded, once per run, at the end of a run whose image loop uses
sixteen threads and a GPU - and for a diagnostic that corrects no
intensity and removes no reflection. It was 8% of one run, 12%, 15% and
16% of others, and 19% of the largest.
The fix is the idiom this repository already uses for this exact sort:
RotationScaleMerge's ingest sorts the same partials on the same key
through a narrow record and its header says why. ScaledObservations was
the one place that read the key back through the pointer instead. The
key now travels with the part. Twelve lines, no bit-packing, and a
reserve of the upper bound so the growth chain stops copying a few
hundred megabytes on a large sweep.
Ties are real and had to be handled rather than argued away: the
predictor emits both Ewald intersections and near the blind region both
land on the same frame, so one crystal has 7424 tied adjacent pairs, 3778
of them in reverse arrival order. The comparator keeps the same four
values in the same order, so introsort - whose every branch is decided by
comparator outcomes at positions and never by element size - makes the
same comparisons and the same swaps and leaves the same permutation.
Checked rather than asserted: a hash of the sorted permutation of all
29.1 million parts, a hash of the returned vector's raw bytes, and the
md5 of the whole .cif all match on eight crystals, each first run twice
on the baseline as a determinism control.
The sort is 1.7-2.1x faster and the function 1.5-2.0x, which is 6.7 s of
single-threaded critical path over those eight crystals and 10.8% of
total wall clock over six timed warm and interleaved.
A wider element costs 7% in the grouping pass that follows. A 24-byte
variant that indexes back instead was built and is marginally faster to
sort and slower overall, so the plainest version is also the fastest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CHMmeM1d489zvNFT7ZMN2P