diff --git a/crates/ml-backtesting/build.rs b/crates/ml-backtesting/build.rs index 343f076d5..f827135f7 100644 --- a/crates/ml-backtesting/build.rs +++ b/crates/ml-backtesting/build.rs @@ -11,9 +11,21 @@ fn main() -> Result<(), String> { // Per pearl_build_rs_rerun_if_env_changed.md: pair every env::var // with rerun-if-env-changed. - // Default sm_86 covers RTX 3050 (local dev). Production Argo workflows - // override via FOXHUNT_CUDA_ARCH=sm_89 (L40S Ada) or sm_90 (H100). - let arch = std::env::var("FOXHUNT_CUDA_ARCH").unwrap_or_else(|_| "sm_86".into()); + // + // Arch detection (mirrors crates/ml-alpha/build.rs:detect_arch — the + // original "mirror" docstring above was aspirational, B-9 cluster + // alpha-rl-mbg2n on H100 caught the gap: ml-alpha picked sm_90 via + // CUDA_COMPUTE_CAP but ml-backtesting fell through to default sm_86 + // → no kernel image error at LobSimCuda::new). Priority: + // 1. CUDA_COMPUTE_CAP (numeric, e.g. "90") — set by alpha-rl-template + // from nvidia-smi inside the compile pod + // 2. FOXHUNT_CUDA_ARCH (sm_-prefixed, e.g. "sm_90") — set by + // lob-backtest-sweep-template + // 3. nvidia-smi --query-gpu=compute_cap at build time + // 4. Default sm_86 (RTX 3050 Ti local dev) + let arch = detect_arch(); + eprintln!(" ml-backtesting: compiling kernels for {arch}"); + println!("cargo:rerun-if-env-changed=CUDA_COMPUTE_CAP"); println!("cargo:rerun-if-env-changed=FOXHUNT_CUDA_ARCH"); println!("cargo:rerun-if-env-changed=CUDA_PATH"); println!("cargo:rerun-if-env-changed=NVCC"); @@ -60,3 +72,38 @@ fn main() -> Result<(), String> { } Ok(()) } + +/// Returns the `sm_` arch string, honoring (in order): +/// `CUDA_COMPUTE_CAP` numeric env → `FOXHUNT_CUDA_ARCH` sm_-prefixed env +/// → `nvidia-smi --query-gpu=compute_cap` → default `sm_86`. +fn detect_arch() -> String { + // 1. Numeric CUDA_COMPUTE_CAP (e.g. "90") from alpha-rl-template. + if let Ok(cap) = std::env::var("CUDA_COMPUTE_CAP") { + let trimmed = cap.trim(); + if !trimmed.is_empty() { + return format!("sm_{trimmed}"); + } + } + // 2. sm_-prefixed FOXHUNT_CUDA_ARCH (e.g. "sm_90") from lob-backtest-sweep. + if let Ok(arch) = std::env::var("FOXHUNT_CUDA_ARCH") { + let trimmed = arch.trim(); + if !trimmed.is_empty() { + return trimmed.to_string(); + } + } + // 3. Query the GPU on this machine (e.g. "9.0" → "90" → "sm_90"). + if let Ok(output) = Command::new("nvidia-smi") + .args(["--query-gpu=compute_cap", "--format=csv,noheader"]) + .output() + { + if output.status.success() { + let s = String::from_utf8_lossy(&output.stdout); + let cap = s.trim().replace('.', ""); + if !cap.is_empty() { + return format!("sm_{cap}"); + } + } + } + // 4. Default — RTX 3050 Ti local dev. + "sm_86".to_string() +}