()
| 8 | static PTX: &str = include_str!("../../../resources/add.ptx"); |
| 9 | |
| 10 | fn main() -> Result<(), Box<dyn Error>> { |
| 11 | // generate our random vectors. |
| 12 | let mut wyrand = WyRand::new(); |
| 13 | let mut lhs = vec![2.0f32; NUMBERS_LEN]; |
| 14 | wyrand.fill(&mut lhs); |
| 15 | let mut rhs = vec![0.0f32; NUMBERS_LEN]; |
| 16 | wyrand.fill(&mut rhs); |
| 17 | |
| 18 | // initialize CUDA, this will pick the first available device and will |
| 19 | // make a CUDA context from it. |
| 20 | // We don't need the context for anything but it must be kept alive. |
| 21 | let _ctx = cust::quick_init()?; |
| 22 | |
| 23 | // Make the CUDA module, modules just house the GPU code for the kernels we created. |
| 24 | // they can be made from PTX code, cubins, or fatbins. |
| 25 | let module = Module::from_ptx(PTX, &[])?; |
| 26 | |
| 27 | // make a CUDA stream to issue calls to. You can think of this as an OS thread but for dispatching |
| 28 | // GPU calls. |
| 29 | let stream = Stream::new(StreamFlags::NON_BLOCKING, None)?; |
| 30 | |
| 31 | // allocate the GPU memory needed to house our numbers and copy them over. |
| 32 | let lhs_gpu = lhs.as_slice().as_dbuf()?; |
| 33 | let rhs_gpu = rhs.as_slice().as_dbuf()?; |
| 34 | |
| 35 | // allocate our output buffer. You could also use DeviceBuffer::uninitialized() to avoid the |
| 36 | // cost of the copy, but you need to be careful not to read from the buffer. |
| 37 | let mut out = vec![0.0f32; NUMBERS_LEN]; |
| 38 | let out_buf = out.as_slice().as_dbuf()?; |
| 39 | |
| 40 | // retrieve the add kernel from the module so we can calculate the right launch config. |
| 41 | let func = module.get_function("add")?; |
| 42 | |
| 43 | // use the CUDA occupancy API to find an optimal launch configuration for the grid and block size. |
| 44 | // This will try to maximize how much of the GPU is used by finding the best launch configuration for the |
| 45 | // current CUDA device/architecture. |
| 46 | let (_, block_size) = func.suggested_launch_configuration(0, 0.into())?; |
| 47 | |
| 48 | let grid_size = (NUMBERS_LEN as u32 + block_size - 1) / block_size; |
| 49 | |
| 50 | println!( |
| 51 | "using {} blocks and {} threads per block", |
| 52 | grid_size, block_size |
| 53 | ); |
| 54 | |
| 55 | // Actually launch the GPU kernel. This will queue up the launch on the stream, it will |
| 56 | // not block the thread until the kernel is finished. |
| 57 | unsafe { |
| 58 | launch!( |
| 59 | // slices are passed as two parameters, the pointer and the length. |
| 60 | func<<<grid_size, block_size, 0, stream>>>( |
| 61 | lhs_gpu.as_device_ptr(), |
| 62 | lhs_gpu.len(), |
| 63 | rhs_gpu.as_device_ptr(), |
| 64 | rhs_gpu.len(), |
| 65 | out_buf.as_device_ptr(), |
| 66 | ) |
| 67 | )?; |
nothing calls this directly
no test coverage detected