diff --git a/image_analysis/image_preprocessing/ImagePreprocessorGPU.cu b/image_analysis/image_preprocessing/ImagePreprocessorGPU.cu index 37c772af..1dfb2052 100644 --- a/image_analysis/image_preprocessing/ImagePreprocessorGPU.cu +++ b/image_analysis/image_preprocessing/ImagePreprocessorGPU.cu @@ -237,9 +237,14 @@ ImagePreprocessorGPU::ImagePreprocessorGPU(const DiffractionExperiment &experime cpu_stats_reg(cpu_stats) { // Setup mask. The same for every worker, so it is uploaded once per GPU and shared; keyed on the // PixelMask's own vector, which the derived table is a pure function of. + // Hoist the accessor and index without the bounds check: this runs once per worker over every + // pixel of the detector - 18 million times per engine on a 16 Mpx one, and an engine is built per + // worker per pass - and .at() on each of them stops the loop vectorising for a bound the loop + // itself already respects. + const std::vector &mask_raw = mask.GetMask(); std::vector mask_vec(npixels); for (int i = 0; i < npixels; i++) - mask_vec[i] = (mask.GetMask().at(i) != 0); + mask_vec[i] = (mask_raw[i] != 0); gpu_mask = SharedDeviceTable(mask.GetMask().data(), npixels, mask_vec.data(), *stream); // Setup GPU settings. The current device, not device 0: workers are pinned round-robin across GPUs, diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index c063e16d..6255c59a 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -2782,7 +2782,12 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b } } - if (result.consensus_cell && write_files && config_.write_merged) { + // Not on the geometry pre-pass. Pass 1 exists to choose the space group and post-refine the + // geometry; pass 2 remakes these files seconds later at the refined geometry, and that is the + // answer anyone reads. Writing them twice costs 0.6 s of a 15 s run - a fifth of the merged + // mmCIF on a large crystal - for a file that is superseded before the run ends. The pass-2 + // quality guard is unaffected: has_merge_statistics is set well above this, at the merge. + if (result.consensus_cell && write_files && config_.write_merged && !geometry_prepass) { phase("Writing reflections"); const ErrorModelReport em_report{ result.error_model_isa > 0 ? fmt::format("{:.2f}", result.error_model_isa) : "?", @@ -2799,7 +2804,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b ScalingResult(indexer->GetIntegrationOutcome()).SaveToFile(config_.output_prefix); } - if (result.consensus_cell && write_files && !config_.model_path.empty()) { + if (result.consensus_cell && write_files && !config_.model_path.empty() && !geometry_prepass) { phase("Validating against model"); const auto data_sg = experiment_.GetSpaceGroupNumber(); // With a reference MTZ the merohedral indexing was already resolved against it (rotation