build.rs compiles every .cu under crates/ml-backtesting/cuda/ to \$OUT_DIR/<name>.cubin via nvcc (no nvrtc per feedback_no_nvrtc.md); pairs every env::var with rerun-if-env-changed per pearl_build_rs_rerun_if_env_changed.md. Default arch sm_86 (RTX 3050 local); production sets FOXHUNT_CUDA_ARCH=sm_89 (L40S) or sm_90 (H100). First kernel: book_update_apply_snapshot — block-per-backtest replace of the 10-level book from a broadcast MBP-10 snapshot. Single-writer per level (thread tid = level idx), no atomics per feedback_no_atomicadd.md. lob_state.cuh defines the canonical per-block shared-mem layout that all subsequent kernels share (Book struct in this commit; Orders, Pos, IsvKellyState[5] added in C5-C7). LobSimCuda host struct (in src/sim.rs) constructed from an &MlDevice; owns per-backtest device book state + mapped-pinned input snapshots. apply_snapshot launches the kernel and synchronises; read_books drains device state for tests. Three Ring 1 fixture tests (book_update_replace, _two_step, _n_backtests=4) — N≤4 bit-exact GPU oracle assertions loaded from JSON. All three pass on RTX 3050 locally. Verifies the build-script → cubin → cudarc.load_cubin → kernel launch → DtoH read pipeline end-to-end. cudarc added as direct path dep (../../vendor/cudarc) since it's not in [workspace.dependencies] — same vendored fork ml-alpha uses. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
63 lines
2.3 KiB
Rust
63 lines
2.3 KiB
Rust
//! Cubin compilation for the LOB simulator CUDA kernels.
|
|
//! Mirrors crates/ml-alpha/build.rs.
|
|
//! Per feedback_no_nvrtc.md: cubins only, no runtime nvrtc.
|
|
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
fn main() -> Result<(), String> {
|
|
let cuda_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("cuda");
|
|
let out_dir = PathBuf::from(std::env::var("OUT_DIR").map_err(|e| e.to_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());
|
|
println!("cargo:rerun-if-env-changed=FOXHUNT_CUDA_ARCH");
|
|
println!("cargo:rerun-if-env-changed=CUDA_PATH");
|
|
println!("cargo:rerun-if-env-changed=NVCC");
|
|
|
|
let nvcc = std::env::var("NVCC")
|
|
.map(PathBuf::from)
|
|
.unwrap_or_else(|_| PathBuf::from("nvcc"));
|
|
|
|
if !cuda_dir.exists() {
|
|
// No kernels yet (e.g. before C4 lands them) — quietly noop.
|
|
return Ok(());
|
|
}
|
|
|
|
println!("cargo:rerun-if-changed={}", cuda_dir.display());
|
|
let entries = std::fs::read_dir(&cuda_dir).map_err(|e| format!("read cuda dir: {e}"))?;
|
|
for entry in entries {
|
|
let entry = entry.map_err(|e| e.to_string())?;
|
|
let path = entry.path();
|
|
if path.extension().and_then(|s| s.to_str()) != Some("cu") {
|
|
continue;
|
|
}
|
|
let stem = path
|
|
.file_stem()
|
|
.ok_or("cu file has no stem")?
|
|
.to_string_lossy()
|
|
.to_string();
|
|
let cubin = out_dir.join(format!("{stem}.cubin"));
|
|
println!("cargo:rerun-if-changed={}", path.display());
|
|
|
|
let mut cmd = Command::new(&nvcc);
|
|
cmd.args(["--cubin", "-O3", "-std=c++17"])
|
|
.arg(format!("-arch={arch}"))
|
|
.arg("-I")
|
|
.arg(&cuda_dir)
|
|
.arg("-o")
|
|
.arg(&cubin)
|
|
.arg(&path);
|
|
let status = cmd
|
|
.status()
|
|
.map_err(|e| format!("spawn nvcc for {}: {e}", path.display()))?;
|
|
if !status.success() {
|
|
return Err(format!("nvcc failed for {}", path.display()));
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|