From ae4ece78054c4b41726f70187328be2aa81a191c Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Tue, 28 Apr 2026 20:58:48 +0200 Subject: [PATCH] perf(htod): migrate PPO trainer + hyperopt adapters to mapped-pinned (6 sites) - trainers/ppo.rs: 2 sites (features, targets in set_raw_market_data) - hyperopt/adapters/ppo.rs: 3 sites (features, targets in upload path; action_indices in run_gpu_backtest forward closure) - hyperopt/adapters/mamba2.rs: 1 site (gpu_to_stream) All clone_htod_f32 and bare stream.clone_htod calls rewritten to mapped_pinned::clone_to_device_{f32,i32}_via_pinned. Audit row appended (Fix 7) in docs/dqn-gpu-hot-path-audit.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/ml/src/hyperopt/adapters/mamba2.rs | 3 ++- crates/ml/src/hyperopt/adapters/ppo.rs | 15 +++++++-------- crates/ml/src/trainers/ppo.rs | 6 ++++-- docs/dqn-gpu-hot-path-audit.md | 3 +++ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/crates/ml/src/hyperopt/adapters/mamba2.rs b/crates/ml/src/hyperopt/adapters/mamba2.rs index f1171497d..25e9c8c57 100644 --- a/crates/ml/src/hyperopt/adapters/mamba2.rs +++ b/crates/ml/src/hyperopt/adapters/mamba2.rs @@ -745,7 +745,8 @@ fn gpu_to_stream( stream: &Arc, ) -> Result { let host = t.to_host(stream)?; - let data = crate::cuda_pipeline::clone_htod_f32(stream, &host)?; + let data = crate::cuda_pipeline::mapped_pinned::clone_to_device_f32_via_pinned(stream, &host) + .map_err(|e| MLError::ModelError(format!("gpu_to_stream upload via pinned: {e}")))?; Ok(StreamTensor { data, shape: t.shape().to_vec(), // cpu-side shape clone diff --git a/crates/ml/src/hyperopt/adapters/ppo.rs b/crates/ml/src/hyperopt/adapters/ppo.rs index d413e6fa1..b68b2d633 100644 --- a/crates/ml/src/hyperopt/adapters/ppo.rs +++ b/crates/ml/src/hyperopt/adapters/ppo.rs @@ -580,14 +580,14 @@ impl PPOTrainer { for (features, _) in training_data { flat_features.extend_from_slice(features); } - match crate::cuda_pipeline::clone_htod_f32(&stream, &flat_features) { + match crate::cuda_pipeline::mapped_pinned::clone_to_device_f32_via_pinned(&stream, &flat_features) { Ok(buf) => { info!("PPO CUDA features uploaded: {} bars × 42 ({:.1} MB)", num_bars, (num_bars * 42 * 4) as f64 / 1_048_576.0); self.features_cuda = Some(buf); } Err(e) => { - return Err(MLError::TrainingError(format!("PPO CUDA features upload FAILED (no CPU fallback): {e}"))); + return Err(MLError::TrainingError(format!("PPO CUDA features upload via pinned FAILED (no CPU fallback): {e}"))); } } @@ -604,7 +604,7 @@ impl PPOTrainer { flat_targets.push(close); flat_targets.push(next_close); } - match crate::cuda_pipeline::clone_htod_f32(&stream, &flat_targets) { + match crate::cuda_pipeline::mapped_pinned::clone_to_device_f32_via_pinned(&stream, &flat_targets) { Ok(buf) => { info!("PPO CUDA targets uploaded: {} bars × 4 ({:.1} MB)", num_bars, (num_bars * 4 * 4) as f64 / 1_048_576.0); @@ -612,7 +612,7 @@ impl PPOTrainer { } Err(e) => { self.features_cuda = None; - return Err(MLError::TrainingError(format!("PPO CUDA targets upload FAILED (no CPU fallback): {e}"))); + return Err(MLError::TrainingError(format!("PPO CUDA targets upload via pinned FAILED (no CPU fallback): {e}"))); } } @@ -1326,10 +1326,9 @@ impl PPOTrainer { } } - // Upload action indices to GPU - stream.clone_htod(&action_indices).map_err(|e| { - MLError::ModelError(format!("PPO backtest action upload: {e}")) - }) + // Upload action indices to GPU via mapped-pinned staging (no explicit HtoD) + crate::cuda_pipeline::mapped_pinned::clone_to_device_i32_via_pinned(stream, &action_indices) + .map_err(|e| MLError::ModelError(format!("PPO backtest action upload via pinned: {e}"))) }, 24, // portfolio_dim: 8 portfolio + 16 multi-timeframe (matches training state layout) )?; diff --git a/crates/ml/src/trainers/ppo.rs b/crates/ml/src/trainers/ppo.rs index 716ea938d..d0f274048 100644 --- a/crates/ml/src/trainers/ppo.rs +++ b/crates/ml/src/trainers/ppo.rs @@ -395,7 +395,8 @@ impl PpoTrainer { flat_features.push(v as f32); } } - let features_buf = crate::cuda_pipeline::clone_htod_f32(&stream, &flat_features)?; + let features_buf = crate::cuda_pipeline::mapped_pinned::clone_to_device_f32_via_pinned(&stream, &flat_features) + .map_err(|e| MLError::ModelError(format!("PPO features upload via pinned: {e}")))?; self.features_raw_cuda = Some(features_buf); // Upload targets [num_bars * 4] @@ -405,7 +406,8 @@ impl PpoTrainer { flat_targets.push(targets.get(i).copied().unwrap_or(0.0) as f32); } } - let targets_buf = crate::cuda_pipeline::clone_htod_f32(&stream, &flat_targets)?; + let targets_buf = crate::cuda_pipeline::mapped_pinned::clone_to_device_f32_via_pinned(&stream, &flat_targets) + .map_err(|e| MLError::ModelError(format!("PPO targets upload via pinned: {e}")))?; self.targets_raw_cuda = Some(targets_buf); self.raw_data_num_bars = num_bars; diff --git a/docs/dqn-gpu-hot-path-audit.md b/docs/dqn-gpu-hot-path-audit.md index 465bddde5..305a80a3a 100644 --- a/docs/dqn-gpu-hot-path-audit.md +++ b/docs/dqn-gpu-hot-path-audit.md @@ -103,3 +103,6 @@ ### Fix 6 — Migrate `gpu_walk_forward.rs` to mapped-pinned (3 sites) `gpu_walk_forward.rs`: 3 `clone_htod_f32` calls in the walk-forward GPU data upload (features, targets, OFI) rewritten to `mapped_pinned::clone_to_device_f32_via_pinned`. + +### Fix 7 — Migrate PPO trainer + hyperopt adapters to mapped-pinned (6 sites) +`trainers/ppo.rs` (2 sites: features, targets in `set_raw_market_data`), `hyperopt/adapters/ppo.rs` (3 sites: features, targets in upload path; action_indices in backtest forward closure), `hyperopt/adapters/mamba2.rs` (1 site: `gpu_to_stream`). All `clone_htod_f32` and bare `stream.clone_htod` calls rewritten to `mapped_pinned::clone_to_device_{f32,i32}_via_pinned`.