Refinement: density records sent from pinned memory, and the L-BFGS direction in fused sweeps

Two byte-identical cuts to the per-evaluation host chain, which on a large model is twice the GPU's
work per evaluation (34 ms against 16 ms of kernels per weight-trial slot, measured in nsys) and so bounds
the refinement on one card with three slots, and the whole of it on a card with one slot.

- The gather's per-atom density records (38 MB on a 242k-atom model, sent on every B-factor evaluation)
  live in a PinnedVector and go up by DMA; the boxes go through a pinned staging buffer of the density
  object. ModelDensityGPU::SetAtoms and RefinementGPUEngine::SetDensities take a pointer and a count.
- RefinementMinimiser::Direction takes each pair's dot product in the same sweep that applies the previous
  pair's update to q: the dot products are serial chains of n additions, and the update's memory traffic
  now hides under them (20 -> 13 ms per call at 365k parameters). The same operations per element in the
  same order; the loop-2 coefficient stays written as (a[i] - b) with b on its own, because a[i] - rho * dot
  contracts into a fused multiply-add and changes the bits (checked bitwise in a standalone harness).
- A debug line with the GPU engine's bytes and the budget.

4nwv (open arm, 242k atoms), live flags, one lock hold, loaded box: refinement 42.35 -> 38.40 s;
refine.gpu.upload 8.14 -> 3.94 s. All outputs identical but provenance lines.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
This commit is contained in:
leonarski_fandClaude Fable 5.1 committed 2026-10-26 22:48:28 +01:00
1 parent 3189f71e8f
commit b16873036c
7 files changed
+93 -40

No files matched your search

@@ -916,9 +916,11 @@ void MakeEngine(Context &ctx, bool gpu, const RefinementSettings &settings, Logg
ctx.b_ceiling, budget, &bytes, settings.nthreads);
ctx.device = device;
ctx.engine_bytes = bytes;
if (ctx.engine)
if (ctx.engine) {
ctx.scale_gpu = std::make_unique<ModelScaleGPUPool>(device, ctx.refl.Size(), 1);
else
logger.Debug("Refinement: GPU engine {:.2f} GB of a {:.2f} GB budget (scale pool {:.2f} GB)", bytes / 1e9,
budget / 1e9, ModelScaleGPUPool::DeviceBytes(ctx.refl.Size(), 1, true) / 1e9);
} else
logger.Info("Refinement: on the CPU - the GPU cannot take this case or {:.1f} GB is over its budget",
bytes / 1e9);
} catch (const JFJochException &e) {