From f34d2f4ccaeb6b066e126ffb406737ea7c59f4d5 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 26 Oct 2026 08:13:06 +0100 Subject: [PATCH] Refinement: the weight trials' own slots made while the placement runs Bitwise. MakeOwnSlot copies the model as it was before the topology and reads only what the placement does not change (cell, reflections, the engine's kind, the B ceiling), so the slots are now built on their threads right after the start score, beside the rigid-body and group-B placement, and joined where they were built before (also where the placement throws). The "slots" stage goes from 0.23 s to 0 on 9yl4; the placement is not slowed (0.59 -> 0.60 s). Refined CIF md5 unchanged on 5reo, 5f6m, 9yl4, 7d98, 5f6m MD start (GPU) and 6o2h (CPU); side by side = one at a time. Wall: 5reo 1.46-1.53 -> 1.54, 5f6m 2.26 -> 2.21, 9yl4 7.03 -> 6.83, 7d98 3.65 -> 3.56, 5f6m MD start 4.20 -> 4.15 s. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu (cherry picked from commit 0752a921a4293d4213894e3242c4ef36d8ce2a05) --- .../structure_refinement/Refinement.cpp | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/image_analysis/structure_refinement/Refinement.cpp b/image_analysis/structure_refinement/Refinement.cpp index 5d532a825..8a26c7d26 100644 --- a/image_analysis/structure_refinement/Refinement.cpp +++ b/image_analysis/structure_refinement/Refinement.cpp @@ -1272,6 +1272,35 @@ RefinementResult RefineModel(const gemmi::Structure &placed, const std::vector(std::max({settings.weight_ladder.size() * settings.adp_ladder.size(), + settings.final_ladder.size(), size_t{3}})); + if (settings.max_trials_at_once > 0) + wanted = std::min(wanted, static_cast(settings.max_trials_at_once)); + const std::vector devices = settings.restrained ? OwnSlotDevices(ctx, wanted - 1, settings) : std::vector(); + const size_t slot_threads = std::max(1, settings.nthreads / (devices.size() + 1)); + std::vector> own(devices.size()); + std::vector slot_builders; + // joined before the slots are taken, or here where the placement throws + struct JoinAll { + std::vector &threads; + ~JoinAll() { + for (std::thread &t : threads) + if (t.joinable()) + t.join(); + } + } join_slot_builders{slot_builders}; + for (size_t k = 0; k < devices.size(); k++) + slot_builders.emplace_back([&, k]() { +#ifdef JFJOCH_USE_CUDA + const ScopedGpuPin pin(devices[k] >= 0 ? devices[k] : get_gpu()); +#endif + own[k] = MakeOwnSlot(before_topology, ctx, devices[k], slot_threads, settings); + }); + RefinePlacement(ctx, settings.group_b, result); if (restrained) PlaceRiders(ctx, topo); @@ -1317,28 +1346,9 @@ RefinementResult RefineModel(const gemmi::Structure &placed, const std::vector pos0 = ctx.Positions(); const std::vector b0 = ctx.B(); - // The trials run side by side, as many at a time as the longest set of them has and the cards take engines: - // on the main model and engine, and on copies with engines of their own; each with a share of the threads. std::optional slot_stage("slots"); - int wanted = static_cast(std::max({settings.weight_ladder.size() * settings.adp_ladder.size(), - settings.final_ladder.size(), size_t{3}})); - if (settings.max_trials_at_once > 0) - wanted = std::min(wanted, static_cast(settings.max_trials_at_once)); - const std::vector devices = OwnSlotDevices(ctx, wanted - 1, settings); - const size_t slot_threads = std::max(1, settings.nthreads / (devices.size() + 1)); - std::vector> own(devices.size()); - { - std::vector threads; - for (size_t k = 0; k < devices.size(); k++) - threads.emplace_back([&, k]() { -#ifdef JFJOCH_USE_CUDA - const ScopedGpuPin pin(devices[k] >= 0 ? devices[k] : get_gpu()); -#endif - own[k] = MakeOwnSlot(before_topology, ctx, devices[k], slot_threads, settings); - }); - for (std::thread &t : threads) - t.join(); - } + for (std::thread &t : slot_builders) + t.join(); const size_t main_threads = ctx.nthreads; ctx.nthreads = slot_threads; std::vector trial_slots{{&ctx, &topo}};