diff --git a/image_analysis/scale_merge/RotationScaleMerge.cpp b/image_analysis/scale_merge/RotationScaleMerge.cpp index 8b3e371aa..c251c7fb7 100644 --- a/image_analysis/scale_merge/RotationScaleMerge.cpp +++ b/image_analysis/scale_merge/RotationScaleMerge.cpp @@ -712,7 +712,9 @@ void RotationScaleMerge::BuildInRangeObservations(std::vector &keys) { if (!keep[src]) { ++src; continue; } ++src; ObsIngest &obs = partials_ingest[at]; - obs.h = r.h; obs.k = r.k; obs.l = r.l; + obs.h = static_cast(r.h); + obs.k = static_cast(r.k); + obs.l = static_cast(r.l); obs.frame = o; obs.image_number = r.image_number; obs.d = r.d; @@ -734,7 +736,10 @@ void RotationScaleMerge::BuildInRangeObservations(std::vector &keys) { gpu_->SetPartialsLayout(n_keep, n_frames, frame_start.data(), frame_count.data()); using F = RotationScaleMergeGPU::ObsField; - constexpr int STAGE_SLICE = 8 << 20; + // Two million observations a slice: the thirteen staging arrays are then under a hundred + // megabytes together, where eight million put four hundred on top of the ingest's peak, and a + // slice still spans tens of frames for the threads to share. + constexpr int STAGE_SLICE = 2 << 20; constexpr int NFLOAT = 11; int max_frame_obs = 0; for (int o = 0; o < n_frames; ++o) max_frame_obs = std::max(max_frame_obs, static_cast(frame_count[o])); diff --git a/image_analysis/scale_merge/RotationScaleMerge.h b/image_analysis/scale_merge/RotationScaleMerge.h index 533242ad3..81cf8164c 100644 --- a/image_analysis/scale_merge/RotationScaleMerge.h +++ b/image_analysis/scale_merge/RotationScaleMerge.h @@ -144,16 +144,20 @@ private: // observation dump): every immutable per-obs field is uploaded straight from the source // reflections inside BuildInRangeObservations, and the host keeps only what its own remaining // stages touch - the geometry smoothing (delta_phi, then the recomputed partiality), plus the - // rocking-event walk's inputs. Forty bytes against the Obs's eighty, and on that path the fat + // rocking-event walk's inputs. Thirty-two bytes against the Obs's eighty, and on that path the fat // record is never materialised at all: on a fine-sliced long axis it alone is many gigabytes, // held beside the source reflections it was copied from. struct ObsIngest { - int32_t h, k, l, frame; - float image_number, d, zeta, delta_phi, partiality; + // Sixteen bits are plenty for a Miller index - |h| <= a / d_min, in the hundreds even on the + // longest axis at atomic resolution - and on a long axis this array sets the ingest's memory + // high-water mark, so each index costs two bytes here, not four. + int16_t h, k, l; // The rocking-event usability test (RockingEventFrames), evaluated on the ingest-time // values - which is what that walk reads on the resident path, where the host corr is - // never refreshed (see partials_released). + // never refreshed (see partials_released). Beside the indices, where it fills their padding. uint8_t rock_ok; + int32_t frame; + float image_number, d, zeta, delta_phi, partiality; }; // The narrow per-observation record the ingest sort orders: the raw hkl the runs are cut on, the