4783 lines
142 KiB
Markdown
4783 lines
142 KiB
Markdown
cudarc::driver
|
||
Module safe Copy item path
|
||
Source
|
||
|
||
Safe abstractions over crate::driver::result provided by CudaSlice, CudaContext, CudaStream, and more.
|
||
|
||
Re-exports
|
||
pub use crate::driver::result::DriverError;
|
||
Structs
|
||
CudaContext
|
||
Represents a primary cuda context on a certain device. When created with CudaContext::new() it will push a new primary context onto the stack.
|
||
CudaEvent
|
||
A lightweight synchronization primitive used to synchronize between CudaStreams.
|
||
CudaFunction
|
||
Wrapper around sys::CUfunction. Used by CudaStream::launch_builder to execute kernels.
|
||
CudaGraph
|
||
Represents a replay-able Cuda Graph. Create with CudaStream::begin_capture() and CudaStream::end_capture().
|
||
CudaModule
|
||
Wrapper around sys::CUmodule. Create with CudaContext::load_module().
|
||
CudaSlice
|
||
Vec<T> on a cuda device. You can allocate and modify this with CudaStream.
|
||
CudaStream
|
||
A wrapper around sys::CUstream that you can schedule work on.
|
||
CudaView
|
||
&[T] on a cuda device. An immutable sub-view into a CudaSlice created by CudaSlice::as_view()/CudaSlice::slice().
|
||
CudaViewMut
|
||
&mut [T] on a cuda device. A mutable sub-view into a CudaSlice created by CudaSlice::as_view_mut()/CudaSlice::slice_mut().
|
||
ExternalMemory
|
||
An abstraction for imported external memory.
|
||
LaunchArgs
|
||
The kernel launch builder. Instantiate with CudaStream::launch_builder(), and then launch the kernel with LaunchArgs::launch()
|
||
LaunchConfig
|
||
Configuration for result::launch_kernel
|
||
MappedBuffer
|
||
An abstraction for a mapped buffer for some external memory.
|
||
PinnedHostSlice
|
||
Rust side data that the cuda driver knows is pinned. This is different than Pin<Vec<T>> mainly because cuda driver manages this memory and ensures it is page locked.
|
||
Profiler
|
||
Calls profiler_start() in Profiler::new(), and profiler_stop() in Drop.
|
||
UnifiedSlice
|
||
Unified memory allocated with CudaContext::alloc_unified() (via cuMemAllocManaged).
|
||
Enums
|
||
SyncOnDrop
|
||
A synchronization primitive to enable stream & event synchronization. Primarily used with DevicePtr and DevicePtrMut
|
||
Traits
|
||
DevicePtr
|
||
Abstraction over CudaSlice/CudaView
|
||
DevicePtrMut
|
||
Abstraction over CudaSlice/CudaViewMut
|
||
DeviceRepr
|
||
Something that can be copied to device memory and turned into a parameter for result::launch_kernel.
|
||
DeviceSlice
|
||
Base trait for abstracting over CudaSlice/CudaView/CudaViewMut.
|
||
HostSlice
|
||
Abstraction over &[T], &Vec<T> and PinnedHostSlice<T>.
|
||
PushKernelArg
|
||
Something that can be copied to device memory and turned into a parameter for result::launch_kernel.
|
||
ValidAsZeroBits
|
||
Marker trait to indicate that the type is valid when all of its bits are set to 0.
|
||
Functions
|
||
profiler_start
|
||
Enables profile collection by the active profiling tool for the current context. If profiling is already enabled, then profiler_start() has no effect. More info in Cuda docs For RAII version see Profiler::new.
|
||
profiler_stop
|
||
Disables profile collection by the active profiling tool for the current context. If profiling is already disabled, then profiler_stop() has no effect.
|
||
|
||
pub struct CudaContext { /* private fields */ }
|
||
Represents a primary cuda context on a certain device. When created with CudaContext::new() it will push a new primary context onto the stack.
|
||
|
||
This is the entrypoint to using any cuda calls, all objects maintain a pointer to Arc<CudaContext> to ensure proper lifetimes.
|
||
|
||
On thread safety
|
||
This object is thread safe and can be shared/used on multiple threads. All safe apis call CudaContext::bind_to_thread() before doing work in a certain context.
|
||
|
||
Implementations
|
||
Source
|
||
impl CudaContext
|
||
Source
|
||
pub fn new(ordinal: usize) -> Result<Arc<Self>, DriverError>
|
||
Creates a new context on the specified device ordinal.
|
||
|
||
Examples found in repository?
|
||
examples/01-allocate.rs (line 4)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 // unsafe initialization of unset memory
|
||
8 let _: CudaSlice<f32> = unsafe { stream.alloc::<f32>(10) }?;
|
||
9
|
||
10 // this will have memory initialized as 0
|
||
11 let _: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
12
|
||
13 // initialize with slices!
|
||
14 let _: CudaSlice<usize> = stream.memcpy_stod(&[0; 10])?;
|
||
15 let _: CudaSlice<u32> = stream.memcpy_stod(&[1, 2, 3])?;
|
||
16
|
||
17 Ok(())
|
||
18}
|
||
More examples
|
||
Hide additional examples
|
||
examples/05-device-repr.rs (line 33)
|
||
32fn main() -> Result<(), DriverError> {
|
||
33 let ctx = CudaContext::new(0)?;
|
||
34 let stream = ctx.default_stream();
|
||
35
|
||
36 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
37 let module = ctx.load_module(ptx)?;
|
||
38 let f = module.load_function("my_custom_kernel")?;
|
||
39
|
||
40 // try changing some of these values to see a device assert
|
||
41 let thing = MyCoolRustStruct {
|
||
42 a: 1.0,
|
||
43 b: 2.34,
|
||
44 c: 57,
|
||
45 d: 420,
|
||
46 };
|
||
47
|
||
48 let mut builder = stream.launch_builder(&f);
|
||
49 // since MyCoolRustStruct implements DeviceRepr, we can pass it to launch.
|
||
50 builder.arg(&thing);
|
||
51 unsafe { builder.launch(LaunchConfig::for_num_elems(1)) }?;
|
||
52
|
||
53 Ok(())
|
||
54}
|
||
examples/cufile-copy.rs (line 19)
|
||
2fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
3 use std::fs;
|
||
4
|
||
5 use cudarc::{cufile::safe::Cufile, driver::CudaContext};
|
||
6
|
||
7 const N: usize = 100000;
|
||
8 let data: Vec<u8> = (0..N).flat_map(|x| (x as f32).to_le_bytes()).collect();
|
||
9 let data_sz = data.len();
|
||
10 let src_file = "/tmp/cufile_test.bin";
|
||
11 fs::write(src_file, &data)?;
|
||
12
|
||
13 let cufile = Cufile::new()?;
|
||
14 println!("{:?}", cufile.get_properties()?);
|
||
15
|
||
16 let file = fs::File::open(src_file)?;
|
||
17 let handle = cufile.register(file)?;
|
||
18
|
||
19 let ctx = CudaContext::new(0)?;
|
||
20 let stream = ctx.default_stream();
|
||
21 let mut buf = stream.alloc_zeros::<u8>(data_sz)?;
|
||
22
|
||
23 handle.sync_read(0, &mut buf)?;
|
||
24
|
||
25 let verify_dst = stream.memcpy_dtov(&buf)?;
|
||
26 assert_eq!(verify_dst, data);
|
||
27
|
||
28 Ok(())
|
||
29}
|
||
examples/02-copy.rs (line 4)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 let a: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
8 let mut b = stream.alloc_zeros::<f64>(10)?;
|
||
9
|
||
10 // you can do device to device copies of course
|
||
11 stream.memcpy_dtod(&a, &mut b)?;
|
||
12
|
||
13 // but also host to device copys with already allocated buffers
|
||
14 stream.memcpy_htod(&vec![2.0; b.len()], &mut b)?;
|
||
15 // you can use any type of slice
|
||
16 stream.memcpy_htod(&[3.0; 10], &mut b)?;
|
||
17
|
||
18 // you can transfer back using memcpy_dtov
|
||
19 let mut a_host: Vec<f64> = stream.memcpy_dtov(&a)?;
|
||
20 assert_eq!(a_host, [0.0; 10]);
|
||
21
|
||
22 let b_host = stream.memcpy_dtov(&b)?;
|
||
23 assert_eq!(b_host, [3.0; 10]);
|
||
24
|
||
25 // or transfer into a pre allocated slice
|
||
26 stream.memcpy_dtoh(&b, &mut a_host)?;
|
||
27 assert_eq!(a_host, b_host);
|
||
28
|
||
29 Ok(())
|
||
30}
|
||
examples/03-launch-kernel.rs (line 7)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/matmul-kernel.rs (line 28)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
Additional examples can be found in:
|
||
examples/04-streams.rs
|
||
examples/06-threading.rs
|
||
Source
|
||
pub fn device_count() -> Result<i32, DriverError>
|
||
The number of devices available.
|
||
|
||
Source
|
||
pub fn ordinal(&self) -> usize
|
||
Get the ordinal index of the device this is on.
|
||
|
||
Source
|
||
pub fn name(&self) -> Result<String, DriverError>
|
||
Get the name of this device.
|
||
|
||
Source
|
||
pub fn uuid(&self) -> Result<CUuuid, DriverError>
|
||
Get the UUID of this device.
|
||
|
||
Source
|
||
pub fn cu_device(&self) -> CUdevice
|
||
Get the underlying sys::CUdevice of this CudaContext.
|
||
|
||
Safety
|
||
While this function is marked as safe, actually using the returned object is unsafe.
|
||
|
||
You must not free/release the device pointer, as it is still owned by the CudaContext.
|
||
|
||
Source
|
||
pub fn cu_ctx(&self) -> CUcontext
|
||
Get the underlying sys::CUcontext of this CudaContext.
|
||
|
||
Safety
|
||
While this function is marked as safe, actually using the returned object is unsafe.
|
||
|
||
You must not free/release the context pointer, as it is still owned by the CudaContext.
|
||
|
||
Source
|
||
pub fn bind_to_thread(&self) -> Result<(), DriverError>
|
||
Binds this context to the calling thread. Calling this is key for thread safety.
|
||
|
||
Source
|
||
pub fn attribute(&self, attrib: CUdevice_attribute) -> Result<i32, DriverError>
|
||
Get the value of the specified attribute of the device in CudaContext.
|
||
|
||
Source
|
||
pub fn synchronize(&self) -> Result<(), DriverError>
|
||
Synchronize this context. Will only block CPU if you call CudaContext::set_flags() with sys::CUctx_flags::CU_CTX_SCHED_BLOCKING_SYNC.
|
||
|
||
Source
|
||
pub fn set_blocking_synchronize(&self) -> Result<(), DriverError>
|
||
Ensures calls to CudaContext::synchronize() block the calling thread.
|
||
|
||
Sets sys::CUctx_flags::CU_CTX_SCHED_BLOCKING_SYNC
|
||
|
||
Source
|
||
pub fn set_flags(&self, flags: CUctx_flags) -> Result<(), DriverError>
|
||
Set flags for this context
|
||
|
||
Source
|
||
pub fn is_in_multi_stream_mode(&self) -> bool
|
||
Whether multiple streams have been created in this context. If so, the CudaSlice::read and CudaSlice::write events will be activated.
|
||
|
||
This only get’s set to true by CudaContext::new_stream().
|
||
|
||
Source
|
||
pub fn is_event_tracking(&self) -> bool
|
||
Whether event tracking is being managed by this context (via CudaContext::enable_event_tracking(), which is the default behavior), or false if the user is manually managing stream synchronization (via CudaContext::disable_event_tracking()).
|
||
|
||
Source
|
||
pub fn is_managing_stream_synchronization(&self) -> bool
|
||
Whether the context is automatically managing multiple stream synchronization. Both of these must be true:
|
||
|
||
CudaContext::is_in_multi_stream_mode()
|
||
CudaContext::is_event_tracking()
|
||
Source
|
||
pub unsafe fn enable_event_tracking(&self)
|
||
When turned on, all CudaSlice created after calling this function will record usages using CudaEvent to ensure proper synchronization between streams.
|
||
|
||
Safety
|
||
If CudaContext::disable_event_tracking() was called previously, then any CudaSlice created after that and before this current call won’t have CudaEvent tracking their uses. Those CudaSlice will not manage their synchronization, even after this call.
|
||
|
||
Source
|
||
pub unsafe fn disable_event_tracking(&self)
|
||
When turned on, all CudaSlice created after calling this function will not track uses via CudaEvents.
|
||
|
||
Safety
|
||
It is up to the user to ensure proper synchronization between multiple streams:
|
||
|
||
Ensure that no CudaSlice is freed before a use on another stream is finished.
|
||
Ensure that a CudaSlice is not used on another stream before allocation on the allocating stream finishes.
|
||
Ensure that a CudaSlice is not written two concurrently by multiple streams.
|
||
Source
|
||
pub fn check_err(&self) -> Result<(), DriverError>
|
||
Checks to see if there have been any calls that stored an Err in a function that couldn’t return a result (e.g. Drop calls).
|
||
|
||
If there are any errors stored, this method will return the Err value, and then clear the stored error state.
|
||
|
||
Source
|
||
pub fn record_err<T>(&self, result: Result<T, DriverError>)
|
||
Records a result for later inspection when a Result can be returned.
|
||
|
||
Source
|
||
impl CudaContext
|
||
Source
|
||
pub fn new_event(
|
||
self: &Arc<Self>,
|
||
flags: Option<CUevent_flags>,
|
||
) -> Result<CudaEvent, DriverError>
|
||
Creates a new CudaEvent with no work recorded. If flags is None, the event is created with sys::CUevent_flags::CU_EVENT_DISABLE_TIMING.
|
||
|
||
Source
|
||
impl CudaContext
|
||
Source
|
||
pub fn default_stream(self: &Arc<Self>) -> Arc<CudaStream>
|
||
Get’s the default stream for this context (the null ptr stream). Note that context’s on the same device can all submit to the same default stream from separate context objects.
|
||
|
||
Examples found in repository?
|
||
examples/01-allocate.rs (line 5)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 // unsafe initialization of unset memory
|
||
8 let _: CudaSlice<f32> = unsafe { stream.alloc::<f32>(10) }?;
|
||
9
|
||
10 // this will have memory initialized as 0
|
||
11 let _: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
12
|
||
13 // initialize with slices!
|
||
14 let _: CudaSlice<usize> = stream.memcpy_stod(&[0; 10])?;
|
||
15 let _: CudaSlice<u32> = stream.memcpy_stod(&[1, 2, 3])?;
|
||
16
|
||
17 Ok(())
|
||
18}
|
||
More examples
|
||
Hide additional examples
|
||
examples/05-device-repr.rs (line 34)
|
||
32fn main() -> Result<(), DriverError> {
|
||
33 let ctx = CudaContext::new(0)?;
|
||
34 let stream = ctx.default_stream();
|
||
35
|
||
36 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
37 let module = ctx.load_module(ptx)?;
|
||
38 let f = module.load_function("my_custom_kernel")?;
|
||
39
|
||
40 // try changing some of these values to see a device assert
|
||
41 let thing = MyCoolRustStruct {
|
||
42 a: 1.0,
|
||
43 b: 2.34,
|
||
44 c: 57,
|
||
45 d: 420,
|
||
46 };
|
||
47
|
||
48 let mut builder = stream.launch_builder(&f);
|
||
49 // since MyCoolRustStruct implements DeviceRepr, we can pass it to launch.
|
||
50 builder.arg(&thing);
|
||
51 unsafe { builder.launch(LaunchConfig::for_num_elems(1)) }?;
|
||
52
|
||
53 Ok(())
|
||
54}
|
||
examples/cufile-copy.rs (line 20)
|
||
2fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
3 use std::fs;
|
||
4
|
||
5 use cudarc::{cufile::safe::Cufile, driver::CudaContext};
|
||
6
|
||
7 const N: usize = 100000;
|
||
8 let data: Vec<u8> = (0..N).flat_map(|x| (x as f32).to_le_bytes()).collect();
|
||
9 let data_sz = data.len();
|
||
10 let src_file = "/tmp/cufile_test.bin";
|
||
11 fs::write(src_file, &data)?;
|
||
12
|
||
13 let cufile = Cufile::new()?;
|
||
14 println!("{:?}", cufile.get_properties()?);
|
||
15
|
||
16 let file = fs::File::open(src_file)?;
|
||
17 let handle = cufile.register(file)?;
|
||
18
|
||
19 let ctx = CudaContext::new(0)?;
|
||
20 let stream = ctx.default_stream();
|
||
21 let mut buf = stream.alloc_zeros::<u8>(data_sz)?;
|
||
22
|
||
23 handle.sync_read(0, &mut buf)?;
|
||
24
|
||
25 let verify_dst = stream.memcpy_dtov(&buf)?;
|
||
26 assert_eq!(verify_dst, data);
|
||
27
|
||
28 Ok(())
|
||
29}
|
||
examples/02-copy.rs (line 5)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 let a: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
8 let mut b = stream.alloc_zeros::<f64>(10)?;
|
||
9
|
||
10 // you can do device to device copies of course
|
||
11 stream.memcpy_dtod(&a, &mut b)?;
|
||
12
|
||
13 // but also host to device copys with already allocated buffers
|
||
14 stream.memcpy_htod(&vec![2.0; b.len()], &mut b)?;
|
||
15 // you can use any type of slice
|
||
16 stream.memcpy_htod(&[3.0; 10], &mut b)?;
|
||
17
|
||
18 // you can transfer back using memcpy_dtov
|
||
19 let mut a_host: Vec<f64> = stream.memcpy_dtov(&a)?;
|
||
20 assert_eq!(a_host, [0.0; 10]);
|
||
21
|
||
22 let b_host = stream.memcpy_dtov(&b)?;
|
||
23 assert_eq!(b_host, [3.0; 10]);
|
||
24
|
||
25 // or transfer into a pre allocated slice
|
||
26 stream.memcpy_dtoh(&b, &mut a_host)?;
|
||
27 assert_eq!(a_host, b_host);
|
||
28
|
||
29 Ok(())
|
||
30}
|
||
examples/03-launch-kernel.rs (line 8)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/matmul-kernel.rs (line 29)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
Additional examples can be found in:
|
||
examples/04-streams.rs
|
||
examples/06-threading.rs
|
||
Source
|
||
pub fn new_stream(self: &Arc<Self>) -> Result<Arc<CudaStream>, DriverError>
|
||
Create a new sys::CUstream_flags::CU_STREAM_NON_BLOCKING stream.
|
||
|
||
This will swap the calling context to multi stream mode CudaContext::is_in_multi_stream_mode(). If the context is not already in multiple stream mode, then this function will also call CudaContext::synchronize().
|
||
|
||
Source
|
||
impl CudaContext
|
||
Source
|
||
pub unsafe fn alloc_pinned<T: DeviceRepr>(
|
||
self: &Arc<Self>,
|
||
len: usize,
|
||
) -> Result<PinnedHostSlice<T>, DriverError>
|
||
Allocates page locked host memory with sys::CU_MEMHOSTALLOC_WRITECOMBINED flags.
|
||
|
||
See cuda docs
|
||
|
||
Safety
|
||
This is unsafe because the memory is unset after this call.
|
||
Source
|
||
impl CudaContext
|
||
Source
|
||
pub fn load_module(
|
||
self: &Arc<Self>,
|
||
ptx: Ptx,
|
||
) -> Result<Arc<CudaModule>, DriverError>
|
||
Dynamically load a compiled ptx into this context.
|
||
|
||
ptx contains the compiled ptx
|
||
Examples found in repository?
|
||
examples/05-device-repr.rs (line 37)
|
||
32fn main() -> Result<(), DriverError> {
|
||
33 let ctx = CudaContext::new(0)?;
|
||
34 let stream = ctx.default_stream();
|
||
35
|
||
36 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
37 let module = ctx.load_module(ptx)?;
|
||
38 let f = module.load_function("my_custom_kernel")?;
|
||
39
|
||
40 // try changing some of these values to see a device assert
|
||
41 let thing = MyCoolRustStruct {
|
||
42 a: 1.0,
|
||
43 b: 2.34,
|
||
44 c: 57,
|
||
45 d: 420,
|
||
46 };
|
||
47
|
||
48 let mut builder = stream.launch_builder(&f);
|
||
49 // since MyCoolRustStruct implements DeviceRepr, we can pass it to launch.
|
||
50 builder.arg(&thing);
|
||
51 unsafe { builder.launch(LaunchConfig::for_num_elems(1)) }?;
|
||
52
|
||
53 Ok(())
|
||
54}
|
||
More examples
|
||
Hide additional examples
|
||
examples/03-launch-kernel.rs (line 11)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/matmul-kernel.rs (line 32)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
examples/04-streams.rs (line 10)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
examples/06-threading.rs (line 18)
|
||
12fn main() -> Result<(), DriverError> {
|
||
13 {
|
||
14 // Option 1: sharing ctx & module between threads
|
||
15 thread::scope(|s| {
|
||
16 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
17 let ctx = CudaContext::new(0)?;
|
||
18 let module = ctx.load_module(ptx)?;
|
||
19 for i in 0..10i32 {
|
||
20 let thread_ctx = ctx.clone();
|
||
21 let thread_module = module.clone();
|
||
22 s.spawn(move || {
|
||
23 let stream = thread_ctx.default_stream();
|
||
24 let f = thread_module.load_function("hello_world")?;
|
||
25 unsafe {
|
||
26 stream
|
||
27 .launch_builder(&f)
|
||
28 .arg(&i)
|
||
29 .launch(LaunchConfig::for_num_elems(1))
|
||
30 }
|
||
31 });
|
||
32 }
|
||
33 Ok(())
|
||
34 })?;
|
||
35 }
|
||
36
|
||
37 {
|
||
38 // Option 2: initializing different context in each
|
||
39 // Note that this will still schedule to the same stream since we are using the
|
||
40 // default stream here on the same device.
|
||
41 thread::scope(move |s| {
|
||
42 for i in 0..10i32 {
|
||
43 s.spawn(move || {
|
||
44 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
45 let ctx = CudaContext::new(0)?;
|
||
46 let module = ctx.load_module(ptx)?;
|
||
47 let stream = ctx.default_stream();
|
||
48 let f = module.load_function("hello_world")?;
|
||
49 unsafe {
|
||
50 stream
|
||
51 .launch_builder(&f)
|
||
52 .arg(&i)
|
||
53 .launch(LaunchConfig::for_num_elems(1))
|
||
54 }
|
||
55 });
|
||
56 }
|
||
57 Ok(())
|
||
58 })?;
|
||
59 }
|
||
60
|
||
61 Ok(())
|
||
62}
|
||
Source
|
||
impl CudaContext
|
||
Source
|
||
pub unsafe fn import_external_memory(
|
||
self: &Arc<Self>,
|
||
file: File,
|
||
size: u64,
|
||
) -> Result<ExternalMemory, DriverError>
|
||
Import external memory from a File.
|
||
|
||
Safety
|
||
size must be the size of the external memory in bytes.
|
||
|
||
Source
|
||
impl CudaContext
|
||
Source
|
||
pub unsafe fn alloc_unified<T: DeviceRepr>(
|
||
self: &Arc<Self>,
|
||
len: usize,
|
||
attach_global: bool,
|
||
) -> Result<UnifiedSlice<T>, DriverError>
|
||
Allocates managed memory using cuMemAllocManaged.
|
||
|
||
If attach_global is true, then allocates the memory with flag sys::CUmemAttach_flags::CU_MEM_ATTACH_GLOBAL, otherwise uses flag sys::CUmemAttach_flags::CU_MEM_ATTACH_HOST.
|
||
|
||
Note that only these two flags are valid during allocation, you can change the attach mode later via UnifiedSlice::attach()
|
||
|
||
If the device does not support managed memory (sys::CUdevice_attribute::CU_DEVICE_ATTRIBUTE_MANAGED_MEMORY is 0), then this method will return Err with sys::cudaError_enum::CUDA_ERROR_NOT_PERMITTED.
|
||
|
||
Safety
|
||
This is unsafe because this method has no restrictions that T is valid for any bit pattern.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl Debug for CudaContext
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl Drop for CudaContext
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Source
|
||
impl PartialEq for CudaContext
|
||
Source
|
||
fn eq(&self, other: &Self) -> bool
|
||
Tests for self and other values to be equal, and is used by ==.
|
||
1.0.0 · Source
|
||
fn ne(&self, other: &Rhs) -> bool
|
||
Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
|
||
Source
|
||
impl Eq for CudaContext
|
||
Source
|
||
impl Send for CudaContext
|
||
Source
|
||
impl Sync for CudaContext
|
||
Auto Trait Implementations
|
||
impl !Freeze for CudaContext
|
||
impl RefUnwindSafe for CudaContext
|
||
impl Unpin for CudaContext
|
||
impl UnwindSafe for CudaContext
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source§
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
----------------------------
|
||
|
||
pub struct CudaEvent { /* private fields */ }
|
||
A lightweight synchronization primitive used to synchronize between CudaStreams.
|
||
|
||
Create using CudaContext::new_event().
|
||
Record a point of time in a stream using CudaEvent::record().
|
||
Either call CudaEvent::synchronize() or CudaStream::wait() to use.
|
||
Note that calls to CudaEvent::record() will not change any previous calls to CudaStream::wait().
|
||
|
||
Thread safety
|
||
This object is thread safe
|
||
|
||
Implementations
|
||
Source
|
||
impl CudaEvent
|
||
Source
|
||
pub fn cu_event(&self) -> CUevent
|
||
The underlying cu_event object.
|
||
|
||
Safety
|
||
Do not destroy this value
|
||
|
||
Source
|
||
pub fn context(&self) -> &Arc<CudaContext>
|
||
The context this was created in.
|
||
|
||
Source
|
||
pub fn record(&self, stream: &CudaStream) -> Result<(), DriverError>
|
||
Records the current amount of work in CudaStream into this event.
|
||
|
||
This does not affect any previous calls to CudaStream::wait()
|
||
|
||
If stream belongs to a different CudaContext, this will fail with sys::cudaError_enum::CUDA_ERROR_INVALID_CONTEXT.
|
||
|
||
See cuda docs
|
||
|
||
Source
|
||
pub fn synchronize(&self) -> Result<(), DriverError>
|
||
Will only block CPU thraed if sys::CUevent_flags::CU_EVENT_BLOCKING_SYNC was used to create this event.
|
||
|
||
Source
|
||
pub fn elapsed_ms(&self, end: &Self) -> Result<f32, DriverError>
|
||
The time between two events. self is the start event, and end is the end event. This is effectively end - self.
|
||
|
||
Source
|
||
pub fn is_complete(&self) -> bool
|
||
Returns true if all recorded work has been completed, false otherwise.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl Debug for CudaEvent
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl Drop for CudaEvent
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Source
|
||
impl Send for CudaEvent
|
||
Source
|
||
impl Sync for CudaEvent
|
||
Auto Trait Implementations
|
||
impl Freeze for CudaEvent
|
||
impl RefUnwindSafe for CudaEvent
|
||
impl Unpin for CudaEvent
|
||
impl UnwindSafe for CudaEvent
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
----------------------------
|
||
|
||
pub struct CudaFunction { /* private fields */ }
|
||
Wrapper around sys::CUfunction. Used by CudaStream::launch_builder to execute kernels.
|
||
|
||
Implementations
|
||
Source
|
||
impl CudaFunction
|
||
Source
|
||
pub fn occupancy_available_dynamic_smem_per_block(
|
||
&self,
|
||
num_blocks: u32,
|
||
block_size: u32,
|
||
) -> Result<usize, DriverError>
|
||
Source
|
||
pub fn occupancy_max_active_blocks_per_multiprocessor(
|
||
&self,
|
||
block_size: u32,
|
||
dynamic_smem_size: usize,
|
||
flags: Option<CUoccupancy_flags_enum>,
|
||
) -> Result<u32, DriverError>
|
||
Source
|
||
pub fn occupancy_max_active_clusters(
|
||
&self,
|
||
config: LaunchConfig,
|
||
stream: &CudaStream,
|
||
) -> Result<u32, DriverError>
|
||
Source
|
||
pub fn occupancy_max_potential_block_size(
|
||
&self,
|
||
block_size_to_dynamic_smem_size: extern "C" fn(block_size: c_int) -> usize,
|
||
dynamic_smem_size: usize,
|
||
block_size_limit: u32,
|
||
flags: Option<CUoccupancy_flags_enum>,
|
||
) -> Result<(u32, u32), DriverError>
|
||
Source
|
||
pub fn occupancy_max_potential_cluster_size(
|
||
&self,
|
||
config: LaunchConfig,
|
||
stream: &CudaStream,
|
||
) -> Result<u32, DriverError>
|
||
Source
|
||
pub fn set_attribute(
|
||
&self,
|
||
attribute: CUfunction_attribute_enum,
|
||
value: i32,
|
||
) -> Result<(), DriverError>
|
||
Set the value of a specific attribute of this CudaFunction.
|
||
|
||
Source
|
||
pub fn set_function_cache_config(
|
||
&self,
|
||
attribute: CUfunc_cache_enum,
|
||
) -> Result<(), DriverError>
|
||
Set the cache config of this CudaFunction.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl Clone for CudaFunction
|
||
Source
|
||
fn clone(&self) -> CudaFunction
|
||
Returns a duplicate of the value. Read more
|
||
1.0.0 · Source
|
||
fn clone_from(&mut self, source: &Self)
|
||
Performs copy-assignment from source. Read more
|
||
Source
|
||
impl Debug for CudaFunction
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl Send for CudaFunction
|
||
Source
|
||
impl Sync for CudaFunction
|
||
Auto Trait Implementations
|
||
impl Freeze for CudaFunction
|
||
impl RefUnwindSafe for CudaFunction
|
||
impl Unpin for CudaFunction
|
||
impl UnwindSafe for CudaFunction
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> CloneToUninit for T
|
||
where
|
||
T: Clone,
|
||
Source
|
||
unsafe fn clone_to_uninit(&self, dest: *mut u8)
|
||
🔬This is a nightly-only experimental API. (clone_to_uninit)
|
||
Performs copy-assignment from self to dest. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T> ToOwned for T
|
||
where
|
||
T: Clone,
|
||
Source
|
||
type Owned = T
|
||
The resulting type after obtaining ownership.
|
||
Source
|
||
fn to_owned(&self) -> T
|
||
Creates owned data from borrowed data, usually by cloning. Read more
|
||
Source
|
||
fn clone_into(&self, target: &mut T)
|
||
Uses borrowed data to replace owned data, usually by cloning. Read more
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
---------------------------------------------------
|
||
pub struct CudaGraph { /* private fields */ }
|
||
Represents a replay-able Cuda Graph. Create with CudaStream::begin_capture() and CudaStream::end_capture().
|
||
|
||
Once created you can replay with CudaGraph::launch().
|
||
|
||
On Thread safety
|
||
This object is NOT thread safe.
|
||
|
||
From official docs:
|
||
|
||
Graph objects (cudaGraph_t, CUgraph) are not internally synchronized and must not be accessed concurrently from multiple threads. API calls accessing the same graph object must be serialized externally.
|
||
|
||
Note that this includes APIs which may appear to be read-only, such as cudaGraphClone() (cuGraphClone()) and cudaGraphInstantiate() (cuGraphInstantiate()). No API or pair of APIs is guaranteed to be safe to call on the same graph object from two different threads without serialization.
|
||
|
||
https://docs.nvidia.com/cuda/cuda-driver-api/graphs-thread-safety.html#graphs-thread-safety
|
||
|
||
Implementations
|
||
Source
|
||
impl CudaGraph
|
||
Source
|
||
pub fn launch(&self) -> Result<(), DriverError>
|
||
See cuda docs
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl Drop for CudaGraph
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Auto Trait Implementations
|
||
impl Freeze for CudaGraph
|
||
impl RefUnwindSafe for CudaGraph
|
||
impl !Send for CudaGraph
|
||
impl !Sync for CudaGraph
|
||
impl Unpin for CudaGraph
|
||
impl UnwindSafe for CudaGraph
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
---------------------------------------------
|
||
pub struct CudaModule { /* private fields */ }
|
||
Wrapper around sys::CUmodule. Create with CudaContext::load_module().
|
||
|
||
Call CudaModule::load_function to load a CudaFunction.
|
||
|
||
Implementations
|
||
Source
|
||
impl CudaModule
|
||
Source
|
||
pub fn load_function(
|
||
self: &Arc<Self>,
|
||
fn_name: &str,
|
||
) -> Result<CudaFunction, DriverError>
|
||
Loads a function from the loaded module with the given name.
|
||
|
||
Examples found in repository?
|
||
examples/05-device-repr.rs (line 38)
|
||
32fn main() -> Result<(), DriverError> {
|
||
33 let ctx = CudaContext::new(0)?;
|
||
34 let stream = ctx.default_stream();
|
||
35
|
||
36 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
37 let module = ctx.load_module(ptx)?;
|
||
38 let f = module.load_function("my_custom_kernel")?;
|
||
39
|
||
40 // try changing some of these values to see a device assert
|
||
41 let thing = MyCoolRustStruct {
|
||
42 a: 1.0,
|
||
43 b: 2.34,
|
||
44 c: 57,
|
||
45 d: 420,
|
||
46 };
|
||
47
|
||
48 let mut builder = stream.launch_builder(&f);
|
||
49 // since MyCoolRustStruct implements DeviceRepr, we can pass it to launch.
|
||
50 builder.arg(&thing);
|
||
51 unsafe { builder.launch(LaunchConfig::for_num_elems(1)) }?;
|
||
52
|
||
53 Ok(())
|
||
54}
|
||
More examples
|
||
Hide additional examples
|
||
examples/03-launch-kernel.rs (line 14)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/matmul-kernel.rs (line 33)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
examples/04-streams.rs (line 11)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
examples/06-threading.rs (line 24)
|
||
12fn main() -> Result<(), DriverError> {
|
||
13 {
|
||
14 // Option 1: sharing ctx & module between threads
|
||
15 thread::scope(|s| {
|
||
16 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
17 let ctx = CudaContext::new(0)?;
|
||
18 let module = ctx.load_module(ptx)?;
|
||
19 for i in 0..10i32 {
|
||
20 let thread_ctx = ctx.clone();
|
||
21 let thread_module = module.clone();
|
||
22 s.spawn(move || {
|
||
23 let stream = thread_ctx.default_stream();
|
||
24 let f = thread_module.load_function("hello_world")?;
|
||
25 unsafe {
|
||
26 stream
|
||
27 .launch_builder(&f)
|
||
28 .arg(&i)
|
||
29 .launch(LaunchConfig::for_num_elems(1))
|
||
30 }
|
||
31 });
|
||
32 }
|
||
33 Ok(())
|
||
34 })?;
|
||
35 }
|
||
36
|
||
37 {
|
||
38 // Option 2: initializing different context in each
|
||
39 // Note that this will still schedule to the same stream since we are using the
|
||
40 // default stream here on the same device.
|
||
41 thread::scope(move |s| {
|
||
42 for i in 0..10i32 {
|
||
43 s.spawn(move || {
|
||
44 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
45 let ctx = CudaContext::new(0)?;
|
||
46 let module = ctx.load_module(ptx)?;
|
||
47 let stream = ctx.default_stream();
|
||
48 let f = module.load_function("hello_world")?;
|
||
49 unsafe {
|
||
50 stream
|
||
51 .launch_builder(&f)
|
||
52 .arg(&i)
|
||
53 .launch(LaunchConfig::for_num_elems(1))
|
||
54 }
|
||
55 });
|
||
56 }
|
||
57 Ok(())
|
||
58 })?;
|
||
59 }
|
||
60
|
||
61 Ok(())
|
||
62}
|
||
Trait Implementations
|
||
Source
|
||
impl Debug for CudaModule
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl Drop for CudaModule
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Source
|
||
impl Send for CudaModule
|
||
Source
|
||
impl Sync for CudaModule
|
||
Auto Trait Implementations
|
||
impl Freeze for CudaModule
|
||
impl RefUnwindSafe for CudaModule
|
||
impl Unpin for CudaModule
|
||
impl UnwindSafe for CudaModule
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
-------------------------------------------------------
|
||
pub struct CudaModule { /* private fields */ }
|
||
Wrapper around sys::CUmodule. Create with CudaContext::load_module().
|
||
|
||
Call CudaModule::load_function to load a CudaFunction.
|
||
|
||
Implementations
|
||
Source
|
||
impl CudaModule
|
||
Source
|
||
pub fn load_function(
|
||
self: &Arc<Self>,
|
||
fn_name: &str,
|
||
) -> Result<CudaFunction, DriverError>
|
||
Loads a function from the loaded module with the given name.
|
||
|
||
Examples found in repository?
|
||
examples/05-device-repr.rs (line 38)
|
||
32fn main() -> Result<(), DriverError> {
|
||
33 let ctx = CudaContext::new(0)?;
|
||
34 let stream = ctx.default_stream();
|
||
35
|
||
36 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
37 let module = ctx.load_module(ptx)?;
|
||
38 let f = module.load_function("my_custom_kernel")?;
|
||
39
|
||
40 // try changing some of these values to see a device assert
|
||
41 let thing = MyCoolRustStruct {
|
||
42 a: 1.0,
|
||
43 b: 2.34,
|
||
44 c: 57,
|
||
45 d: 420,
|
||
46 };
|
||
47
|
||
48 let mut builder = stream.launch_builder(&f);
|
||
49 // since MyCoolRustStruct implements DeviceRepr, we can pass it to launch.
|
||
50 builder.arg(&thing);
|
||
51 unsafe { builder.launch(LaunchConfig::for_num_elems(1)) }?;
|
||
52
|
||
53 Ok(())
|
||
54}
|
||
More examples
|
||
Hide additional examples
|
||
examples/03-launch-kernel.rs (line 14)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/matmul-kernel.rs (line 33)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
examples/04-streams.rs (line 11)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
examples/06-threading.rs (line 24)
|
||
12fn main() -> Result<(), DriverError> {
|
||
13 {
|
||
14 // Option 1: sharing ctx & module between threads
|
||
15 thread::scope(|s| {
|
||
16 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
17 let ctx = CudaContext::new(0)?;
|
||
18 let module = ctx.load_module(ptx)?;
|
||
19 for i in 0..10i32 {
|
||
20 let thread_ctx = ctx.clone();
|
||
21 let thread_module = module.clone();
|
||
22 s.spawn(move || {
|
||
23 let stream = thread_ctx.default_stream();
|
||
24 let f = thread_module.load_function("hello_world")?;
|
||
25 unsafe {
|
||
26 stream
|
||
27 .launch_builder(&f)
|
||
28 .arg(&i)
|
||
29 .launch(LaunchConfig::for_num_elems(1))
|
||
30 }
|
||
31 });
|
||
32 }
|
||
33 Ok(())
|
||
34 })?;
|
||
35 }
|
||
36
|
||
37 {
|
||
38 // Option 2: initializing different context in each
|
||
39 // Note that this will still schedule to the same stream since we are using the
|
||
40 // default stream here on the same device.
|
||
41 thread::scope(move |s| {
|
||
42 for i in 0..10i32 {
|
||
43 s.spawn(move || {
|
||
44 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
45 let ctx = CudaContext::new(0)?;
|
||
46 let module = ctx.load_module(ptx)?;
|
||
47 let stream = ctx.default_stream();
|
||
48 let f = module.load_function("hello_world")?;
|
||
49 unsafe {
|
||
50 stream
|
||
51 .launch_builder(&f)
|
||
52 .arg(&i)
|
||
53 .launch(LaunchConfig::for_num_elems(1))
|
||
54 }
|
||
55 });
|
||
56 }
|
||
57 Ok(())
|
||
58 })?;
|
||
59 }
|
||
60
|
||
61 Ok(())
|
||
62}
|
||
Trait Implementations
|
||
Source
|
||
impl Debug for CudaModule
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl Drop for CudaModule
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Source
|
||
impl Send for CudaModule
|
||
Source
|
||
impl Sync for CudaModule
|
||
Auto Trait Implementations
|
||
impl Freeze for CudaModule
|
||
impl RefUnwindSafe for CudaModule
|
||
impl Unpin for CudaModule
|
||
impl UnwindSafe for CudaModule
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
---------------------------------------------
|
||
pub struct CudaSlice<T> { /* private fields */ }
|
||
Vec<T> on a cuda device. You can allocate and modify this with CudaStream.
|
||
|
||
This object is thread safe.
|
||
|
||
Implementations
|
||
Source
|
||
impl<T> CudaSlice<T>
|
||
Source
|
||
pub fn len(&self) -> usize
|
||
The number of elements of T in this object.
|
||
|
||
Examples found in repository?
|
||
examples/02-copy.rs (line 14)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 let a: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
8 let mut b = stream.alloc_zeros::<f64>(10)?;
|
||
9
|
||
10 // you can do device to device copies of course
|
||
11 stream.memcpy_dtod(&a, &mut b)?;
|
||
12
|
||
13 // but also host to device copys with already allocated buffers
|
||
14 stream.memcpy_htod(&vec![2.0; b.len()], &mut b)?;
|
||
15 // you can use any type of slice
|
||
16 stream.memcpy_htod(&[3.0; 10], &mut b)?;
|
||
17
|
||
18 // you can transfer back using memcpy_dtov
|
||
19 let mut a_host: Vec<f64> = stream.memcpy_dtov(&a)?;
|
||
20 assert_eq!(a_host, [0.0; 10]);
|
||
21
|
||
22 let b_host = stream.memcpy_dtov(&b)?;
|
||
23 assert_eq!(b_host, [3.0; 10]);
|
||
24
|
||
25 // or transfer into a pre allocated slice
|
||
26 stream.memcpy_dtoh(&b, &mut a_host)?;
|
||
27 assert_eq!(a_host, b_host);
|
||
28
|
||
29 Ok(())
|
||
30}
|
||
Source
|
||
pub fn num_bytes(&self) -> usize
|
||
The number of bytes in this object.
|
||
|
||
Source
|
||
pub fn is_empty(&self) -> bool
|
||
True if there are no elements in the object.
|
||
|
||
Source
|
||
pub fn ordinal(&self) -> usize
|
||
The device ordinal this belongs to
|
||
|
||
Source
|
||
pub fn context(&self) -> &Arc<CudaContext>
|
||
The context this belongs to
|
||
|
||
Source
|
||
pub fn stream(&self) -> &Arc<CudaStream>
|
||
The stream this object was allocated on and later will be dropped on.
|
||
|
||
Source
|
||
impl<T: DeviceRepr> CudaSlice<T>
|
||
Source
|
||
pub fn try_clone(&self) -> Result<Self, DriverError>
|
||
Allocates copy of self and schedules a device to device copy of memory.
|
||
|
||
Source
|
||
impl<T> CudaSlice<T>
|
||
Source
|
||
pub fn as_view(&self) -> CudaView<'_, T>
|
||
Source
|
||
impl<T> CudaSlice<T>
|
||
Source
|
||
pub fn as_view_mut(&mut self) -> CudaViewMut<'_, T>
|
||
Source
|
||
impl<T> CudaSlice<T>
|
||
Source
|
||
pub fn slice(&self, bounds: impl RangeBounds<usize>) -> CudaView<'_, T>
|
||
Creates a CudaView at the specified offset from the start of self.
|
||
|
||
Panics if range.start >= self.len.
|
||
|
||
Example
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
let mut view = slice.slice(0..50);
|
||
do_something(&view);
|
||
Like a normal slice, borrow checking prevents the underlying CudaSlice from being dropped.
|
||
|
||
ⓘ
|
||
let view = {
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
// cannot return view, since it borrows from slice
|
||
slice.slice(0..50)
|
||
};
|
||
do_something(&view);
|
||
Source
|
||
pub fn try_slice(
|
||
&self,
|
||
bounds: impl RangeBounds<usize>,
|
||
) -> Option<CudaView<'_, T>>
|
||
Fallible version of CudaSlice::slice().
|
||
|
||
Source
|
||
pub fn slice_mut(
|
||
&mut self,
|
||
bounds: impl RangeBounds<usize>,
|
||
) -> CudaViewMut<'_, T>
|
||
Creates a CudaViewMut at the specified offset from the start of self.
|
||
|
||
Panics if range and 0...self.len() are not overlapping.
|
||
|
||
Example
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
let mut view = slice.slice_mut(0..50);
|
||
do_something(&mut view);
|
||
Like a normal mutable slice, borrow checking prevents the underlying CudaSlice from being dropped.
|
||
|
||
ⓘ
|
||
let mut view = {
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
// cannot return view, since it borrows from slice
|
||
slice.slice_mut(0..50)
|
||
};
|
||
do_something(&mut view);
|
||
Like with normal mutable slices, one cannot mutably slice twice into the same CudaSlice:
|
||
|
||
ⓘ
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
let mut view1 = slice.slice_mut(0..50);
|
||
// cannot borrow twice from slice
|
||
let mut view2 = slice.slice_mut(50..100);
|
||
do_something(view1, view2);
|
||
If you need non-overlapping mutable views into a CudaSlice, you can use CudaSlice::split_at_mut().
|
||
|
||
Source
|
||
pub fn try_slice_mut(
|
||
&mut self,
|
||
bounds: impl RangeBounds<usize>,
|
||
) -> Option<CudaViewMut<'_, T>>
|
||
Fallible version of CudaSlice::slice_mut
|
||
|
||
Source
|
||
pub unsafe fn transmute<S>(&self, len: usize) -> Option<CudaView<'_, S>>
|
||
Reinterprets the slice of memory into a different type. len is the number of elements of the new type S that are expected. If not enough bytes are allocated in self for the view, then this returns None.
|
||
|
||
Safety
|
||
This is unsafe because not the memory for the view may not be a valid interpretation for the type S.
|
||
|
||
Source
|
||
pub unsafe fn transmute_mut<S>(
|
||
&mut self,
|
||
len: usize,
|
||
) -> Option<CudaViewMut<'_, S>>
|
||
Reinterprets the slice of memory into a different type. len is the number of elements of the new type S that are expected. If not enough bytes are allocated in self for the view, then this returns None.
|
||
|
||
Safety
|
||
This is unsafe because not the memory for the view may not be a valid interpretation for the type S.
|
||
|
||
Source
|
||
pub fn split_at(&self, mid: usize) -> (CudaView<'_, T>, CudaView<'_, T>)
|
||
Source
|
||
pub fn try_split_at(
|
||
&self,
|
||
mid: usize,
|
||
) -> Option<(CudaView<'_, T>, CudaView<'_, T>)>
|
||
Fallible version of CudaSlice::split_at. Returns None if mid > self.len.
|
||
|
||
Source
|
||
pub fn split_at_mut(
|
||
&mut self,
|
||
mid: usize,
|
||
) -> (CudaViewMut<'_, T>, CudaViewMut<'_, T>)
|
||
Splits the CudaSlice into two at the given index, returning two CudaViewMut for the two halves.
|
||
|
||
Panics if mid > self.len.
|
||
|
||
This method can be used to create non-overlapping mutable views into a CudaSlice.
|
||
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
// split the slice into two non-overlapping, mutable views
|
||
let (mut view1, mut view2) = slice.split_at_mut(50);
|
||
do_something(view1, view2);
|
||
Source
|
||
pub fn try_split_at_mut(
|
||
&mut self,
|
||
mid: usize,
|
||
) -> Option<(CudaViewMut<'_, T>, CudaViewMut<'_, T>)>
|
||
Fallible version of CudaSlice::split_at_mut.
|
||
|
||
Returns None if mid > self.len.
|
||
|
||
Source
|
||
impl<T> CudaSlice<T>
|
||
Source
|
||
pub fn leak(self) -> CUdeviceptr
|
||
Takes ownership of the underlying sys::CUdeviceptr. It is up to the owner to free this value.
|
||
|
||
Drops the underlying host_buf if there is one.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl<T: DeviceRepr> Clone for CudaSlice<T>
|
||
Source
|
||
fn clone(&self) -> Self
|
||
Returns a duplicate of the value. Read more
|
||
1.0.0 · Source
|
||
fn clone_from(&mut self, source: &Self)
|
||
Performs copy-assignment from source. Read more
|
||
Source
|
||
impl<T: Debug> Debug for CudaSlice<T>
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl<T> DevicePtr<T> for CudaSlice<T>
|
||
Source
|
||
fn device_ptr<'a>(
|
||
&'a self,
|
||
stream: &'a CudaStream,
|
||
) -> (CUdeviceptr, SyncOnDrop<'a>)
|
||
Retrieve the device pointer with the intent to read the device memory associated with it. Read more
|
||
Source
|
||
impl<T> DevicePtrMut<T> for CudaSlice<T>
|
||
Source
|
||
fn device_ptr_mut<'a>(
|
||
&'a mut self,
|
||
stream: &'a CudaStream,
|
||
) -> (CUdeviceptr, SyncOnDrop<'a>)
|
||
Retrieve the device pointer with the intent to modify the device memory associated with it. Read more
|
||
Source
|
||
impl<T> DeviceSlice<T> for CudaSlice<T>
|
||
Source
|
||
fn len(&self) -> usize
|
||
Source
|
||
fn stream(&self) -> &Arc<CudaStream>
|
||
Source
|
||
fn num_bytes(&self) -> usize
|
||
Source
|
||
fn is_empty(&self) -> bool
|
||
Source
|
||
impl<T> Drop for CudaSlice<T>
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Source
|
||
impl<'a, 'b: 'a, T> PushKernelArg<&'b CudaSlice<T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b CudaSlice<T>) -> &mut Self
|
||
Source
|
||
impl<'a, 'b: 'a, T> PushKernelArg<&'b mut CudaSlice<T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b mut CudaSlice<T>) -> &mut Self
|
||
Source
|
||
impl<T: Clone + Default + DeviceRepr> TryFrom<CudaSlice<T>> for Vec<T>
|
||
Source
|
||
type Error = DriverError
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: CudaSlice<T>) -> Result<Self, Self::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T> Send for CudaSlice<T>
|
||
Source
|
||
impl<T> Sync for CudaSlice<T>
|
||
Auto Trait Implementations
|
||
impl<T> Freeze for CudaSlice<T>
|
||
impl<T> RefUnwindSafe for CudaSlice<T>
|
||
where
|
||
T: RefUnwindSafe,
|
||
impl<T> Unpin for CudaSlice<T>
|
||
impl<T> UnwindSafe for CudaSlice<T>
|
||
where
|
||
T: RefUnwindSafe,
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> CloneToUninit for T
|
||
where
|
||
T: Clone,
|
||
Source
|
||
unsafe fn clone_to_uninit(&self, dest: *mut u8)
|
||
🔬This is a nightly-only experimental API. (clone_to_uninit)
|
||
Performs copy-assignment from self to dest. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T> ToOwned for T
|
||
where
|
||
T: Clone,
|
||
Source
|
||
type Owned = T
|
||
The resulting type after obtaining ownership.
|
||
Source
|
||
fn to_owned(&self) -> T
|
||
Creates owned data from borrowed data, usually by cloning. Read more
|
||
Source
|
||
fn clone_into(&self, target: &mut T)
|
||
Uses borrowed data to replace owned data, usually by cloning. Read more
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
-------------------------------
|
||
pub struct CudaStream { /* private fields */ }
|
||
A wrapper around sys::CUstream that you can schedule work on.
|
||
|
||
Create with CudaContext::new_stream(), CudaContext::default_stream(), or CudaStream::fork().
|
||
Work done on this is asynchronous with respect to the host.
|
||
|
||
See CUDA C/C++ Streams and Concurrency See 3. Stream synchronization behavior See 6.6. Event Management See Out-of-order execution See Dependence analysis
|
||
|
||
Implementations
|
||
Source
|
||
impl CudaStream
|
||
Source
|
||
pub fn memcpy_ftod<'a, T: DeviceRepr, Dst: DevicePtrMut<T>>(
|
||
self: &Arc<Self>,
|
||
fh: &'a FileHandle,
|
||
file_offset: i64,
|
||
dst: &mut Dst,
|
||
) -> Result<AsyncFileRead<'a>, CufileError>
|
||
Copy memory from a file into a destination buffer on the device.
|
||
|
||
The return value of this is initialized with 0, and after the operation successfully finishes on the stream, it will contain a value other than 0. See the docs for possible values.
|
||
|
||
Wrapper around cuFileReadAsync
|
||
|
||
See FileHandle::sync_read() for synchronous version.
|
||
|
||
Source
|
||
pub fn memcpy_dtof<'a, T: DeviceRepr, Src: DevicePtr<T>>(
|
||
self: &Arc<Self>,
|
||
src: &Src,
|
||
fh: &'a mut FileHandle,
|
||
file_offset: i64,
|
||
) -> Result<AsyncFileWrite<'a>, CufileError>
|
||
Copy memory from a device buffer to a file.
|
||
|
||
The return value of this is initialized with 0, and after the operation successfully finishes on the stream, it will contain a value other than 0. See the docs for possible values.
|
||
|
||
Wrapper around cuFileWriteAsync
|
||
|
||
See FileHandle::sync_write() for synchronous version.
|
||
|
||
Source
|
||
impl CudaStream
|
||
Source
|
||
pub fn fork(&self) -> Result<Arc<Self>, DriverError>
|
||
Create’s a new stream and then makes the new stream wait on self
|
||
|
||
Examples found in repository?
|
||
examples/04-streams.rs (line 22)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
Source
|
||
pub fn cu_stream(&self) -> CUstream
|
||
The underlying cuda stream object
|
||
|
||
Safety
|
||
Do not destroy this value.
|
||
|
||
Source
|
||
pub fn context(&self) -> &Arc<CudaContext>
|
||
The context the stream belongs to.
|
||
|
||
Source
|
||
pub fn synchronize(&self) -> Result<(), DriverError>
|
||
Will only block CPU if you call CudaContext::set_flags() with sys::CUctx_flags::CU_CTX_SCHED_BLOCKING_SYNC.
|
||
|
||
See cuda docs
|
||
|
||
Source
|
||
pub fn record_event(
|
||
&self,
|
||
flags: Option<CUevent_flags>,
|
||
) -> Result<CudaEvent, DriverError>
|
||
Creates a new CudaEvent and records the current work in the stream to the event.
|
||
|
||
Source
|
||
pub fn wait(&self, event: &CudaEvent) -> Result<(), DriverError>
|
||
Waits for the work recorded in CudaEvent to be completed.
|
||
|
||
You can record new work in event after calling this method without affecting this call.
|
||
|
||
See cuda docs
|
||
|
||
Source
|
||
pub fn join(&self, other: &CudaStream) -> Result<(), DriverError>
|
||
Ensures this stream waits for the current workload in other to complete. This is shorthand for self.wait(other.record_event())
|
||
|
||
Source
|
||
impl CudaStream
|
||
Source
|
||
pub fn null<T>(self: &Arc<Self>) -> Result<CudaSlice<T>, DriverError>
|
||
Allocates an empty CudaSlice with 0 length.
|
||
|
||
Source
|
||
pub unsafe fn alloc<T: DeviceRepr>(
|
||
self: &Arc<Self>,
|
||
len: usize,
|
||
) -> Result<CudaSlice<T>, DriverError>
|
||
Allocates a CudaSlice with len elements of type T.
|
||
|
||
Safety
|
||
This is unsafe because the memory is unset.
|
||
|
||
Examples found in repository?
|
||
examples/01-allocate.rs (line 8)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 // unsafe initialization of unset memory
|
||
8 let _: CudaSlice<f32> = unsafe { stream.alloc::<f32>(10) }?;
|
||
9
|
||
10 // this will have memory initialized as 0
|
||
11 let _: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
12
|
||
13 // initialize with slices!
|
||
14 let _: CudaSlice<usize> = stream.memcpy_stod(&[0; 10])?;
|
||
15 let _: CudaSlice<u32> = stream.memcpy_stod(&[1, 2, 3])?;
|
||
16
|
||
17 Ok(())
|
||
18}
|
||
Source
|
||
pub fn alloc_zeros<T: DeviceRepr + ValidAsZeroBits>(
|
||
self: &Arc<Self>,
|
||
len: usize,
|
||
) -> Result<CudaSlice<T>, DriverError>
|
||
Allocates a CudaSlice with len elements of type T. All values are zero’d out.
|
||
|
||
Examples found in repository?
|
||
examples/01-allocate.rs (line 11)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 // unsafe initialization of unset memory
|
||
8 let _: CudaSlice<f32> = unsafe { stream.alloc::<f32>(10) }?;
|
||
9
|
||
10 // this will have memory initialized as 0
|
||
11 let _: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
12
|
||
13 // initialize with slices!
|
||
14 let _: CudaSlice<usize> = stream.memcpy_stod(&[0; 10])?;
|
||
15 let _: CudaSlice<u32> = stream.memcpy_stod(&[1, 2, 3])?;
|
||
16
|
||
17 Ok(())
|
||
18}
|
||
More examples
|
||
Hide additional examples
|
||
examples/cufile-copy.rs (line 21)
|
||
2fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
3 use std::fs;
|
||
4
|
||
5 use cudarc::{cufile::safe::Cufile, driver::CudaContext};
|
||
6
|
||
7 const N: usize = 100000;
|
||
8 let data: Vec<u8> = (0..N).flat_map(|x| (x as f32).to_le_bytes()).collect();
|
||
9 let data_sz = data.len();
|
||
10 let src_file = "/tmp/cufile_test.bin";
|
||
11 fs::write(src_file, &data)?;
|
||
12
|
||
13 let cufile = Cufile::new()?;
|
||
14 println!("{:?}", cufile.get_properties()?);
|
||
15
|
||
16 let file = fs::File::open(src_file)?;
|
||
17 let handle = cufile.register(file)?;
|
||
18
|
||
19 let ctx = CudaContext::new(0)?;
|
||
20 let stream = ctx.default_stream();
|
||
21 let mut buf = stream.alloc_zeros::<u8>(data_sz)?;
|
||
22
|
||
23 handle.sync_read(0, &mut buf)?;
|
||
24
|
||
25 let verify_dst = stream.memcpy_dtov(&buf)?;
|
||
26 assert_eq!(verify_dst, data);
|
||
27
|
||
28 Ok(())
|
||
29}
|
||
examples/02-copy.rs (line 7)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 let a: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
8 let mut b = stream.alloc_zeros::<f64>(10)?;
|
||
9
|
||
10 // you can do device to device copies of course
|
||
11 stream.memcpy_dtod(&a, &mut b)?;
|
||
12
|
||
13 // but also host to device copys with already allocated buffers
|
||
14 stream.memcpy_htod(&vec![2.0; b.len()], &mut b)?;
|
||
15 // you can use any type of slice
|
||
16 stream.memcpy_htod(&[3.0; 10], &mut b)?;
|
||
17
|
||
18 // you can transfer back using memcpy_dtov
|
||
19 let mut a_host: Vec<f64> = stream.memcpy_dtov(&a)?;
|
||
20 assert_eq!(a_host, [0.0; 10]);
|
||
21
|
||
22 let b_host = stream.memcpy_dtov(&b)?;
|
||
23 assert_eq!(b_host, [3.0; 10]);
|
||
24
|
||
25 // or transfer into a pre allocated slice
|
||
26 stream.memcpy_dtoh(&b, &mut a_host)?;
|
||
27 assert_eq!(a_host, b_host);
|
||
28
|
||
29 Ok(())
|
||
30}
|
||
examples/04-streams.rs (line 16)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
Source
|
||
pub fn memset_zeros<T: DeviceRepr + ValidAsZeroBits, Dst: DevicePtrMut<T>>(
|
||
self: &Arc<Self>,
|
||
dst: &mut Dst,
|
||
) -> Result<(), DriverError>
|
||
Set’s all the memory in dst to 0. dst can be a CudaSlice or CudaViewMut
|
||
|
||
Source
|
||
pub fn memcpy_stod<T: DeviceRepr, Src: HostSlice<T> + ?Sized>(
|
||
self: &Arc<Self>,
|
||
src: &Src,
|
||
) -> Result<CudaSlice<T>, DriverError>
|
||
Copy a [T]/Vec<T>/PinnedHostSlice<T> to a new CudaSlice.
|
||
|
||
Examples found in repository?
|
||
examples/01-allocate.rs (line 14)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 // unsafe initialization of unset memory
|
||
8 let _: CudaSlice<f32> = unsafe { stream.alloc::<f32>(10) }?;
|
||
9
|
||
10 // this will have memory initialized as 0
|
||
11 let _: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
12
|
||
13 // initialize with slices!
|
||
14 let _: CudaSlice<usize> = stream.memcpy_stod(&[0; 10])?;
|
||
15 let _: CudaSlice<u32> = stream.memcpy_stod(&[1, 2, 3])?;
|
||
16
|
||
17 Ok(())
|
||
18}
|
||
More examples
|
||
Hide additional examples
|
||
examples/03-launch-kernel.rs (line 18)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/matmul-kernel.rs (line 40)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
examples/04-streams.rs (line 15)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
Source
|
||
pub fn memcpy_htod<T: DeviceRepr, Src: HostSlice<T> + ?Sized, Dst: DevicePtrMut<T>>(
|
||
self: &Arc<Self>,
|
||
src: &Src,
|
||
dst: &mut Dst,
|
||
) -> Result<(), DriverError>
|
||
Copy a [T]/Vec<T>/PinnedHostSlice<T> into an existing CudaSlice/CudaViewMut.
|
||
|
||
Examples found in repository?
|
||
examples/02-copy.rs (line 14)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 let a: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
8 let mut b = stream.alloc_zeros::<f64>(10)?;
|
||
9
|
||
10 // you can do device to device copies of course
|
||
11 stream.memcpy_dtod(&a, &mut b)?;
|
||
12
|
||
13 // but also host to device copys with already allocated buffers
|
||
14 stream.memcpy_htod(&vec![2.0; b.len()], &mut b)?;
|
||
15 // you can use any type of slice
|
||
16 stream.memcpy_htod(&[3.0; 10], &mut b)?;
|
||
17
|
||
18 // you can transfer back using memcpy_dtov
|
||
19 let mut a_host: Vec<f64> = stream.memcpy_dtov(&a)?;
|
||
20 assert_eq!(a_host, [0.0; 10]);
|
||
21
|
||
22 let b_host = stream.memcpy_dtov(&b)?;
|
||
23 assert_eq!(b_host, [3.0; 10]);
|
||
24
|
||
25 // or transfer into a pre allocated slice
|
||
26 stream.memcpy_dtoh(&b, &mut a_host)?;
|
||
27 assert_eq!(a_host, b_host);
|
||
28
|
||
29 Ok(())
|
||
30}
|
||
Source
|
||
pub fn memcpy_dtov<T: DeviceRepr, Src: DevicePtr<T>>(
|
||
self: &Arc<Self>,
|
||
src: &Src,
|
||
) -> Result<Vec<T>, DriverError>
|
||
Copy a CudaSlice/CudaView to a new Vec<T>.
|
||
|
||
Examples found in repository?
|
||
examples/cufile-copy.rs (line 25)
|
||
2fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
3 use std::fs;
|
||
4
|
||
5 use cudarc::{cufile::safe::Cufile, driver::CudaContext};
|
||
6
|
||
7 const N: usize = 100000;
|
||
8 let data: Vec<u8> = (0..N).flat_map(|x| (x as f32).to_le_bytes()).collect();
|
||
9 let data_sz = data.len();
|
||
10 let src_file = "/tmp/cufile_test.bin";
|
||
11 fs::write(src_file, &data)?;
|
||
12
|
||
13 let cufile = Cufile::new()?;
|
||
14 println!("{:?}", cufile.get_properties()?);
|
||
15
|
||
16 let file = fs::File::open(src_file)?;
|
||
17 let handle = cufile.register(file)?;
|
||
18
|
||
19 let ctx = CudaContext::new(0)?;
|
||
20 let stream = ctx.default_stream();
|
||
21 let mut buf = stream.alloc_zeros::<u8>(data_sz)?;
|
||
22
|
||
23 handle.sync_read(0, &mut buf)?;
|
||
24
|
||
25 let verify_dst = stream.memcpy_dtov(&buf)?;
|
||
26 assert_eq!(verify_dst, data);
|
||
27
|
||
28 Ok(())
|
||
29}
|
||
More examples
|
||
Hide additional examples
|
||
examples/02-copy.rs (line 19)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 let a: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
8 let mut b = stream.alloc_zeros::<f64>(10)?;
|
||
9
|
||
10 // you can do device to device copies of course
|
||
11 stream.memcpy_dtod(&a, &mut b)?;
|
||
12
|
||
13 // but also host to device copys with already allocated buffers
|
||
14 stream.memcpy_htod(&vec![2.0; b.len()], &mut b)?;
|
||
15 // you can use any type of slice
|
||
16 stream.memcpy_htod(&[3.0; 10], &mut b)?;
|
||
17
|
||
18 // you can transfer back using memcpy_dtov
|
||
19 let mut a_host: Vec<f64> = stream.memcpy_dtov(&a)?;
|
||
20 assert_eq!(a_host, [0.0; 10]);
|
||
21
|
||
22 let b_host = stream.memcpy_dtov(&b)?;
|
||
23 assert_eq!(b_host, [3.0; 10]);
|
||
24
|
||
25 // or transfer into a pre allocated slice
|
||
26 stream.memcpy_dtoh(&b, &mut a_host)?;
|
||
27 assert_eq!(a_host, b_host);
|
||
28
|
||
29 Ok(())
|
||
30}
|
||
examples/03-launch-kernel.rs (line 30)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/04-streams.rs (line 37)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
Source
|
||
pub fn memcpy_dtoh<T: DeviceRepr, Src: DevicePtr<T>, Dst: HostSlice<T> + ?Sized>(
|
||
self: &Arc<Self>,
|
||
src: &Src,
|
||
dst: &mut Dst,
|
||
) -> Result<(), DriverError>
|
||
Copy a CudaSlice/CudaView to a existing [T]/Vec<T>/PinnedHostSlice<T>.
|
||
|
||
Examples found in repository?
|
||
examples/02-copy.rs (line 26)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 let a: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
8 let mut b = stream.alloc_zeros::<f64>(10)?;
|
||
9
|
||
10 // you can do device to device copies of course
|
||
11 stream.memcpy_dtod(&a, &mut b)?;
|
||
12
|
||
13 // but also host to device copys with already allocated buffers
|
||
14 stream.memcpy_htod(&vec![2.0; b.len()], &mut b)?;
|
||
15 // you can use any type of slice
|
||
16 stream.memcpy_htod(&[3.0; 10], &mut b)?;
|
||
17
|
||
18 // you can transfer back using memcpy_dtov
|
||
19 let mut a_host: Vec<f64> = stream.memcpy_dtov(&a)?;
|
||
20 assert_eq!(a_host, [0.0; 10]);
|
||
21
|
||
22 let b_host = stream.memcpy_dtov(&b)?;
|
||
23 assert_eq!(b_host, [3.0; 10]);
|
||
24
|
||
25 // or transfer into a pre allocated slice
|
||
26 stream.memcpy_dtoh(&b, &mut a_host)?;
|
||
27 assert_eq!(a_host, b_host);
|
||
28
|
||
29 Ok(())
|
||
30}
|
||
More examples
|
||
Hide additional examples
|
||
examples/matmul-kernel.rs (line 58)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
Source
|
||
pub fn memcpy_dtod<T, Src: DevicePtr<T>, Dst: DevicePtrMut<T>>(
|
||
self: &Arc<Self>,
|
||
src: &Src,
|
||
dst: &mut Dst,
|
||
) -> Result<(), DriverError>
|
||
Copy a CudaSlice/CudaView to a existing CudaSlice/CudaViewMut.
|
||
|
||
Examples found in repository?
|
||
examples/02-copy.rs (line 11)
|
||
3fn main() -> Result<(), DriverError> {
|
||
4 let ctx = CudaContext::new(0)?;
|
||
5 let stream = ctx.default_stream();
|
||
6
|
||
7 let a: CudaSlice<f64> = stream.alloc_zeros::<f64>(10)?;
|
||
8 let mut b = stream.alloc_zeros::<f64>(10)?;
|
||
9
|
||
10 // you can do device to device copies of course
|
||
11 stream.memcpy_dtod(&a, &mut b)?;
|
||
12
|
||
13 // but also host to device copys with already allocated buffers
|
||
14 stream.memcpy_htod(&vec![2.0; b.len()], &mut b)?;
|
||
15 // you can use any type of slice
|
||
16 stream.memcpy_htod(&[3.0; 10], &mut b)?;
|
||
17
|
||
18 // you can transfer back using memcpy_dtov
|
||
19 let mut a_host: Vec<f64> = stream.memcpy_dtov(&a)?;
|
||
20 assert_eq!(a_host, [0.0; 10]);
|
||
21
|
||
22 let b_host = stream.memcpy_dtov(&b)?;
|
||
23 assert_eq!(b_host, [3.0; 10]);
|
||
24
|
||
25 // or transfer into a pre allocated slice
|
||
26 stream.memcpy_dtoh(&b, &mut a_host)?;
|
||
27 assert_eq!(a_host, b_host);
|
||
28
|
||
29 Ok(())
|
||
30}
|
||
Source
|
||
pub fn clone_dtod<T: DeviceRepr, Src: DevicePtr<T>>(
|
||
self: &Arc<Self>,
|
||
src: &Src,
|
||
) -> Result<CudaSlice<T>, DriverError>
|
||
Copy a CudaSlice/CudaView to a new CudaSlice.
|
||
|
||
Source
|
||
impl CudaStream
|
||
Source
|
||
pub unsafe fn upgrade_device_ptr<T>(
|
||
self: &Arc<Self>,
|
||
cu_device_ptr: CUdeviceptr,
|
||
len: usize,
|
||
) -> CudaSlice<T>
|
||
Creates a CudaSlice from a sys::CUdeviceptr. Useful in conjunction with CudaSlice::leak().
|
||
|
||
Safety
|
||
cu_device_ptr must be a valid allocation
|
||
cu_device_ptr must space for len * std::mem::size_of<T>() bytes
|
||
The memory may not be valid for type T, so some sort of memset operation should be called on the memory.
|
||
Source
|
||
impl CudaStream
|
||
Source
|
||
pub fn begin_capture(
|
||
&self,
|
||
mode: CUstreamCaptureMode,
|
||
) -> Result<(), DriverError>
|
||
See cuda docs
|
||
|
||
Source
|
||
pub fn end_capture(
|
||
self: &Arc<Self>,
|
||
flags: CUgraphInstantiate_flags,
|
||
) -> Result<Option<CudaGraph>, DriverError>
|
||
See cuda docs
|
||
|
||
flags is passed to cuGraphInstantiate
|
||
|
||
Source
|
||
pub fn capture_status(&self) -> Result<CUstreamCaptureStatus, DriverError>
|
||
See cuda docs
|
||
|
||
Source
|
||
impl CudaStream
|
||
Source
|
||
pub fn launch_builder<'a>(&'a self, func: &'a CudaFunction) -> LaunchArgs<'a>
|
||
Creates a new kernel launch builder that will launch func on stream self.
|
||
|
||
Add arguments to the builder using LaunchArgs::arg(), and submit it to the stream using LaunchArgs::launch().
|
||
|
||
Examples found in repository?
|
||
examples/05-device-repr.rs (line 48)
|
||
32fn main() -> Result<(), DriverError> {
|
||
33 let ctx = CudaContext::new(0)?;
|
||
34 let stream = ctx.default_stream();
|
||
35
|
||
36 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
37 let module = ctx.load_module(ptx)?;
|
||
38 let f = module.load_function("my_custom_kernel")?;
|
||
39
|
||
40 // try changing some of these values to see a device assert
|
||
41 let thing = MyCoolRustStruct {
|
||
42 a: 1.0,
|
||
43 b: 2.34,
|
||
44 c: 57,
|
||
45 d: 420,
|
||
46 };
|
||
47
|
||
48 let mut builder = stream.launch_builder(&f);
|
||
49 // since MyCoolRustStruct implements DeviceRepr, we can pass it to launch.
|
||
50 builder.arg(&thing);
|
||
51 unsafe { builder.launch(LaunchConfig::for_num_elems(1)) }?;
|
||
52
|
||
53 Ok(())
|
||
54}
|
||
More examples
|
||
Hide additional examples
|
||
examples/03-launch-kernel.rs (line 24)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/matmul-kernel.rs (line 46)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
examples/04-streams.rs (line 25)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
examples/06-threading.rs (line 27)
|
||
12fn main() -> Result<(), DriverError> {
|
||
13 {
|
||
14 // Option 1: sharing ctx & module between threads
|
||
15 thread::scope(|s| {
|
||
16 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
17 let ctx = CudaContext::new(0)?;
|
||
18 let module = ctx.load_module(ptx)?;
|
||
19 for i in 0..10i32 {
|
||
20 let thread_ctx = ctx.clone();
|
||
21 let thread_module = module.clone();
|
||
22 s.spawn(move || {
|
||
23 let stream = thread_ctx.default_stream();
|
||
24 let f = thread_module.load_function("hello_world")?;
|
||
25 unsafe {
|
||
26 stream
|
||
27 .launch_builder(&f)
|
||
28 .arg(&i)
|
||
29 .launch(LaunchConfig::for_num_elems(1))
|
||
30 }
|
||
31 });
|
||
32 }
|
||
33 Ok(())
|
||
34 })?;
|
||
35 }
|
||
36
|
||
37 {
|
||
38 // Option 2: initializing different context in each
|
||
39 // Note that this will still schedule to the same stream since we are using the
|
||
40 // default stream here on the same device.
|
||
41 thread::scope(move |s| {
|
||
42 for i in 0..10i32 {
|
||
43 s.spawn(move || {
|
||
44 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
45 let ctx = CudaContext::new(0)?;
|
||
46 let module = ctx.load_module(ptx)?;
|
||
47 let stream = ctx.default_stream();
|
||
48 let f = module.load_function("hello_world")?;
|
||
49 unsafe {
|
||
50 stream
|
||
51 .launch_builder(&f)
|
||
52 .arg(&i)
|
||
53 .launch(LaunchConfig::for_num_elems(1))
|
||
54 }
|
||
55 });
|
||
56 }
|
||
57 Ok(())
|
||
58 })?;
|
||
59 }
|
||
60
|
||
61 Ok(())
|
||
62}
|
||
Trait Implementations
|
||
Source
|
||
impl Debug for CudaStream
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl Drop for CudaStream
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Source
|
||
impl PartialEq for CudaStream
|
||
Source
|
||
fn eq(&self, other: &CudaStream) -> bool
|
||
Tests for self and other values to be equal, and is used by ==.
|
||
1.0.0 · Source
|
||
fn ne(&self, other: &Rhs) -> bool
|
||
Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
|
||
Source
|
||
impl Eq for CudaStream
|
||
Source
|
||
impl Send for CudaStream
|
||
Source
|
||
impl StructuralPartialEq for CudaStream
|
||
Source
|
||
impl Sync for CudaStream
|
||
Auto Trait Implementations
|
||
impl Freeze for CudaStream
|
||
impl RefUnwindSafe for CudaStream
|
||
impl Unpin for CudaStream
|
||
impl UnwindSafe for CudaStream
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
-------------------------------------------
|
||
pub struct CudaView<'a, T> { /* private fields */ }
|
||
&[T] on a cuda device. An immutable sub-view into a CudaSlice created by CudaSlice::as_view()/CudaSlice::slice().
|
||
|
||
Implementations
|
||
Source
|
||
impl<T> CudaView<'_, T>
|
||
Source
|
||
pub fn len(&self) -> usize
|
||
The number of elements T in this view.
|
||
|
||
Source
|
||
pub fn is_empty(&self) -> bool
|
||
Source
|
||
impl<'a, T> CudaView<'a, T>
|
||
Source
|
||
pub fn slice(&self, bounds: impl RangeBounds<usize>) -> Self
|
||
Creates a CudaView at the specified offset from the start of self.
|
||
|
||
Panics if range.start >= self.len.
|
||
|
||
Example
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
let mut view = slice.slice(0..50);
|
||
let mut view2 = view.slice(0..25);
|
||
do_something(&view);
|
||
Source
|
||
pub fn try_slice(&self, bounds: impl RangeBounds<usize>) -> Option<Self>
|
||
Fallible version of CudaView::slice
|
||
|
||
Source
|
||
pub unsafe fn transmute<S>(&self, len: usize) -> Option<CudaView<'a, S>>
|
||
Reinterprets the slice of memory into a different type. len is the number of elements of the new type S that are expected. If not enough bytes are allocated in self for the view, then this returns None.
|
||
|
||
Safety
|
||
This is unsafe because not the memory for the view may not be a valid interpretation for the type S.
|
||
|
||
Source
|
||
pub fn split_at(&self, mid: usize) -> (Self, Self)
|
||
Source
|
||
pub fn try_split_at(&self, mid: usize) -> Option<(Self, Self)>
|
||
Fallible version of CudaSlice::split_at.
|
||
|
||
Returns None if mid > self.len.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl<'a, T: Debug> Debug for CudaView<'a, T>
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl<T> DevicePtr<T> for CudaView<'_, T>
|
||
Source
|
||
fn device_ptr<'a>(
|
||
&'a self,
|
||
stream: &'a CudaStream,
|
||
) -> (CUdeviceptr, SyncOnDrop<'a>)
|
||
Retrieve the device pointer with the intent to read the device memory associated with it. Read more
|
||
Source
|
||
impl<T> DeviceSlice<T> for CudaView<'_, T>
|
||
Source
|
||
fn len(&self) -> usize
|
||
Source
|
||
fn stream(&self) -> &Arc<CudaStream>
|
||
Source
|
||
fn num_bytes(&self) -> usize
|
||
Source
|
||
fn is_empty(&self) -> bool
|
||
Source
|
||
impl<'a, 'b: 'a, 'c: 'b, T> PushKernelArg<&'b CudaView<'c, T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b CudaView<'c, T>) -> &mut Self
|
||
Auto Trait Implementations
|
||
impl<'a, T> Freeze for CudaView<'a, T>
|
||
impl<'a, T> RefUnwindSafe for CudaView<'a, T>
|
||
where
|
||
T: RefUnwindSafe,
|
||
impl<'a, T> Send for CudaView<'a, T>
|
||
where
|
||
T: Sync,
|
||
impl<'a, T> Sync for CudaView<'a, T>
|
||
where
|
||
T: Sync,
|
||
impl<'a, T> Unpin for CudaView<'a, T>
|
||
impl<'a, T> UnwindSafe for CudaView<'a, T>
|
||
where
|
||
T: RefUnwindSafe,
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
------------------------------------------------------
|
||
|
||
pub struct CudaViewMut<'a, T> { /* private fields */ }
|
||
&mut [T] on a cuda device. A mutable sub-view into a CudaSlice created by CudaSlice::as_view_mut()/CudaSlice::slice_mut().
|
||
|
||
Implementations
|
||
Source
|
||
impl<T> CudaViewMut<'_, T>
|
||
Source
|
||
pub fn len(&self) -> usize
|
||
Number of elements T that are in this view.
|
||
|
||
Source
|
||
pub fn is_empty(&self) -> bool
|
||
Source
|
||
pub fn as_view(&self) -> CudaView<'_, T>
|
||
Downgrade this to a &[T]
|
||
|
||
Source
|
||
impl<'a, T> CudaViewMut<'a, T>
|
||
Source
|
||
pub fn slice<'b: 'a>(
|
||
&'b self,
|
||
bounds: impl RangeBounds<usize>,
|
||
) -> CudaView<'a, T>
|
||
Creates a CudaView at the specified offset from the start of self.
|
||
|
||
Panics if range and 0...self.len() are not overlapping.
|
||
|
||
Example
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
let mut view = slice.slice_mut(0..50);
|
||
let mut view2 = view.slice_mut(0..25);
|
||
do_something(&mut view2);
|
||
One cannot slice twice into the same CudaViewMut:
|
||
|
||
ⓘ
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
let mut view = slice.slice_mut(0..50);
|
||
// cannot borrow twice from same view
|
||
let mut view1 = slice.slice_mut(0..25);
|
||
let mut view2 = slice.slice_mut(25..50);
|
||
do_something(view1, view2);
|
||
If you need non-overlapping mutable views into a CudaViewMut, you can use CudaViewMut::split_at_mut().
|
||
|
||
Source
|
||
pub fn try_slice<'b: 'a>(
|
||
&'b self,
|
||
bounds: impl RangeBounds<usize>,
|
||
) -> Option<CudaView<'a, T>>
|
||
Fallible version of CudaViewMut::slice
|
||
|
||
Source
|
||
pub unsafe fn transmute<S>(&self, len: usize) -> Option<CudaView<'a, S>>
|
||
Reinterprets the slice of memory into a different type. len is the number of elements of the new type S that are expected. If not enough bytes are allocated in self for the view, then this returns None.
|
||
|
||
Safety
|
||
This is unsafe because not the memory for the view may not be a valid interpretation for the type S.
|
||
|
||
Source
|
||
pub fn slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> Self
|
||
Creates a CudaViewMut at the specified offset from the start of self.
|
||
|
||
Panics if range and 0...self.len() are not overlapping.
|
||
|
||
Source
|
||
pub fn try_slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> Option<Self>
|
||
Fallible version of CudaViewMut::slice_mut
|
||
|
||
Source
|
||
pub fn split_at_mut(&mut self, mid: usize) -> (Self, Self)
|
||
Splits the CudaViewMut into two at the given index.
|
||
|
||
Panics if mid > self.len.
|
||
|
||
This method can be used to create non-overlapping mutable views into a CudaViewMut.
|
||
|
||
let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
|
||
let mut view = slice.slice_mut(0..50);
|
||
// split the view into two non-overlapping, mutable views
|
||
let (mut view1, mut view2) = view.split_at_mut(25);
|
||
do_something(view1, view2);
|
||
Source
|
||
pub fn try_split_at_mut(&mut self, mid: usize) -> Option<(Self, Self)>
|
||
Fallible version of CudaViewMut::split_at_mut.
|
||
|
||
Returns None if mid > self.len
|
||
|
||
Source
|
||
pub unsafe fn transmute_mut<S>(
|
||
&mut self,
|
||
len: usize,
|
||
) -> Option<CudaViewMut<'a, S>>
|
||
Reinterprets the slice of memory into a different type. len is the number of elements of the new type S that are expected. If not enough bytes are allocated in self for the view, then this returns None.
|
||
|
||
Safety
|
||
This is unsafe because not the memory for the view may not be a valid interpretation for the type S.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl<'a, T: Debug> Debug for CudaViewMut<'a, T>
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl<T> DevicePtr<T> for CudaViewMut<'_, T>
|
||
Source
|
||
fn device_ptr<'a>(
|
||
&'a self,
|
||
stream: &'a CudaStream,
|
||
) -> (CUdeviceptr, SyncOnDrop<'a>)
|
||
Retrieve the device pointer with the intent to read the device memory associated with it. Read more
|
||
Source
|
||
impl<T> DevicePtrMut<T> for CudaViewMut<'_, T>
|
||
Source
|
||
fn device_ptr_mut<'a>(
|
||
&'a mut self,
|
||
stream: &'a CudaStream,
|
||
) -> (CUdeviceptr, SyncOnDrop<'a>)
|
||
Retrieve the device pointer with the intent to modify the device memory associated with it. Read more
|
||
Source
|
||
impl<T> DeviceSlice<T> for CudaViewMut<'_, T>
|
||
Source
|
||
fn len(&self) -> usize
|
||
Source
|
||
fn stream(&self) -> &Arc<CudaStream>
|
||
Source
|
||
fn num_bytes(&self) -> usize
|
||
Source
|
||
fn is_empty(&self) -> bool
|
||
Source
|
||
impl<'a, 'b: 'a, 'c: 'b, T> PushKernelArg<&'b mut CudaViewMut<'c, T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b mut CudaViewMut<'c, T>) -> &mut Self
|
||
Auto Trait Implementations
|
||
impl<'a, T> Freeze for CudaViewMut<'a, T>
|
||
impl<'a, T> RefUnwindSafe for CudaViewMut<'a, T>
|
||
where
|
||
T: RefUnwindSafe,
|
||
impl<'a, T> Send for CudaViewMut<'a, T>
|
||
where
|
||
T: Send,
|
||
impl<'a, T> Sync for CudaViewMut<'a, T>
|
||
where
|
||
T: Sync,
|
||
impl<'a, T> Unpin for CudaViewMut<'a, T>
|
||
impl<'a, T> !UnwindSafe for CudaViewMut<'a, T>
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
--------------------------------------------------
|
||
|
||
pub struct ExternalMemory { /* private fields */ }
|
||
An abstraction for imported external memory.
|
||
|
||
This struct can be created via CudaContext::import_external_memory(). The imported external memory will be destroyed when this struct is dropped.
|
||
|
||
Implementations
|
||
Source
|
||
impl ExternalMemory
|
||
Source
|
||
pub fn map_all(self) -> Result<MappedBuffer, DriverError>
|
||
Map the whole external memory to get mapped buffer.
|
||
|
||
Source
|
||
pub fn map_range(self, range: Range<usize>) -> Result<MappedBuffer, DriverError>
|
||
Map a range of the external memory to a mapped buffer.
|
||
|
||
Only one mapped buffer is allowed at a time. This is more restrictive than it necessarily needs to be, but it makes enforcing safety easier.
|
||
|
||
Panics
|
||
This function will panic if the range is invalid, such as when the start or end is larger than the size.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl Debug for ExternalMemory
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl Drop for ExternalMemory
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Auto Trait Implementations
|
||
impl Freeze for ExternalMemory
|
||
impl RefUnwindSafe for ExternalMemory
|
||
impl !Send for ExternalMemory
|
||
impl !Sync for ExternalMemory
|
||
impl Unpin for ExternalMemory
|
||
impl UnwindSafe for ExternalMemory
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
------------------------------------------------------------
|
||
pub struct LaunchArgs<'a> { /* private fields */ }
|
||
The kernel launch builder. Instantiate with CudaStream::launch_builder(), and then launch the kernel with LaunchArgs::launch()
|
||
|
||
Anything added as a kernel argument with LaunchArgs::arg() must either:
|
||
|
||
Implement DeviceRepr
|
||
Add a custom implementation of impl<'a> PushKernelArg<T> for LaunchArgs<'a>, where T is your type.
|
||
Implementations
|
||
Source
|
||
impl LaunchArgs<'_>
|
||
Source
|
||
pub fn record_kernel_launch(&mut self, flags: CUevent_flags) -> &mut Self
|
||
Calling this will make LaunchArgs::launch() and LaunchArgs::launch_cooperative() return 2 CudaEvents that recorded before and after the kernel is submitted.
|
||
|
||
Source
|
||
pub unsafe fn launch(
|
||
&mut self,
|
||
cfg: LaunchConfig,
|
||
) -> Result<Option<(CudaEvent, CudaEvent)>, DriverError>
|
||
Submits the configuration CudaFunction to execute asychronously on the configured device stream.
|
||
|
||
Safety
|
||
This is generally unsafe for two main reasons:
|
||
|
||
We can’t guarantee that the arguments are valid for the configured CudaFunction. We don’t know if the types are correct, if the arguments are in the correct order, if the types are representable in CUDA, etc.
|
||
We can’t guarantee that the cuda kernel follows the mutability of the arguments configured with LaunchArgs::arg(). For instance, you can pass a reference to a CudaSlice, which on rust side can’t be mutated, but on cuda side the kernel can mutate it.
|
||
CudaFunction can access memory outside of limits.
|
||
Handling asynchronous mutation
|
||
All CudaSlice/CudaView/CudaViewMut contain 2 events that record when the data associated with them are read from/written to.
|
||
|
||
The PushKernelArg implementation of these adds these events to LaunchArgs, so when LaunchArgs::launch() is called, we properly do multi stream synchronization.
|
||
|
||
So in practice it is not possible to have multiple kernels concurrently modify device data while using the safe api.
|
||
|
||
Handling use after free
|
||
Since LaunchArgs::launch() properly records reads/writes for CudaSlice/CudaView/CudaViewMut, and the drop implementation of CudaSlice waits on those events to finish, we will never encounter a use after free situation.
|
||
|
||
Examples found in repository?
|
||
examples/05-device-repr.rs (line 51)
|
||
32fn main() -> Result<(), DriverError> {
|
||
33 let ctx = CudaContext::new(0)?;
|
||
34 let stream = ctx.default_stream();
|
||
35
|
||
36 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
37 let module = ctx.load_module(ptx)?;
|
||
38 let f = module.load_function("my_custom_kernel")?;
|
||
39
|
||
40 // try changing some of these values to see a device assert
|
||
41 let thing = MyCoolRustStruct {
|
||
42 a: 1.0,
|
||
43 b: 2.34,
|
||
44 c: 57,
|
||
45 d: 420,
|
||
46 };
|
||
47
|
||
48 let mut builder = stream.launch_builder(&f);
|
||
49 // since MyCoolRustStruct implements DeviceRepr, we can pass it to launch.
|
||
50 builder.arg(&thing);
|
||
51 unsafe { builder.launch(LaunchConfig::for_num_elems(1)) }?;
|
||
52
|
||
53 Ok(())
|
||
54}
|
||
More examples
|
||
Hide additional examples
|
||
examples/03-launch-kernel.rs (line 28)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/matmul-kernel.rs (line 56)
|
||
22fn main() -> Result<(), DriverError> {
|
||
23 let start = std::time::Instant::now();
|
||
24
|
||
25 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
26 println!("Compilation succeeded in {:?}", start.elapsed());
|
||
27
|
||
28 let ctx = CudaContext::new(0)?;
|
||
29 let stream = ctx.default_stream();
|
||
30 println!("Built in {:?}", start.elapsed());
|
||
31
|
||
32 let module = ctx.load_module(ptx)?;
|
||
33 let f = module.load_function("matmul")?;
|
||
34 println!("Loaded in {:?}", start.elapsed());
|
||
35
|
||
36 let a_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
37 let b_host = [1.0f32, 2.0, 3.0, 4.0];
|
||
38 let mut c_host = [0.0f32; 4];
|
||
39
|
||
40 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
41 let b_dev = stream.memcpy_stod(&b_host)?;
|
||
42 let mut c_dev = stream.memcpy_stod(&c_host)?;
|
||
43
|
||
44 println!("Copied in {:?}", start.elapsed());
|
||
45
|
||
46 let mut builder = stream.launch_builder(&f);
|
||
47 builder.arg(&a_dev);
|
||
48 builder.arg(&b_dev);
|
||
49 builder.arg(&mut c_dev);
|
||
50 builder.arg(&2i32);
|
||
51 let cfg = LaunchConfig {
|
||
52 block_dim: (2, 2, 1),
|
||
53 grid_dim: (1, 1, 1),
|
||
54 shared_mem_bytes: 0,
|
||
55 };
|
||
56 unsafe { builder.launch(cfg) }?;
|
||
57
|
||
58 stream.memcpy_dtoh(&c_dev, &mut c_host)?;
|
||
59 println!("Found {:?} in {:?}", c_host, start.elapsed());
|
||
60 Ok(())
|
||
61}
|
||
examples/04-streams.rs (line 29)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
examples/06-threading.rs (line 29)
|
||
12fn main() -> Result<(), DriverError> {
|
||
13 {
|
||
14 // Option 1: sharing ctx & module between threads
|
||
15 thread::scope(|s| {
|
||
16 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
17 let ctx = CudaContext::new(0)?;
|
||
18 let module = ctx.load_module(ptx)?;
|
||
19 for i in 0..10i32 {
|
||
20 let thread_ctx = ctx.clone();
|
||
21 let thread_module = module.clone();
|
||
22 s.spawn(move || {
|
||
23 let stream = thread_ctx.default_stream();
|
||
24 let f = thread_module.load_function("hello_world")?;
|
||
25 unsafe {
|
||
26 stream
|
||
27 .launch_builder(&f)
|
||
28 .arg(&i)
|
||
29 .launch(LaunchConfig::for_num_elems(1))
|
||
30 }
|
||
31 });
|
||
32 }
|
||
33 Ok(())
|
||
34 })?;
|
||
35 }
|
||
36
|
||
37 {
|
||
38 // Option 2: initializing different context in each
|
||
39 // Note that this will still schedule to the same stream since we are using the
|
||
40 // default stream here on the same device.
|
||
41 thread::scope(move |s| {
|
||
42 for i in 0..10i32 {
|
||
43 s.spawn(move || {
|
||
44 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
45 let ctx = CudaContext::new(0)?;
|
||
46 let module = ctx.load_module(ptx)?;
|
||
47 let stream = ctx.default_stream();
|
||
48 let f = module.load_function("hello_world")?;
|
||
49 unsafe {
|
||
50 stream
|
||
51 .launch_builder(&f)
|
||
52 .arg(&i)
|
||
53 .launch(LaunchConfig::for_num_elems(1))
|
||
54 }
|
||
55 });
|
||
56 }
|
||
57 Ok(())
|
||
58 })?;
|
||
59 }
|
||
60
|
||
61 Ok(())
|
||
62}
|
||
Source
|
||
pub unsafe fn launch_cooperative(
|
||
&mut self,
|
||
cfg: LaunchConfig,
|
||
) -> Result<Option<(CudaEvent, CudaEvent)>, DriverError>
|
||
Launch a cooperative kernel.
|
||
|
||
Safety
|
||
See LaunchArgs::launch()
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl<'a> Debug for LaunchArgs<'a>
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl<'a, 'b: 'a, T> PushKernelArg<&'b CudaSlice<T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b CudaSlice<T>) -> &mut Self
|
||
Source
|
||
impl<'a, 'b: 'a, 'c: 'b, T> PushKernelArg<&'b CudaView<'c, T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b CudaView<'c, T>) -> &mut Self
|
||
Source
|
||
impl<'a, 'b: 'a, T: DeviceRepr> PushKernelArg<&'b T> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b T) -> &mut Self
|
||
Source
|
||
impl<'a, 'b: 'a, T> PushKernelArg<&'b UnifiedSlice<T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b UnifiedSlice<T>) -> &mut Self
|
||
Source
|
||
impl<'a, 'b: 'a, T> PushKernelArg<&'b mut CudaSlice<T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b mut CudaSlice<T>) -> &mut Self
|
||
Source
|
||
impl<'a, 'b: 'a, 'c: 'b, T> PushKernelArg<&'b mut CudaViewMut<'c, T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b mut CudaViewMut<'c, T>) -> &mut Self
|
||
Source
|
||
impl<'a, 'b: 'a, T> PushKernelArg<&'b mut UnifiedSlice<T>> for LaunchArgs<'a>
|
||
Source
|
||
fn arg(&mut self, arg: &'b mut UnifiedSlice<T>) -> &mut Self
|
||
Auto Trait Implementations
|
||
impl<'a> Freeze for LaunchArgs<'a>
|
||
impl<'a> RefUnwindSafe for LaunchArgs<'a>
|
||
impl<'a> !Send for LaunchArgs<'a>
|
||
impl<'a> !Sync for LaunchArgs<'a>
|
||
impl<'a> Unpin for LaunchArgs<'a>
|
||
impl<'a> UnwindSafe for LaunchArgs<'a>
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
---------------------------------------------------------------
|
||
|
||
pub struct LaunchConfig {
|
||
pub grid_dim: (u32, u32, u32),
|
||
pub block_dim: (u32, u32, u32),
|
||
pub shared_mem_bytes: u32,
|
||
}
|
||
Configuration for result::launch_kernel
|
||
|
||
See cuda docs for description of each parameter.
|
||
|
||
Fields
|
||
grid_dim: (u32, u32, u32)
|
||
(width, height, depth) of grid in blocks
|
||
|
||
block_dim: (u32, u32, u32)
|
||
(x, y, z) dimension of each thread block
|
||
|
||
shared_mem_bytes: u32
|
||
Dynamic shared-memory size per thread block in bytes
|
||
|
||
Implementations
|
||
Source
|
||
impl LaunchConfig
|
||
Source
|
||
pub fn for_num_elems(n: u32) -> Self
|
||
Creates a LaunchConfig with:
|
||
|
||
block_dim == 1024
|
||
grid_dim == (n + 1023) / 1024
|
||
shared_mem_bytes == 0
|
||
Examples found in repository?
|
||
examples/05-device-repr.rs (line 51)
|
||
32fn main() -> Result<(), DriverError> {
|
||
33 let ctx = CudaContext::new(0)?;
|
||
34 let stream = ctx.default_stream();
|
||
35
|
||
36 let ptx = compile_ptx(PTX_SRC).unwrap();
|
||
37 let module = ctx.load_module(ptx)?;
|
||
38 let f = module.load_function("my_custom_kernel")?;
|
||
39
|
||
40 // try changing some of these values to see a device assert
|
||
41 let thing = MyCoolRustStruct {
|
||
42 a: 1.0,
|
||
43 b: 2.34,
|
||
44 c: 57,
|
||
45 d: 420,
|
||
46 };
|
||
47
|
||
48 let mut builder = stream.launch_builder(&f);
|
||
49 // since MyCoolRustStruct implements DeviceRepr, we can pass it to launch.
|
||
50 builder.arg(&thing);
|
||
51 unsafe { builder.launch(LaunchConfig::for_num_elems(1)) }?;
|
||
52
|
||
53 Ok(())
|
||
54}
|
||
More examples
|
||
Hide additional examples
|
||
examples/03-launch-kernel.rs (line 23)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 // You can load a function from a pre-compiled PTX like so:
|
||
11 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
12
|
||
13 // and then load a function from it:
|
||
14 let f = module.load_function("sin_kernel").unwrap();
|
||
15
|
||
16 let a_host = [1.0, 2.0, 3.0];
|
||
17
|
||
18 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
19 let mut b_dev = a_dev.clone();
|
||
20
|
||
21 // we use a buidler pattern to launch kernels.
|
||
22 let n = 3i32;
|
||
23 let cfg = LaunchConfig::for_num_elems(n as u32);
|
||
24 let mut launch_args = stream.launch_builder(&f);
|
||
25 launch_args.arg(&mut b_dev);
|
||
26 launch_args.arg(&a_dev);
|
||
27 launch_args.arg(&n);
|
||
28 unsafe { launch_args.launch(cfg) }?;
|
||
29
|
||
30 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
31 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
32
|
||
33 println!("Found {b_host:?}");
|
||
34 println!("Expected {:?}", a_host.map(f32::sin));
|
||
35 assert_eq!(&a_host, a_host_2.as_slice());
|
||
36
|
||
37 Ok(())
|
||
38}
|
||
examples/04-streams.rs (line 29)
|
||
6fn main() -> Result<(), DriverError> {
|
||
7 let ctx = CudaContext::new(0)?;
|
||
8 let stream = ctx.default_stream();
|
||
9
|
||
10 let module = ctx.load_module(Ptx::from_file("./examples/sin.ptx"))?;
|
||
11 let f = module.load_function("sin_kernel")?;
|
||
12
|
||
13 let n = 3i32;
|
||
14 let a_host = [1.0, 2.0, 3.0];
|
||
15 let a_dev = stream.memcpy_stod(&a_host)?;
|
||
16 let mut b_dev = stream.alloc_zeros::<f32>(n as usize)?;
|
||
17
|
||
18 // we can safely create a second stream using [CudaStream::fork()].
|
||
19 // This synchronizes with the source stream, so
|
||
20 // the `memcpy_vtod` & `alloc_zeros` above will complete **before**
|
||
21 // work on this stream can start.
|
||
22 let stream2 = stream.fork()?;
|
||
23
|
||
24 // now we launch this work on the other stream
|
||
25 let mut builder = stream2.launch_builder(&f);
|
||
26 builder.arg(&mut b_dev); // NOTE: tells cudarc that we are mutating this.
|
||
27 builder.arg(&a_dev); // NOTE: tells cudarc that we are reading from this slice
|
||
28 builder.arg(&n);
|
||
29 unsafe { builder.launch(LaunchConfig::for_num_elems(n as u32)) }?;
|
||
30
|
||
31 // cudarc automatically manages multi stream synchronization,
|
||
32 // so even though we launched the above on a separate stream,
|
||
33 // doing this device to host transfer will still properly synchronize.
|
||
34 // a_dev doesn't need to synchronize at all since we specified it is just
|
||
35 // being read from.
|
||
36 // b_dev DOES need to be synchronized, because it was mutated on a different stream.
|
||
37 let a_host_2 = stream.memcpy_dtov(&a_dev)?;
|
||
38 let b_host = stream.memcpy_dtov(&b_dev)?;
|
||
39
|
||
40 println!("Found {b_host:?}");
|
||
41 println!("Expected {:?}", a_host.map(f32::sin));
|
||
42 assert_eq!(&a_host, a_host_2.as_slice());
|
||
43
|
||
44 Ok(())
|
||
45}
|
||
examples/06-threading.rs (line 29)
|
||
12fn main() -> Result<(), DriverError> {
|
||
13 {
|
||
14 // Option 1: sharing ctx & module between threads
|
||
15 thread::scope(|s| {
|
||
16 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
17 let ctx = CudaContext::new(0)?;
|
||
18 let module = ctx.load_module(ptx)?;
|
||
19 for i in 0..10i32 {
|
||
20 let thread_ctx = ctx.clone();
|
||
21 let thread_module = module.clone();
|
||
22 s.spawn(move || {
|
||
23 let stream = thread_ctx.default_stream();
|
||
24 let f = thread_module.load_function("hello_world")?;
|
||
25 unsafe {
|
||
26 stream
|
||
27 .launch_builder(&f)
|
||
28 .arg(&i)
|
||
29 .launch(LaunchConfig::for_num_elems(1))
|
||
30 }
|
||
31 });
|
||
32 }
|
||
33 Ok(())
|
||
34 })?;
|
||
35 }
|
||
36
|
||
37 {
|
||
38 // Option 2: initializing different context in each
|
||
39 // Note that this will still schedule to the same stream since we are using the
|
||
40 // default stream here on the same device.
|
||
41 thread::scope(move |s| {
|
||
42 for i in 0..10i32 {
|
||
43 s.spawn(move || {
|
||
44 let ptx = compile_ptx(KERNEL_SRC).unwrap();
|
||
45 let ctx = CudaContext::new(0)?;
|
||
46 let module = ctx.load_module(ptx)?;
|
||
47 let stream = ctx.default_stream();
|
||
48 let f = module.load_function("hello_world")?;
|
||
49 unsafe {
|
||
50 stream
|
||
51 .launch_builder(&f)
|
||
52 .arg(&i)
|
||
53 .launch(LaunchConfig::for_num_elems(1))
|
||
54 }
|
||
55 });
|
||
56 }
|
||
57 Ok(())
|
||
58 })?;
|
||
59 }
|
||
60
|
||
61 Ok(())
|
||
62}
|
||
Trait Implementations
|
||
Source
|
||
impl Clone for LaunchConfig
|
||
Source
|
||
fn clone(&self) -> LaunchConfig
|
||
Returns a duplicate of the value. Read more
|
||
1.0.0 · Source
|
||
fn clone_from(&mut self, source: &Self)
|
||
Performs copy-assignment from source. Read more
|
||
Source
|
||
impl Debug for LaunchConfig
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl Copy for LaunchConfig
|
||
Auto Trait Implementations
|
||
impl Freeze for LaunchConfig
|
||
impl RefUnwindSafe for LaunchConfig
|
||
impl Send for LaunchConfig
|
||
impl Sync for LaunchConfig
|
||
impl Unpin for LaunchConfig
|
||
impl UnwindSafe for LaunchConfig
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> CloneToUninit for T
|
||
where
|
||
T: Clone,
|
||
Source
|
||
unsafe fn clone_to_uninit(&self, dest: *mut u8)
|
||
🔬This is a nightly-only experimental API. (clone_to_uninit)
|
||
Performs copy-assignment from self to dest. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T> ToOwned for T
|
||
where
|
||
T: Clone,
|
||
Source
|
||
type Owned = T
|
||
The resulting type after obtaining ownership.
|
||
Source
|
||
fn to_owned(&self) -> T
|
||
Creates owned data from borrowed data, usually by cloning. Read more
|
||
Source
|
||
fn clone_into(&self, target: &mut T)
|
||
Uses borrowed data to replace owned data, usually by cloning. Read more
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
--------------------------------------------------------------
|
||
|
||
pub struct MappedBuffer { /* private fields */ }
|
||
An abstraction for a mapped buffer for some external memory.
|
||
|
||
This struct can be created via ExternalMemory::map_range or ExternalMemory::map_all. The underlying mapped buffer will be freed when this struct is dropped.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl Debug for MappedBuffer
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl DevicePtr<u8> for MappedBuffer
|
||
Source
|
||
fn device_ptr<'a>(
|
||
&'a self,
|
||
stream: &'a CudaStream,
|
||
) -> (CUdeviceptr, SyncOnDrop<'a>)
|
||
Retrieve the device pointer with the intent to read the device memory associated with it. Read more
|
||
Source
|
||
impl DeviceSlice<u8> for MappedBuffer
|
||
Source
|
||
fn len(&self) -> usize
|
||
Source
|
||
fn stream(&self) -> &Arc<CudaStream>
|
||
Source
|
||
fn num_bytes(&self) -> usize
|
||
Source
|
||
fn is_empty(&self) -> bool
|
||
Source
|
||
impl Drop for MappedBuffer
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Auto Trait Implementations
|
||
impl Freeze for MappedBuffer
|
||
impl RefUnwindSafe for MappedBuffer
|
||
impl !Send for MappedBuffer
|
||
impl !Sync for MappedBuffer
|
||
impl Unpin for MappedBuffer
|
||
impl UnwindSafe for MappedBuffer
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
----------------------------------------------------------
|
||
|
||
pub struct PinnedHostSlice<T> { /* private fields */ }
|
||
Rust side data that the cuda driver knows is pinned. This is different than Pin<Vec<T>> mainly because cuda driver manages this memory and ensures it is page locked.
|
||
|
||
Allocate this with CudaContext::alloc_pinned(), and do device copies with CudaStream::memcpy_stod()/CudaStream::memcpy_htod()/CudaStream::memcpy_dtoh()
|
||
|
||
Implementations
|
||
Source
|
||
impl<T> PinnedHostSlice<T>
|
||
Source
|
||
pub fn context(&self) -> &Arc<CudaContext>
|
||
The context this was created in.
|
||
|
||
Source
|
||
pub fn len(&self) -> usize
|
||
The number of elements T in this slice.
|
||
|
||
Source
|
||
pub fn num_bytes(&self) -> usize
|
||
The number of bytes in this slice.
|
||
|
||
Source
|
||
pub fn is_empty(&self) -> bool
|
||
Source
|
||
impl<T: ValidAsZeroBits> PinnedHostSlice<T>
|
||
Source
|
||
pub fn as_ptr(&self) -> Result<*const T, DriverError>
|
||
Waits for any scheduled work to complete and then returns a refernce to the host side data.
|
||
|
||
Source
|
||
pub fn as_mut_ptr(&mut self) -> Result<*mut T, DriverError>
|
||
Waits for any scheduled work to complete and then returns a refernce to the host side data.
|
||
|
||
Source
|
||
pub fn as_slice(&self) -> Result<&[T], DriverError>
|
||
Waits for any scheduled work to complete and then returns a refernce to the host side data.
|
||
|
||
Source
|
||
pub fn as_mut_slice(&mut self) -> Result<&mut [T], DriverError>
|
||
Waits for any scheduled work to complete and then returns a refernce to the host side data.
|
||
|
||
Trait Implementations
|
||
Source
|
||
impl<T: Debug> Debug for PinnedHostSlice<T>
|
||
Source
|
||
fn fmt(&self, f: &mut Formatter<'_>) -> Result
|
||
Formats the value using the given formatter. Read more
|
||
Source
|
||
impl<T> Drop for PinnedHostSlice<T>
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Source
|
||
impl<T> HostSlice<T> for PinnedHostSlice<T>
|
||
Source
|
||
fn len(&self) -> usize
|
||
Source
|
||
unsafe fn stream_synced_slice<'a>(
|
||
&'a self,
|
||
stream: &'a CudaStream,
|
||
) -> (&'a [T], SyncOnDrop<'a>)
|
||
Safety Read more
|
||
Source
|
||
unsafe fn stream_synced_mut_slice<'a>(
|
||
&'a mut self,
|
||
stream: &'a CudaStream,
|
||
) -> (&'a mut [T], SyncOnDrop<'a>)
|
||
Safety Read more
|
||
Source
|
||
fn is_empty(&self) -> bool
|
||
Source
|
||
impl<T> Send for PinnedHostSlice<T>
|
||
Source
|
||
impl<T> Sync for PinnedHostSlice<T>
|
||
Auto Trait Implementations
|
||
impl<T> Freeze for PinnedHostSlice<T>
|
||
impl<T> RefUnwindSafe for PinnedHostSlice<T>
|
||
where
|
||
T: RefUnwindSafe,
|
||
impl<T> Unpin for PinnedHostSlice<T>
|
||
impl<T> UnwindSafe for PinnedHostSlice<T>
|
||
where
|
||
T: RefUnwindSafe,
|
||
Blanket Implementations
|
||
Source§
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|
||
|
||
----------------------------------------------------------
|
||
|
||
pub struct Profiler;
|
||
Calls profiler_start() in Profiler::new(), and profiler_stop() in Drop.
|
||
|
||
Implementations
|
||
Source
|
||
impl Profiler
|
||
Source
|
||
pub fn new() -> Result<Self, DriverError>
|
||
Enables profile collection by the active profiling tool for the current context. If profiling is already enabled, then Profiler::new() has no effect. More info in Cuda docs
|
||
|
||
use cudarc::driver::{Profiler};
|
||
|
||
{
|
||
let profiler = Profiler::new()?;
|
||
// Hotpath
|
||
// Profiler stops on drop
|
||
}
|
||
// Now check your results
|
||
// nsys profile -c cudaProfilerApi /path/to/bin
|
||
// And this will profile only the hotpath.
|
||
Trait Implementations
|
||
Source
|
||
impl Default for Profiler
|
||
Source
|
||
fn default() -> Profiler
|
||
Returns the “default value” for a type. Read more
|
||
Source
|
||
impl Drop for Profiler
|
||
Source
|
||
fn drop(&mut self)
|
||
Executes the destructor for this type. Read more
|
||
Auto Trait Implementations
|
||
impl Freeze for Profiler
|
||
impl RefUnwindSafe for Profiler
|
||
impl Send for Profiler
|
||
impl Sync for Profiler
|
||
impl Unpin for Profiler
|
||
impl UnwindSafe for Profiler
|
||
Blanket Implementations
|
||
Source
|
||
impl<T> Any for T
|
||
where
|
||
T: 'static + ?Sized,
|
||
Source
|
||
fn type_id(&self) -> TypeId
|
||
Gets the TypeId of self. Read more
|
||
Source
|
||
impl<T> Borrow<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow(&self) -> &T
|
||
Immutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> BorrowMut<T> for T
|
||
where
|
||
T: ?Sized,
|
||
Source
|
||
fn borrow_mut(&mut self) -> &mut T
|
||
Mutably borrows from an owned value. Read more
|
||
Source
|
||
impl<T> From<T> for T
|
||
Source
|
||
fn from(t: T) -> T
|
||
Returns the argument unchanged.
|
||
|
||
Source
|
||
impl<T, U> Into<U> for T
|
||
where
|
||
U: From<T>,
|
||
Source
|
||
fn into(self) -> U
|
||
Calls U::from(self).
|
||
|
||
That is, this conversion is whatever the implementation of From<T> for U chooses to do.
|
||
|
||
Source
|
||
impl<T, U> TryFrom<U> for T
|
||
where
|
||
U: Into<T>,
|
||
Source
|
||
type Error = Infallible
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<T, U> TryInto<U> for T
|
||
where
|
||
U: TryFrom<T>,
|
||
Source
|
||
type Error = <U as TryFrom<T>>::Error
|
||
The type returned in the event of a conversion error.
|
||
Source
|
||
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
|
||
Performs the conversion.
|
||
Source
|
||
impl<V, T> VZip<V> for T
|
||
where
|
||
V: MultiLane<T>,
|