72 lines
2.9 KiB
Markdown
72 lines
2.9 KiB
Markdown
# 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<f32>`)
|
|
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<GpuBacktestEvaluator>` 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<Vec<f32>>` field + lazy init
|
|
- `val_closes_gpu: Option<(Vec<f32>, Vec<f32>)>` field + lazy init
|
|
- `val_ofi_gpu: Option<Vec<f32>>` 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
|