# GPU-Native Validation + H100 Performance Optimizations ## Goal Replace the CPU validation path with the existing `GpuBacktestEvaluator`, delete all dead CPU code, and apply remaining performance optimizations to push epoch time from 15.6s toward <5s. ## Architecture The training loop's `compute_validation_loss()` in `metrics.rs` currently: 1. Assembles states on CPU (`Vec`) 2. Uploads to GPU (`clone_htod_f32_to_bf16`) 3. Runs cuBLAS forward (`compute_q_values`) 4. Downloads Q-values to CPU (`dtoh_bf16_to_f32`) — **2s sync** 5. CPU argmax over Q-values 6. CPU PnL computation 7. CPU Sharpe ratio Steps 4-7 are pure CPU with a mandatory GPU→CPU sync. The `GpuBacktestEvaluator` (2086 lines, `gpu_backtest_evaluator.rs`) already does all of this on GPU — used by hyperopt adapters but NOT by the training loop. ### Changes #### 1. Wire GpuBacktestEvaluator into Training Loop - Add `gpu_evaluator: Option` field to `DQNTrainer` - Initialize in constructor from validation data + `DqnBacktestConfig` - Replace `compute_validation_loss()` body: call `gpu_evaluator.evaluate_dqn_graphed()` with current weights, extract `WindowMetrics.sharpe` - The evaluator pre-uploads validation data to GPU once at construction (not per epoch) #### 2. Delete Dead CPU Validation Code Remove from `DQNTrainer`: - `val_features_gpu: Option>` field + lazy init - `val_closes_gpu: Option<(Vec, Vec)>` field + lazy init - `val_ofi_gpu: Option>` field + lazy init - CPU state assembly loop (lines 485-509 in metrics.rs) - `clone_htod_f32_to_bf16` call - `dtoh_bf16_to_f32` readback - CPU argmax loop (lines 535-555) - CPU Sharpe computation (lines 568-584) - All associated `use` imports #### 3. BF16 Backward TF32 Change `batched_backward.rs`: - 2 GEMMs from `CUBLAS_COMPUTE_32F` → `CUBLAS_COMPUTE_32F_FAST_TF32` - Both activation gradient (BF16→BF16) and weight gradient (BF16→F32) paths - TF32's 10-bit mantissa is sufficient for gradients (Adam momentum smooths noise) #### 4. Training Loop Optimizations (already in working tree) - Vaccine sample only every 10th step (was: every step, wasting 43ms) - Training guard every 5th step (was: every step, 12ms overhead) - Single write lock for sample+step (was: read→write cycling, 43ms contention) - Causal intervention interval 10→50 - Remove all H100_DIAG tracing::info ## Expected Impact | Optimization | Per-epoch savings | |---|---| | GPU-native validation (2s CPU → 50ms GPU) | -1.95s | | BF16 backward TF32 (2-3x GEMM throughput) | -3-5s | | Single-sample + guard + lock | -3.6s | | **Total estimated** | **~8-10s** | | **Target epoch time** | **~5-7s** | ## Testing - Smoke tests: verify Sharpe computation matches between old CPU path and new GPU evaluator (within BF16 tolerance) - H100 deployment: confirm epoch timing improvement - Loss convergence: verify training quality unchanged