Skip to content

Commit bbc8b19

Browse files
committed
feat: add d2h benches
1 parent 6e40eeb commit bbc8b19

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,10 @@ required-features = ["dual-stream-rng"]
325325
name = "cuda_fgn_analysis"
326326
required-features = ["cuda-native"]
327327

328+
[[example]]
329+
name = "cuda_d2h_bw"
330+
required-features = ["cuda-native"]
331+
328332
[features]
329333
ai = ["dep:stochastic-rs-ai", "stochastic-rs-ai/quant"]
330334
cuda-native = ["dep:cudarc", "cudarc/cufft", "stochastic-rs-stochastic/cuda-native"]

examples/cuda_d2h_bw.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//! Isolated device->host strategy probe: full cost of producing an owned
2+
//! `Vec<f32>` on the host from a device buffer.
3+
//! Run: cargo run --example cuda_d2h_bw --features cuda-native --release
4+
use std::time::Instant;
5+
6+
use cudarc::driver::CudaContext;
7+
use cudarc::driver::sys;
8+
use rayon::prelude::*;
9+
10+
fn fresh_vec(n: usize) -> Vec<f32> {
11+
let mut v = Vec::<f32>::with_capacity(n);
12+
#[allow(clippy::uninit_vec)]
13+
unsafe {
14+
v.set_len(n)
15+
};
16+
v
17+
}
18+
19+
fn main() {
20+
let n: usize = 256 * 1024 * 1024;
21+
let bytes = n * std::mem::size_of::<f32>();
22+
let iters = 10;
23+
let gb = bytes as f64 / 1e9;
24+
25+
let ctx = CudaContext::new(0).unwrap();
26+
let stream = ctx.new_stream().unwrap();
27+
let d = stream.alloc_zeros::<f32>(n).unwrap();
28+
29+
let mut warm = fresh_vec(n);
30+
for _ in 0..3 {
31+
stream.memcpy_dtoh(&d, &mut warm).unwrap();
32+
stream.synchronize().unwrap();
33+
}
34+
35+
// Current production path: DMA straight into a fresh pageable Vec.
36+
let t = Instant::now();
37+
for _ in 0..iters {
38+
let v: Vec<f32> = stream.clone_dtoh(&d).unwrap();
39+
std::hint::black_box(&v);
40+
}
41+
let a = t.elapsed().as_secs_f64() / iters as f64;
42+
43+
// Cached pinned staging buffer, then a serial copy into a fresh output Vec.
44+
let pin = unsafe { cudarc::driver::result::malloc_host(bytes, 0) }.unwrap() as *mut f32;
45+
let staging = unsafe { std::slice::from_raw_parts_mut(pin, n) };
46+
stream.memcpy_dtoh(&d, staging).unwrap();
47+
stream.synchronize().unwrap();
48+
49+
let t = Instant::now();
50+
for _ in 0..iters {
51+
stream.memcpy_dtoh(&d, staging).unwrap();
52+
stream.synchronize().unwrap();
53+
let mut out = fresh_vec(n);
54+
out.copy_from_slice(staging);
55+
std::hint::black_box(&out);
56+
}
57+
let b = t.elapsed().as_secs_f64() / iters as f64;
58+
59+
// Cached pinned staging buffer, then a rayon-parallel copy (spreads the
60+
// first-touch page faults across cores) into a fresh output Vec.
61+
let t = Instant::now();
62+
for _ in 0..iters {
63+
stream.memcpy_dtoh(&d, staging).unwrap();
64+
stream.synchronize().unwrap();
65+
let mut out = fresh_vec(n);
66+
let chunk = 1 << 20;
67+
out
68+
.par_chunks_mut(chunk)
69+
.zip(staging.par_chunks(chunk))
70+
.for_each(|(o, s)| o.copy_from_slice(s));
71+
std::hint::black_box(&out);
72+
}
73+
let e = t.elapsed().as_secs_f64() / iters as f64;
74+
75+
unsafe {
76+
let _ = cudarc::driver::result::free_host(pin as *mut std::ffi::c_void);
77+
}
78+
79+
// Lower bound: reuse one registered, already-faulted Vec, DMA only.
80+
let mut reuse = fresh_vec(n);
81+
let rc = unsafe { sys::cuMemHostRegister_v2(reuse.as_mut_ptr() as *mut _, bytes, 0) };
82+
assert_eq!(rc, sys::CUresult::CUDA_SUCCESS);
83+
let t = Instant::now();
84+
for _ in 0..iters {
85+
stream.memcpy_dtoh(&d, reuse.as_mut_slice()).unwrap();
86+
stream.synchronize().unwrap();
87+
}
88+
let dd = t.elapsed().as_secs_f64() / iters as f64;
89+
unsafe {
90+
sys::cuMemHostUnregister(reuse.as_mut_ptr() as *mut _);
91+
}
92+
93+
println!(
94+
"buffer = {gb:.3} GB, iters = {iters}, threads = {}\n",
95+
rayon::current_num_threads()
96+
);
97+
println!(
98+
"A) clone_dtoh -> fresh pageable Vec : {:7.1} ms ({:5.2} GB/s)",
99+
a * 1e3,
100+
gb / a
101+
);
102+
println!(
103+
"B) pinned staging + serial copy : {:7.1} ms ({:5.2} GB/s)",
104+
b * 1e3,
105+
gb / b
106+
);
107+
println!(
108+
"E) pinned staging + parallel copy : {:7.1} ms ({:5.2} GB/s)",
109+
e * 1e3,
110+
gb / e
111+
);
112+
println!(
113+
"D) reuse registered Vec (DMA only) : {:7.1} ms ({:5.2} GB/s)",
114+
dd * 1e3,
115+
gb / dd
116+
);
117+
}

0 commit comments

Comments
 (0)