use cudarc::{ driver::{CudaContext, DriverError, LaunchConfig, PushKernelArg}, nvrtc::Ptx, }; fn main() -> Result<(), DriverError> { let ctx = CudaContext::new(0)?; let stream = ctx.default_stream(); let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?; let f = module.load_function("sin_kernel")?; let n = 3i32; let a_host = [1.0, 2.0, 3.0]; let a_dev = stream.clone_htod(&a_host)?; let mut b_dev = stream.alloc_zeros::(n as usize)?; // we can safely create a second stream using [CudaStream::fork()]. // This synchronizes with the source stream, so // the `memcpy_vtod` & `alloc_zeros` above will complete **before** // work on this stream can start. let stream2 = stream.fork()?; // now we launch this work on the other stream let mut builder = stream2.launch_builder(&f); builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this. builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice builder.arg(&n); unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?; // cudarc automatically manages multi stream synchronization, // so even though we launched the above on a separate stream, // doing this device to host transfer will still properly synchronize. // a_dev doesn't need to synchronize at all since we specified it is just // being read from. // b_dev DOES need to be synchronized, because it was mutated on a different stream. let a_host_2 = stream.clone_dtoh(&a_dev)?; let b_host = stream.clone_dtoh(&b_dev)?; println!("Found {b_host:?}"); println!("Expected {:?}", a_host.map(f32::sin)); assert_eq!(&a_host, a_host_2.as_slice()); Ok(()) }