Wire BF16/FP16 mixed precision end-to-end for DQN and PPO with auto-detection from GPU name (Ampere+ → BF16, Volta/Turing → FP16). Add hidden_dim_base to hyperopt and wire through training/eval binaries. Reduce GPU sync points: make DQN NaN checks periodic (every 100 steps), replace PPO GAE GPU round-trip with pure CPU implementation. Cache training data across hyperopt trials for all 10 models via Arc. Batch DQN experience storage (128x fewer lock acquisitions). Correct VRAM constants and batch bounds for all 9 supervised model adapters. 28 files changed, +1207/-208 lines. 2418 tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.5 KiB
GPU Blockers Validation — Design
Date: 2026-03-01 Status: Approved Goal: Fix 5 blockers preventing GPU utilization from reaching 80%+ after initial dynamic-sizing changes
Findings Summary
Post-implementation validation of the GPU dynamic-sizing changes (20 files, +353/-84) found 3 critical and 2 important blockers.
Critical Fixes
Fix 1: Wire BF16 Mixed Precision End-to-End
Problem: Detection works (HardwareBudget::detect() → supports_bf16()) but result stored in _mixed_precision_detected (unused). Chain broken at 3 points:
DQNConfighas nomixed_precisionfieldDQNHyperparametershas nomixed_precisionfieldDQNAgent::new()hardcodesmixed_precision: None
Fix:
- Add
mixed_precision: Option<MixedPrecisionConfig>toDQNConfig(dqn/dqn.rs) - Add
mixed_precision: Option<MixedPrecisionConfig>toDQNHyperparameters(trainers/dqn/config.rs) - In
trainer.rs: rename_mixed_precision_detected→mixed_precision_detected, pass to DQNConfig - In
DQN::new(): passconfig.mixed_precision→QNetworkConfig.mixed_precision - In hyperopt
train_with_params(): auto-detect and pass mixed precision
Files: dqn/dqn.rs, trainers/dqn/config.rs, trainers/dqn/trainer.rs, dqn/agent.rs, hyperopt/adapters/dqn.rs
Fix 2: Wire hidden_dim_base Through Training + Eval Binaries
Problem: train_baseline_rl.rs hardcodes hidden_dims: vec![128, 64] in both train_dqn_fold() and train_ppo_fold(). Hyperopt discovers optimal hidden_dim_base but it's silently ignored at train time.
Fix:
- In
train_dqn_fold(): readhidden_dim_basefrom hyperopt JSON, compute[base, base/2, base/4] - In
train_ppo_fold(): readhidden_dim_base, compute policy[base, base/2]and value[base*2, base, base/2] - In
evaluate_baseline.rs: same treatment for eval DQN/PPO config construction - Fallback to
[128, 64]when key absent (backward compat with old hyperopt results)
Files: examples/train_baseline_rl.rs, examples/evaluate_baseline.rs
Fix 3: Reduce GPU Sync Points (5 per step → 1 periodic)
Problem: Four .to_vec1() NaN checks (BUG #14) + one .to_scalar() force GPU queue flush every training step. GPU cannot pipeline.
Fix:
- Replace per-step NaN detection with periodic check (every 100 steps)
- Use GPU-side
Tensor::any()+Tensor::isnan()to check NaN without CPU transfer - Only pull
.to_vec1()on the periodic check or when GPU-side NaN detected - Keep the
.to_scalar()inestimate_avg_q_valuebut call it every 100 steps instead of every step
Files: dqn/dqn.rs (compute_loss_internal), trainers/dqn/trainer.rs (Q-value monitoring frequency)
Important Fixes
Fix 4: Cache Loaded Data Across Trials
Problem: Every hyperopt trial re-loads 36 .dbn.zst files, decompresses, extracts 51 features. GPU idle for minutes per trial boundary.
Fix:
- In hyperopt runner, load data once into
Arc<Vec<TrainingData>>before trial loop - Pass shared reference to each
train_with_params()call - Add
train_with_preloaded_data()variant that accepts pre-loaded data
Files: hyperopt/adapters/dqn.rs, hyperopt/adapters/ppo.rs
Fix 5: Batch Experience Storage (Remove Per-Sample Lock)
Problem: agent.write().await acquired per sample (100K+ per epoch). Serializes experience collection.
Fix:
- Collect actions + experiences into
Vecduring batch processing - Single
agent.write().awaitto store entire batch at once - Move
track_action()into batched path
Files: trainers/dqn/trainer.rs (experience collection loop)
Files Changed Summary
| File | Fix | Change |
|---|---|---|
dqn/dqn.rs |
1,3 | Add mixed_precision to DQNConfig, periodic NaN check |
trainers/dqn/config.rs |
1 | Add mixed_precision to DQNHyperparameters |
trainers/dqn/trainer.rs |
1,5 | Wire mixed precision, batch experience storage |
dqn/agent.rs |
1 | Pass config.mixed_precision to QNetworkConfig |
hyperopt/adapters/dqn.rs |
1,4 | Auto-detect AMP, pre-loaded data path |
hyperopt/adapters/ppo.rs |
4 | Pre-loaded data path |
examples/train_baseline_rl.rs |
2 | Read hidden_dim_base from JSON |
examples/evaluate_baseline.rs |
2 | Read hidden_dim_base from JSON |
Expected Outcome
With all 5 fixes:
- TENSOR_ACTIVE: 0% → 40-60% (BF16 actually engaged)
- GPU_UTIL: 12% → 60-80% (fewer sync stalls, larger models)
- Trial startup: minutes → seconds (cached data)
- Training step throughput: ~5x (1 sync per 100 steps vs 5 per step)