diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index 8b07a5ca..a6a772cb 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -20,6 +20,7 @@ #include "../reader/JFJochHDF5Reader.h" #include "../common/JFJochMath.h" +#include "../common/ParallelFor.h" #include "../common/Logger.h" #include "../common/AzimuthalIntegrationMapping.h" #include "../common/AzimuthalIntegrationProfile.h" @@ -1290,15 +1291,29 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b validation_settings.quick_integration = false; auto count_indexed = [&](IndexAndRefine &idx, const RotationIndexerResult &r) -> int { idx.ForceRotationIndexerResult(r); - int count = 0; - for (const int ordinal : validation) { + // Fill the spot cache first. It is a plain map filled on demand, and the loop below runs the + // frames in parallel, where a lookup racing an insert is not something the map survives. + // Spot finding stays serial and in frame order, so what ends up in the cache does not depend + // on scheduling; after the first candidate this costs nothing. + for (const int ordinal : validation) + get_spots(ordinal); + // Each frame's verdict is its own - the score is just how many of them index - so they run + // together. Refining one frame's geometry is a few Ceres solves and no GPU, and it is the + // same call the main image loop already makes from every one of its workers on this same + // IndexAndRefine, writing only unit_cells[] under its own mutex. With the candidate forced, + // GetLattice() returns it and the branch that would advance the indexer's own state is never + // reached. The offline solver stops on an iteration count rather than a clock + // (IndexAndRefine::RefineGeometryIfNeeded), so a loaded machine cannot change a verdict. + std::atomic count = 0; + ParallelFor(static_cast(validation.size()), + std::min(validation.size(), config_.nthreads), [&](int i) { DataMessage m{}; - m.number = ordinal; - m.spots = get_spots(ordinal); + m.number = validation[i]; + m.spots = spot_cache.at(validation[i]); if (idx.IndexFrameOnly(m, validation_settings)) - count++; - } - return count; + ++count; + }); + return count.load(); }; // Feed one first-pass scheme (a set of image ordinals) into its own rotation indexer, ready to