MR and model validation: use every card's memory, split MR's functions over the cards
On a multi-GPU host the structure stage ran essentially on one card: the rotation function's launches all went to the search's card, and the rigid-body engines of model validation and MR were admitted only within half of that one card's budget, although RigidBodyGPUPool already placed engines on other cards. On a 4x T4 server 4nwv's MR rotation function took 75.8 s on one card, validation queued its 10 fits on one engine and MR's rescoring and null got one engine. - PlanModelValidationGPU / PlanMolecularReplacementGPU: this card is planned exactly as on a machine with it alone (GPU-vs-CPU placement unchanged: 80% of the total, less what is resident). The rigid-body engines it has no room for go on the other cards in turn, each against its own budget (80% of its total, less the resident bytes, counted on every card) within half of it. MR also puts a copy of the molecular transform on each other card within half of its budget. - RigidBodyGPUPool::Reserve takes per-card engine counts. - Validation forwards the most it holds on any card as gpu_bytes to a validation beside, and may run one beside only where twice that fits every card's budget. - MR splits every rotation and translation function into contiguous slices of the orientation list over the cards holding a transform copy (and room for the call's buffers), one thread per card, gathered back in order. Each orientation is scored by itself (one block / one map), nothing is summed across orientations, so N cards give the bytes of one. - ScoreEngines keeps a room per card; score engines always go on the search's own card; function buffers are held on the card they run on. Byte-identical at N=1 (workstation, one RTX 5080): timing benchmark (myob, cytc, 4nwv) and 4nwv, 8tyy, 6oel, 3inp with --model give the same p.hkl, MTZ data, p_model.pdb, maps and report against rc176. New tests: per-card plans on four 15.6 GB cards (validation 1/3/3/3 engines, MR T+1/T+2/T+2/T+2), and the split driven over 1-4 virtual cards on one device, bitwise equal to one card. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXc1zD1yxHqaR1DGdCTHL4
This commit is contained in:
1 parent
19b135186d
commit
bfc6ca8487
9 files changed
+454
-54
No files matched your search
@@ -799,6 +799,7 @@ ModelValidationResult Validate(const std::vector<MergedReflection> &merged,
|
||||
cuda_trim_allocation_pool();
|
||||
ModelValidationGPUNeed need;
|
||||
need.card = get_gpu_total_memory(device);
|
||||
need.other_cards = OtherCardsTotalMemory(device);
|
||||
need.resident = schedule.gpu_resident_bytes;
|
||||
need.beside = schedule.gpu_beside_bytes;
|
||||
sf_engine = StructureFactorsEngine(device, st.models[0], ucell, *sg, d_min, logger);
|
||||
@@ -837,8 +838,8 @@ ModelValidationResult Validate(const std::vector<MergedReflection> &merged,
|
||||
need.scale_fit = ModelScaleGPUPool::DeviceBytes(obs.size(), 1);
|
||||
need.scale_fit_engines = SCALE_GPU_ENGINES;
|
||||
|
||||
gpu_plan = PlanModelValidationGPU(need, static_cast<size_t>(get_gpu_count()));
|
||||
gpu_bytes = gpu_plan.bytes;
|
||||
gpu_plan = PlanModelValidationGPU(need);
|
||||
gpu_bytes = gpu_plan.most_on_a_card;
|
||||
may_run_beside = gpu_plan.may_run_beside;
|
||||
|
||||
std::string parts;
|
||||
@@ -858,6 +859,12 @@ ModelValidationResult Validate(const std::vector<MergedReflection> &merged,
|
||||
part(fmt::format("the null's to {:.2f} A", null_d_min), gpu_plan.null_structure_factors,
|
||||
need.null_structure_factors, 0);
|
||||
part("rigid body", gpu_plan.rigid_body_engines > 0, need.rigid_body, gpu_plan.rigid_body_engines);
|
||||
if (gpu_plan.rigid_body_engines > gpu_plan.rigid_body_per_card[0]) {
|
||||
std::string per_card;
|
||||
for (size_t n : gpu_plan.rigid_body_per_card)
|
||||
per_card += fmt::format("{}{}", per_card.empty() ? "" : "/", n);
|
||||
parts += fmt::format(" - {} on the cards from this one", per_card);
|
||||
}
|
||||
part("scale fits", gpu_plan.scale_fit_engines > 0, need.scale_fit, gpu_plan.scale_fit_engines);
|
||||
logger.Info("Model validation: {} - {} held of a {} budget, 80% of the card's {}{}{}", parts,
|
||||
GBytes(gpu_plan.bytes), GBytes(gpu_plan.budget), GBytes(need.card),
|
||||
@@ -927,7 +934,7 @@ ModelValidationResult Validate(const std::vector<MergedReflection> &merged,
|
||||
|
||||
#ifdef JFJOCH_USE_CUDA
|
||||
if (rigid_body_engines && gpu_plan.rigid_body_engines > 0) {
|
||||
rigid_body_engines->Reserve(gpu_plan.rigid_body_engines);
|
||||
rigid_body_engines->Reserve(gpu_plan.rigid_body_per_card);
|
||||
rigid_body_pool = rigid_body_engines.get();
|
||||
}
|
||||
if (gpu_plan.scale_fit_engines > 0) {
|
||||
@@ -1888,7 +1895,7 @@ size_t ModelGPUBudget(size_t card, size_t resident) {
|
||||
return usable > resident ? usable - resident : 0;
|
||||
}
|
||||
|
||||
ModelValidationGPUPlan PlanModelValidationGPU(const ModelValidationGPUNeed &need, size_t cards) {
|
||||
ModelValidationGPUPlan PlanModelValidationGPU(const ModelValidationGPUNeed &need) {
|
||||
ModelValidationGPUPlan plan;
|
||||
plan.budget = ModelGPUBudget(need.card, need.resident);
|
||||
auto take = [&](size_t bytes, size_t limit) {
|
||||
@@ -1903,22 +1910,55 @@ ModelValidationGPUPlan PlanModelValidationGPU(const ModelValidationGPUNeed &need
|
||||
plan.scale_fit_engines = need.scale_fit_engines > 0 && take(need.scale_fit, plan.budget) ? 1 : 0;
|
||||
|
||||
// More engines only within half of the budget (see the declaration).
|
||||
const size_t room = std::min(plan.budget / 2, plan.budget > need.beside ? plan.budget - need.beside : 0);
|
||||
const auto room = [&](size_t budget) { return std::min(budget / 2, budget > need.beside ? budget - need.beside : 0); };
|
||||
while (plan.rigid_body_engines > 0 && plan.rigid_body_engines < need.rigid_body_engines
|
||||
&& take(need.rigid_body, room))
|
||||
&& take(need.rigid_body, room(plan.budget)))
|
||||
plan.rigid_body_engines++;
|
||||
while (plan.scale_fit_engines > 0 && plan.scale_fit_engines < need.scale_fit_engines
|
||||
&& take(need.scale_fit, room))
|
||||
&& take(need.scale_fit, room(plan.budget)))
|
||||
plan.scale_fit_engines++;
|
||||
|
||||
// The rigid-body engines this card has no room for, on the other cards in turn.
|
||||
plan.rigid_body_per_card.assign(1 + need.other_cards.size(), 0);
|
||||
plan.rigid_body_per_card[0] = plan.rigid_body_engines;
|
||||
std::vector<size_t> held(plan.rigid_body_per_card.size(), 0);
|
||||
held[0] = plan.bytes;
|
||||
for (bool added = plan.rigid_body_engines > 0; added;) {
|
||||
added = false;
|
||||
for (size_t k = 1; k < held.size() && plan.rigid_body_engines < need.rigid_body_engines; k++)
|
||||
if (held[k] + need.rigid_body <= room(ModelGPUBudget(need.other_cards[k - 1], need.resident))) {
|
||||
held[k] += need.rigid_body;
|
||||
plan.rigid_body_per_card[k]++;
|
||||
plan.rigid_body_engines++;
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
plan.most_on_a_card = *std::max_element(held.begin(), held.end());
|
||||
|
||||
const size_t cards = held.size();
|
||||
size_t smallest_budget = plan.budget;
|
||||
for (size_t total : need.other_cards)
|
||||
smallest_budget = std::min(smallest_budget, ModelGPUBudget(total, need.resident));
|
||||
const size_t whole = (plan.structure_factors ? need.structure_factors : 0)
|
||||
+ (plan.null_structure_factors ? need.null_structure_factors : 0)
|
||||
+ (plan.rigid_body_engines > 0 ? need.rigid_body * need.rigid_body_engines : 0)
|
||||
+ (plan.scale_fit_engines > 0 ? need.scale_fit * need.scale_fit_engines : 0);
|
||||
plan.may_run_beside = 2 * whole <= cards * plan.budget;
|
||||
plan.may_run_beside = 2 * whole <= cards * plan.budget && 2 * plan.most_on_a_card <= smallest_budget;
|
||||
return plan;
|
||||
}
|
||||
|
||||
std::vector<size_t> OtherCardsTotalMemory(int device) {
|
||||
std::vector<size_t> out;
|
||||
#ifdef JFJOCH_USE_CUDA
|
||||
const int cards = get_gpu_count();
|
||||
for (int k = 1; k < cards; k++)
|
||||
out.push_back(get_gpu_total_memory((device + k) % cards));
|
||||
#else
|
||||
(void) device;
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
ModelValidationResult ValidateAgainstModel(const std::vector<MergedReflection> &merged,
|
||||
const UnitCell &cell,
|
||||
const std::string &model_path,
|
||||
|
||||
Reference in new issue
Block a user