cudarc/driver/safe/
core.rs

1use crate::driver::{
2    result::{self, DriverError},
3    sys::{self, CUfunc_cache_enum, CUfunction_attribute_enum},
4};
5
6use std::{
7    ffi::CString,
8    marker::PhantomData,
9    ops::{Bound, RangeBounds},
10    string::String,
11    sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering},
12    sync::Arc,
13    vec::Vec,
14};
15
16/// Represents a primary cuda context on a certain device. When created with [CudaContext::new()] it will
17/// push a new primary context onto the stack.
18///
19/// This is the entrypoint to using any cuda calls, all objects maintain a pointer to `Arc<CudaContext>`
20/// to ensure proper lifetimes.
21///
22/// # On thread safety
23///
24/// This object is thread safe and can be shared/used on multiple threads. All safe apis call
25/// [CudaContext::bind_to_thread()] before doing work in a certain context.
26#[derive(Debug)]
27pub struct CudaContext {
28    pub(crate) cu_device: sys::CUdevice,
29    pub(crate) cu_ctx: sys::CUcontext,
30    pub(crate) ordinal: usize,
31    pub(crate) has_async_alloc: bool,
32    pub(crate) num_streams: AtomicUsize,
33    pub(crate) event_tracking: AtomicBool,
34    pub(crate) error_state: AtomicU32,
35}
36
37unsafe impl Send for CudaContext {}
38unsafe impl Sync for CudaContext {}
39
40impl Drop for CudaContext {
41    fn drop(&mut self) {
42        self.record_err(self.bind_to_thread());
43        let ctx = std::mem::replace(&mut self.cu_ctx, std::ptr::null_mut());
44        if !ctx.is_null() {
45            self.record_err(unsafe { result::primary_ctx::release(self.cu_device) });
46        }
47    }
48}
49
50impl PartialEq for CudaContext {
51    fn eq(&self, other: &Self) -> bool {
52        self.cu_device == other.cu_device
53            && self.cu_ctx == other.cu_ctx
54            && self.ordinal == other.ordinal
55    }
56}
57impl Eq for CudaContext {}
58
59impl CudaContext {
60    /// Creates a new context on the specified device ordinal.
61    pub fn new(ordinal: usize) -> Result<Arc<Self>, DriverError> {
62        result::init()?;
63        let cu_device = result::device::get(ordinal as i32)?;
64        let cu_ctx = unsafe { result::primary_ctx::retain(cu_device) }?;
65        let has_async_alloc = unsafe {
66            let memory_pools_supported = result::device::get_attribute(
67                cu_device,
68                sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED,
69            )?;
70            memory_pools_supported > 0
71        };
72        let ctx = Arc::new(CudaContext {
73            cu_device,
74            cu_ctx,
75            ordinal,
76            has_async_alloc,
77            num_streams: AtomicUsize::new(0),
78            event_tracking: AtomicBool::new(true),
79            error_state: AtomicU32::new(0),
80        });
81        ctx.bind_to_thread()?;
82        Ok(ctx)
83    }
84
85    /// The number of devices available.
86    pub fn device_count() -> Result<i32, DriverError> {
87        result::init()?;
88        result::device::get_count()
89    }
90
91    /// Get the `ordinal` index of the device this is on.
92    pub fn ordinal(&self) -> usize {
93        self.ordinal
94    }
95
96    /// Get the name of this device.
97    pub fn name(&self) -> Result<String, result::DriverError> {
98        self.check_err()?;
99        result::device::get_name(self.cu_device)
100    }
101
102    /// Get the UUID of this device.
103    pub fn uuid(&self) -> Result<sys::CUuuid, result::DriverError> {
104        self.check_err()?;
105        result::device::get_uuid(self.cu_device)
106    }
107
108    /// Get the underlying [sys::CUdevice] of this [CudaContext].
109    ///
110    /// # Safety
111    /// While this function is marked as safe, actually using the
112    /// returned object is unsafe.
113    ///
114    /// **You must not free/release the device pointer**, as it is still
115    /// owned by the [CudaContext].
116    pub fn cu_device(&self) -> sys::CUdevice {
117        self.cu_device
118    }
119
120    /// Get the underlying [sys::CUcontext] of this [CudaContext].
121    ///
122    /// # Safety
123    /// While this function is marked as safe, actually using the
124    /// returned object is unsafe.
125    ///
126    /// **You must not free/release the context pointer**, as it is still
127    /// owned by the [CudaContext].
128    pub fn cu_ctx(&self) -> sys::CUcontext {
129        self.cu_ctx
130    }
131
132    /// Binds this context to the calling thread. Calling this is key for thread safety.
133    pub fn bind_to_thread(&self) -> Result<(), DriverError> {
134        self.check_err()?;
135        if match result::ctx::get_current()? {
136            Some(curr_ctx) => curr_ctx != self.cu_ctx,
137            None => true,
138        } {
139            unsafe { result::ctx::set_current(self.cu_ctx) }?;
140        }
141        Ok(())
142    }
143
144    /// Get the value of the specified attribute of the device in [CudaContext].
145    pub fn attribute(&self, attrib: sys::CUdevice_attribute) -> Result<i32, result::DriverError> {
146        self.check_err()?;
147        unsafe { result::device::get_attribute(self.cu_device, attrib) }
148    }
149
150    /// Synchronize this context. Will only block CPU if you call [CudaContext::set_flags()] with
151    /// [sys::CUctx_flags::CU_CTX_SCHED_BLOCKING_SYNC].
152    pub fn synchronize(&self) -> Result<(), DriverError> {
153        self.bind_to_thread()?;
154        result::ctx::synchronize()
155    }
156
157    /// Ensures calls to [CudaContext::synchronize()] block the calling thread.
158    ///
159    /// Sets [sys::CUctx_flags::CU_CTX_SCHED_BLOCKING_SYNC]
160    #[cfg(not(any(
161        feature = "cuda-11040",
162        feature = "cuda-11050",
163        feature = "cuda-11060",
164        feature = "cuda-11070",
165        feature = "cuda-11080",
166        feature = "cuda-12000"
167    )))]
168    pub fn set_blocking_synchronize(&self) -> Result<(), DriverError> {
169        self.set_flags(sys::CUctx_flags::CU_CTX_SCHED_BLOCKING_SYNC)
170    }
171
172    /// Set flags for this context
173    #[cfg(not(any(
174        feature = "cuda-11040",
175        feature = "cuda-11050",
176        feature = "cuda-11060",
177        feature = "cuda-11070",
178        feature = "cuda-11080",
179        feature = "cuda-12000"
180    )))]
181    pub fn set_flags(&self, flags: sys::CUctx_flags) -> Result<(), DriverError> {
182        self.bind_to_thread()?;
183        result::ctx::set_flags(flags)
184    }
185
186    /// Whether multiple streams have been created in this context. If so,
187    /// the [CudaSlice::read] and [CudaSlice::write] events will be activated.
188    ///
189    /// This only get's set to true by [CudaContext::new_stream()].
190    pub fn is_in_multi_stream_mode(&self) -> bool {
191        self.num_streams.load(Ordering::Relaxed) > 0
192    }
193
194    /// Whether event tracking is being managed by this context
195    /// (via [CudaContext::enable_event_tracking()], which is the default behavior),
196    /// or `false` if the user is manually managing stream synchronization
197    /// (via [CudaContext::disable_event_tracking()]).
198    pub fn is_event_tracking(&self) -> bool {
199        self.event_tracking.load(Ordering::Relaxed)
200    }
201
202    /// Whether the context is automatically managing multiple stream synchronization.
203    /// Both of these must be true:
204    /// - [CudaContext::is_in_multi_stream_mode()]
205    /// - [CudaContext::is_event_tracking()]
206    pub fn is_managing_stream_synchronization(&self) -> bool {
207        self.is_in_multi_stream_mode() && self.is_event_tracking()
208    }
209
210    /// When turned on, all [CudaSlice] **created after calling this function** will
211    /// record usages using [CudaEvent] to ensure proper synchronization between streams.
212    ///
213    /// # Safety
214    ///
215    /// If [CudaContext::disable_event_tracking()] was called previously, then any
216    /// [CudaSlice] created after that and before this current call won't have [CudaEvent]
217    /// tracking their uses. Those [CudaSlice] will not manage their synchronization, even
218    /// after this call.
219    pub unsafe fn enable_event_tracking(&self) {
220        self.event_tracking.store(true, Ordering::Relaxed);
221    }
222
223    /// When turned on, all [CudaSlice] **created after calling this function** will
224    /// not track uses via [CudaEvent]s.
225    ///
226    /// # Safety
227    ///
228    /// It is up to the user to ensure proper synchronization between multiple streams:
229    /// - Ensure that no [CudaSlice] is freed before a use on another stream is finished.
230    /// - Ensure that a [CudaSlice] is not used on another stream before allocation on the
231    ///   allocating stream finishes.
232    /// - Ensure that a [CudaSlice] is not written two concurrently by multiple streams.
233    pub unsafe fn disable_event_tracking(&self) {
234        self.event_tracking.store(false, Ordering::Relaxed);
235    }
236
237    /// Checks to see if there have been any calls that stored an Err in a function
238    /// that couldn't return a result (e.g. Drop calls).
239    ///
240    /// If there are any errors stored, this method will return the Err value, and
241    /// then clear the stored error state.
242    pub fn check_err(&self) -> Result<(), DriverError> {
243        let error_state = self.error_state.swap(0, Ordering::Relaxed);
244        if error_state == 0 {
245            Ok(())
246        } else {
247            Err(result::DriverError(unsafe {
248                std::mem::transmute::<u32, sys::cudaError_enum>(error_state)
249            }))
250        }
251    }
252
253    /// Records a result for later inspection when a Result can be returned.
254    pub fn record_err<T>(&self, result: Result<T, DriverError>) {
255        if let Err(err) = result {
256            self.error_state.store(err.0 as u32, Ordering::Relaxed)
257        }
258    }
259}
260
261/// A lightweight synchronization primitive used to synchronize between [CudaStream]s.
262///
263/// - Create using [CudaContext::new_event()].
264/// - Record a point of time in a stream using [CudaEvent::record()].
265/// - Either call [CudaEvent::synchronize()] or [CudaStream::wait()] to use.
266///
267/// Note that calls to [CudaEvent::record()] will not change any **previous calls** to [CudaStream::wait()].
268///
269/// # Thread safety
270/// This object is thread safe
271#[derive(Debug)]
272pub struct CudaEvent {
273    pub(crate) cu_event: sys::CUevent,
274    pub(crate) ctx: Arc<CudaContext>,
275}
276
277unsafe impl Send for CudaEvent {}
278unsafe impl Sync for CudaEvent {}
279
280impl Drop for CudaEvent {
281    fn drop(&mut self) {
282        self.ctx.record_err(self.ctx.bind_to_thread());
283        self.ctx
284            .record_err(unsafe { result::event::destroy(self.cu_event) });
285    }
286}
287
288impl CudaContext {
289    /// Creates a new [CudaEvent] with no work recorded. If `flags` is None, the event is created with
290    /// [sys::CUevent_flags::CU_EVENT_DISABLE_TIMING].
291    pub fn new_event(
292        self: &Arc<Self>,
293        flags: Option<sys::CUevent_flags>,
294    ) -> Result<CudaEvent, DriverError> {
295        let flags = flags.unwrap_or(sys::CUevent_flags::CU_EVENT_DISABLE_TIMING);
296        self.bind_to_thread()?;
297        let cu_event = result::event::create(flags)?;
298        Ok(CudaEvent {
299            cu_event,
300            ctx: self.clone(),
301        })
302    }
303}
304
305impl CudaEvent {
306    /// The underlying cu_event object.
307    ///
308    /// # Safety
309    /// Do not destroy this value
310    pub fn cu_event(&self) -> sys::CUevent {
311        self.cu_event
312    }
313
314    /// The context this was created in.
315    pub fn context(&self) -> &Arc<CudaContext> {
316        &self.ctx
317    }
318
319    /// Records the current amount of work in [CudaStream] into this event.
320    ///
321    /// **This does not affect any previous calls to [CudaStream::wait()]**
322    ///
323    /// If `stream` belongs to a different [CudaContext], this will fail with
324    /// [sys::cudaError_enum::CUDA_ERROR_INVALID_CONTEXT].
325    ///
326    /// See [cuda docs](https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__EVENT.html#group__CUDA__EVENT_1g95424d3be52c4eb95d83861b70fb89d1)
327    pub fn record(&self, stream: &CudaStream) -> Result<(), DriverError> {
328        if self.ctx != stream.ctx {
329            return Err(DriverError(sys::cudaError_enum::CUDA_ERROR_INVALID_CONTEXT));
330        }
331        self.ctx.bind_to_thread()?;
332        unsafe { result::event::record(self.cu_event, stream.cu_stream) }
333    }
334
335    /// Will only block CPU thraed if [sys::CUevent_flags::CU_EVENT_BLOCKING_SYNC] was used to create this event.
336    pub fn synchronize(&self) -> Result<(), DriverError> {
337        self.ctx.bind_to_thread()?;
338        unsafe { result::event::synchronize(self.cu_event) }
339    }
340
341    /// The time between two events. `self` is the start event, and `end` is the end event.
342    /// This is effectively `end - self`.
343    pub fn elapsed_ms(&self, end: &Self) -> Result<f32, DriverError> {
344        if self.ctx != end.ctx {
345            return Err(DriverError(sys::cudaError_enum::CUDA_ERROR_INVALID_CONTEXT));
346        }
347        self.ctx.bind_to_thread()?;
348        self.synchronize()?;
349        end.synchronize()?;
350        unsafe { result::event::elapsed(self.cu_event, end.cu_event) }
351    }
352
353    /// Returns `true` if all recorded work has been completed, `false` otherwise.
354    pub fn is_complete(&self) -> bool {
355        unsafe { result::event::query(self.cu_event) }.is_ok()
356    }
357}
358
359/// A wrapper around [sys::CUstream] that you can schedule work on.
360///
361/// - Create with [CudaContext::new_stream()], [CudaContext::default_stream()], or [CudaStream::fork()].
362///
363/// **Work done on this is asynchronous with respect to the host.**
364///
365/// See [CUDA C/C++ Streams and Concurrency](https://developer.download.nvidia.com/CUDA/training/StreamsAndConcurrencyWebinar.pdf)
366/// See [3. Stream synchronization behavior](https://docs.nvidia.com/cuda/cuda-runtime-api/stream-sync-behavior.html)
367/// See [6.6. Event Management](https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__EVENT.html)
368/// See [Out-of-order execution](https://en.wikipedia.org/wiki/Out-of-order_execution)
369/// See [Dependence analysis](https://en.wikipedia.org/wiki/Dependence_analysis)
370#[derive(Debug, PartialEq, Eq)]
371pub struct CudaStream {
372    pub(crate) cu_stream: sys::CUstream,
373    pub(crate) ctx: Arc<CudaContext>,
374}
375
376unsafe impl Send for CudaStream {}
377unsafe impl Sync for CudaStream {}
378
379impl Drop for CudaStream {
380    fn drop(&mut self) {
381        self.ctx.record_err(self.ctx.bind_to_thread());
382        if !self.cu_stream.is_null() {
383            self.ctx.num_streams.fetch_sub(1, Ordering::Relaxed);
384            self.ctx
385                .record_err(unsafe { result::stream::destroy(self.cu_stream) });
386        }
387    }
388}
389
390impl CudaContext {
391    /// Get's the default stream for this context (the null ptr stream). Note that context's
392    /// on the same device can all submit to the same default stream from separate context objects.
393    pub fn default_stream(self: &Arc<Self>) -> Arc<CudaStream> {
394        Arc::new(CudaStream {
395            cu_stream: std::ptr::null_mut(),
396            ctx: self.clone(),
397        })
398    }
399
400    /// Create a new [sys::CUstream_flags::CU_STREAM_NON_BLOCKING] stream.
401    ///
402    /// This will swap the calling context to multi stream mode [CudaContext::is_in_multi_stream_mode()].
403    /// If the context is not already in multiple stream mode, then this function will also call [CudaContext::synchronize()].
404    pub fn new_stream(self: &Arc<Self>) -> Result<Arc<CudaStream>, DriverError> {
405        self.bind_to_thread()?;
406        let prev_num_streams = self.num_streams.fetch_add(1, Ordering::Relaxed);
407        if prev_num_streams == 0 && self.is_event_tracking() {
408            self.synchronize()?;
409        }
410        let cu_stream = result::stream::create(result::stream::StreamKind::NonBlocking)?;
411        Ok(Arc::new(CudaStream {
412            cu_stream,
413            ctx: self.clone(),
414        }))
415    }
416}
417
418impl CudaStream {
419    /// Create's a new stream and then makes the new stream wait on `self`
420    pub fn fork(&self) -> Result<Arc<Self>, DriverError> {
421        self.ctx.bind_to_thread()?;
422        self.ctx.num_streams.fetch_add(1, Ordering::Relaxed);
423        let cu_stream = result::stream::create(result::stream::StreamKind::NonBlocking)?;
424        let stream = Arc::new(CudaStream {
425            cu_stream,
426            ctx: self.ctx.clone(),
427        });
428        stream.join(self)?;
429        Ok(stream)
430    }
431
432    /// The underlying cuda stream object
433    /// # Safety
434    /// Do not destroy this value.
435    pub fn cu_stream(&self) -> sys::CUstream {
436        self.cu_stream
437    }
438
439    /// The context the stream belongs to.
440    pub fn context(&self) -> &Arc<CudaContext> {
441        &self.ctx
442    }
443
444    /// Will only block CPU if you call [CudaContext::set_flags()] with
445    /// [sys::CUctx_flags::CU_CTX_SCHED_BLOCKING_SYNC].
446    ///
447    /// See [cuda docs](https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__STREAM.html#group__CUDA__STREAM_1g15e49dd91ec15991eb7c0a741beb7dad)
448    pub fn synchronize(&self) -> Result<(), DriverError> {
449        self.ctx.bind_to_thread()?;
450        unsafe { result::stream::synchronize(self.cu_stream) }
451    }
452
453    /// Creates a new [CudaEvent] and records the current work in the stream to the event.
454    pub fn record_event(
455        &self,
456        flags: Option<sys::CUevent_flags>,
457    ) -> Result<CudaEvent, DriverError> {
458        let event = self.ctx.new_event(flags)?;
459        event.record(self)?;
460        Ok(event)
461    }
462
463    /// Waits for the work recorded in [CudaEvent] to be completed.
464    ///
465    /// You can record new work in `event` after calling this method without
466    /// affecting this call.
467    ///
468    /// See [cuda docs](https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__STREAM.html#group__CUDA__STREAM_1g6a898b652dfc6aa1d5c8d97062618b2f)
469    pub fn wait(&self, event: &CudaEvent) -> Result<(), DriverError> {
470        if self.ctx != event.ctx {
471            return Err(DriverError(sys::cudaError_enum::CUDA_ERROR_INVALID_CONTEXT));
472        }
473        self.ctx.bind_to_thread()?;
474        unsafe {
475            result::stream::wait_event(
476                self.cu_stream,
477                event.cu_event,
478                sys::CUevent_wait_flags::CU_EVENT_WAIT_DEFAULT,
479            )
480        }
481    }
482
483    /// Ensures this stream waits for the current workload in `other` to complete.
484    /// This is shorthand for `self.wait(other.record_event())`
485    pub fn join(&self, other: &CudaStream) -> Result<(), DriverError> {
486        self.wait(&other.record_event(None)?)
487    }
488}
489
490/// `Vec<T>` on a cuda device. You can allocate and modify this with [CudaStream].
491///
492/// This object is thread safe.
493#[derive(Debug)]
494pub struct CudaSlice<T> {
495    pub(crate) cu_device_ptr: sys::CUdeviceptr,
496    pub(crate) len: usize,
497    pub(crate) read: Option<CudaEvent>,
498    pub(crate) write: Option<CudaEvent>,
499    pub(crate) stream: Arc<CudaStream>,
500    pub(crate) marker: PhantomData<*const T>,
501}
502
503unsafe impl<T> Send for CudaSlice<T> {}
504unsafe impl<T> Sync for CudaSlice<T> {}
505
506impl<T> Drop for CudaSlice<T> {
507    fn drop(&mut self) {
508        let ctx = &self.stream.ctx;
509        if let Some(read) = self.read.as_ref() {
510            ctx.record_err(self.stream.wait(read));
511        }
512        if let Some(write) = self.write.as_ref() {
513            ctx.record_err(self.stream.wait(write));
514        }
515        ctx.record_err(unsafe { result::free_async(self.cu_device_ptr, self.stream.cu_stream) });
516    }
517}
518
519impl<T> CudaSlice<T> {
520    /// The number of elements of `T` in this object.
521    pub fn len(&self) -> usize {
522        self.len
523    }
524
525    /// The number of bytes in this object.
526    pub fn num_bytes(&self) -> usize {
527        self.len * std::mem::size_of::<T>()
528    }
529
530    /// True if there are no elements in the object.
531    pub fn is_empty(&self) -> bool {
532        self.len == 0
533    }
534
535    /// The device ordinal this belongs to
536    pub fn ordinal(&self) -> usize {
537        self.stream.ctx.ordinal
538    }
539
540    /// The context this belongs to
541    pub fn context(&self) -> &Arc<CudaContext> {
542        &self.stream.ctx
543    }
544
545    /// The stream this object was allocated on and later will be dropped on.
546    pub fn stream(&self) -> &Arc<CudaStream> {
547        &self.stream
548    }
549}
550
551impl<T: DeviceRepr> CudaSlice<T> {
552    /// Allocates copy of self and schedules a device to device copy of memory.
553    pub fn try_clone(&self) -> Result<Self, result::DriverError> {
554        self.stream.clone_dtod(self)
555    }
556}
557
558impl<T: DeviceRepr> Clone for CudaSlice<T> {
559    fn clone(&self) -> Self {
560        self.try_clone().unwrap()
561    }
562}
563
564impl<T: Clone + Default + DeviceRepr> TryFrom<CudaSlice<T>> for Vec<T> {
565    type Error = result::DriverError;
566    fn try_from(value: CudaSlice<T>) -> Result<Self, Self::Error> {
567        value.stream.memcpy_dtov(&value)
568    }
569}
570
571/// `&[T]` on a cuda device. An immutable sub-view into a [CudaSlice] created by [CudaSlice::as_view()]/[CudaSlice::slice()].
572#[derive(Debug)]
573pub struct CudaView<'a, T> {
574    pub(crate) ptr: sys::CUdeviceptr,
575    pub(crate) len: usize,
576    pub(crate) read: &'a Option<CudaEvent>,
577    pub(crate) write: &'a Option<CudaEvent>,
578    pub(crate) stream: &'a Arc<CudaStream>,
579    marker: PhantomData<&'a [T]>,
580}
581
582impl<T> CudaSlice<T> {
583    pub fn as_view(&self) -> CudaView<'_, T> {
584        CudaView {
585            ptr: self.cu_device_ptr,
586            len: self.len,
587            read: &self.read,
588            write: &self.write,
589            stream: &self.stream,
590            marker: PhantomData,
591        }
592    }
593}
594
595impl<T> CudaView<'_, T> {
596    /// The number of elements `T` in this view.
597    pub fn len(&self) -> usize {
598        self.len
599    }
600
601    pub fn is_empty(&self) -> bool {
602        self.len == 0
603    }
604
605    fn resize(&self, start: usize, end: usize) -> Self {
606        assert!(start <= end && end <= self.len);
607        Self {
608            ptr: self.ptr + (start * std::mem::size_of::<T>()) as u64,
609            len: end - start,
610            read: self.read,
611            write: self.write,
612            stream: self.stream,
613            marker: PhantomData,
614        }
615    }
616}
617
618/// `&mut [T]` on a cuda device. A mutable sub-view into a [CudaSlice] created by [CudaSlice::as_view_mut()]/[CudaSlice::slice_mut()].
619#[derive(Debug)]
620pub struct CudaViewMut<'a, T> {
621    pub(crate) ptr: sys::CUdeviceptr,
622    pub(crate) len: usize,
623    pub(crate) read: &'a Option<CudaEvent>,
624    pub(crate) write: &'a Option<CudaEvent>,
625    pub(crate) stream: &'a Arc<CudaStream>,
626    marker: PhantomData<&'a mut [T]>,
627}
628
629impl<T> CudaSlice<T> {
630    pub fn as_view_mut(&mut self) -> CudaViewMut<'_, T> {
631        CudaViewMut {
632            ptr: self.cu_device_ptr,
633            len: self.len,
634            read: &self.read,
635            write: &self.write,
636            stream: &self.stream,
637            marker: PhantomData,
638        }
639    }
640}
641
642impl<T> CudaViewMut<'_, T> {
643    /// Number of elements `T` that are in this view.
644    pub fn len(&self) -> usize {
645        self.len
646    }
647    pub fn is_empty(&self) -> bool {
648        self.len == 0
649    }
650
651    /// Downgrade this to a `&[T]`
652    pub fn as_view(&self) -> CudaView<'_, T> {
653        CudaView {
654            ptr: self.ptr,
655            len: self.len,
656            read: self.read,
657            write: self.write,
658            stream: self.stream,
659            marker: PhantomData,
660        }
661    }
662
663    fn resize(&self, start: usize, end: usize) -> Self {
664        Self {
665            ptr: self.ptr + (start * std::mem::size_of::<T>()) as u64,
666            len: end - start,
667            read: self.read,
668            write: self.write,
669            stream: self.stream,
670            marker: PhantomData,
671        }
672    }
673}
674
675/// Marker trait to indicate that the type is valid
676/// when all of its bits are set to 0.
677///
678/// # Safety
679/// Not all types are valid when all bits are set to 0.
680/// Be very sure when implementing this trait!
681pub unsafe trait ValidAsZeroBits {}
682unsafe impl ValidAsZeroBits for bool {}
683unsafe impl ValidAsZeroBits for i8 {}
684unsafe impl ValidAsZeroBits for i16 {}
685unsafe impl ValidAsZeroBits for i32 {}
686unsafe impl ValidAsZeroBits for i64 {}
687unsafe impl ValidAsZeroBits for i128 {}
688unsafe impl ValidAsZeroBits for isize {}
689unsafe impl ValidAsZeroBits for u8 {}
690unsafe impl ValidAsZeroBits for u16 {}
691unsafe impl ValidAsZeroBits for u32 {}
692unsafe impl ValidAsZeroBits for u64 {}
693unsafe impl ValidAsZeroBits for u128 {}
694unsafe impl ValidAsZeroBits for usize {}
695unsafe impl ValidAsZeroBits for f32 {}
696unsafe impl ValidAsZeroBits for f64 {}
697#[cfg(feature = "f16")]
698unsafe impl ValidAsZeroBits for half::f16 {}
699#[cfg(feature = "f16")]
700unsafe impl ValidAsZeroBits for half::bf16 {}
701unsafe impl<T: ValidAsZeroBits, const M: usize> ValidAsZeroBits for [T; M] {}
702/// Implement `ValidAsZeroBits` for tuples if all elements are `ValidAsZeroBits`,
703///
704/// # Note
705/// This will also implement `ValidAsZeroBits` for a tuple with one element
706macro_rules! impl_tuples {
707    ($t:tt) => {
708        impl_tuples!(@ $t);
709    };
710    // the $l is in front of the reptition to prevent parsing ambiguities
711    ($l:tt $(,$t:tt)+) => {
712        impl_tuples!($($t),+);
713        impl_tuples!(@ $l $(,$t)+);
714    };
715    (@ $($t:tt),+) => {
716        unsafe impl<$($t: ValidAsZeroBits,)+> ValidAsZeroBits for ($($t,)+) {}
717    };
718}
719impl_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);
720
721/// Something that can be copied to device memory and
722/// turned into a parameter for [result::launch_kernel].
723///
724/// # Safety
725///
726/// This is unsafe because a struct should likely
727/// be `#[repr(C)]` to be represented in cuda memory,
728/// and not all types are valid.
729pub unsafe trait DeviceRepr {}
730unsafe impl DeviceRepr for bool {}
731unsafe impl DeviceRepr for i8 {}
732unsafe impl DeviceRepr for i16 {}
733unsafe impl DeviceRepr for i32 {}
734unsafe impl DeviceRepr for i64 {}
735unsafe impl DeviceRepr for i128 {}
736unsafe impl DeviceRepr for isize {}
737unsafe impl DeviceRepr for u8 {}
738unsafe impl DeviceRepr for u16 {}
739unsafe impl DeviceRepr for u32 {}
740unsafe impl DeviceRepr for u64 {}
741unsafe impl DeviceRepr for u128 {}
742unsafe impl DeviceRepr for usize {}
743unsafe impl DeviceRepr for f32 {}
744unsafe impl DeviceRepr for f64 {}
745#[cfg(feature = "f16")]
746unsafe impl DeviceRepr for half::f16 {}
747#[cfg(feature = "f16")]
748unsafe impl DeviceRepr for half::bf16 {}
749
750#[cfg(feature = "f8")]
751unsafe impl DeviceRepr for float8::F8E4M3 {}
752#[cfg(feature = "f8")]
753unsafe impl ValidAsZeroBits for float8::F8E4M3 {}
754
755#[cfg(feature = "f8")]
756unsafe impl DeviceRepr for float8::F8E5M2 {}
757#[cfg(feature = "f8")]
758unsafe impl ValidAsZeroBits for float8::F8E5M2 {}
759
760#[cfg(feature = "f4")]
761unsafe impl DeviceRepr for float4::F4E2M1 {}
762#[cfg(feature = "f4")]
763unsafe impl ValidAsZeroBits for float4::F4E2M1 {}
764
765#[cfg(feature = "f4")]
766unsafe impl DeviceRepr for float4::E8M0 {}
767#[cfg(feature = "f4")]
768unsafe impl ValidAsZeroBits for float4::E8M0 {}
769
770/// Base trait for abstracting over [CudaSlice]/[CudaView]/[CudaViewMut].
771///
772/// Don't use this directly - use [DevicePtr]/[DevicePtrMut].
773pub trait DeviceSlice<T> {
774    fn len(&self) -> usize;
775    fn num_bytes(&self) -> usize {
776        self.len() * std::mem::size_of::<T>()
777    }
778    fn is_empty(&self) -> bool {
779        self.len() == 0
780    }
781    fn stream(&self) -> &Arc<CudaStream>;
782}
783
784impl<T> DeviceSlice<T> for CudaSlice<T> {
785    fn len(&self) -> usize {
786        self.len
787    }
788    fn stream(&self) -> &Arc<CudaStream> {
789        &self.stream
790    }
791}
792
793impl<T> DeviceSlice<T> for CudaView<'_, T> {
794    fn len(&self) -> usize {
795        self.len
796    }
797    fn stream(&self) -> &Arc<CudaStream> {
798        self.stream
799    }
800}
801
802impl<T> DeviceSlice<T> for CudaViewMut<'_, T> {
803    fn len(&self) -> usize {
804        self.len
805    }
806    fn stream(&self) -> &Arc<CudaStream> {
807        self.stream
808    }
809}
810
811/// A synchronization primitive to enable stream & event synchronization.
812/// Primarily used with [DevicePtr] and [DevicePtrMut]
813#[derive(Debug)]
814#[must_use]
815pub enum SyncOnDrop<'a> {
816    /// Will record the stream's workload to the event on drop.
817    Record(Option<(&'a CudaEvent, &'a CudaStream)>),
818    /// Will call stream synchronize on drop.
819    Sync(Option<&'a CudaStream>),
820}
821
822impl<'a> SyncOnDrop<'a> {
823    /// Construct a [SyncOnDrop::Record] variant
824    pub fn record_event(event: &'a Option<CudaEvent>, stream: &'a CudaStream) -> Self {
825        SyncOnDrop::Record(event.as_ref().map(|e| (e, stream)))
826    }
827    /// Construct a [SyncOnDrop::Sync] variant
828    pub fn sync_stream(stream: &'a CudaStream) -> Self {
829        SyncOnDrop::Sync(Some(stream))
830    }
831}
832
833impl Drop for SyncOnDrop<'_> {
834    fn drop(&mut self) {
835        match self {
836            SyncOnDrop::Record(target) => {
837                if let Some((event, stream)) = std::mem::take(target) {
838                    stream.ctx.record_err(event.record(stream));
839                }
840            }
841            SyncOnDrop::Sync(target) => {
842                if let Some(stream) = std::mem::take(target) {
843                    stream.ctx.record_err(stream.synchronize());
844                }
845            }
846        }
847    }
848}
849
850/// Abstraction over [CudaSlice]/[CudaView]
851pub trait DevicePtr<T>: DeviceSlice<T> {
852    /// Retrieve the device pointer with the intent to read the device memory
853    /// associated with it.
854    ///
855    /// Implementations of this method should ensure `stream` waits for any previous
856    /// writes of this memory before continuing (do not need to wait for any previous reads).
857    ///
858    /// The [SyncOnDrop] item of the return tuple should be dropped **after** the read of
859    /// the [sys::CUdeviceptr] is scheduled.
860    ///
861    /// In most cases you can use like:
862    /// ```ignore
863    /// let (src, _record_src) = src.device_ptr(&stream);
864    /// ```
865    /// Which will drop the [SyncOnDrop] at the end of the scope.
866    fn device_ptr<'a>(&'a self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>);
867}
868
869impl<T> DevicePtr<T> for CudaSlice<T> {
870    fn device_ptr<'a>(&'a self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
871        if self.stream.context().is_managing_stream_synchronization() {
872            if let Some(write) = self.write.as_ref() {
873                stream.ctx.record_err(stream.wait(write));
874            }
875        }
876        (
877            self.cu_device_ptr,
878            SyncOnDrop::record_event(&self.read, stream),
879        )
880    }
881}
882
883impl<T> DevicePtr<T> for CudaView<'_, T> {
884    fn device_ptr<'a>(&'a self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
885        if self.stream.context().is_managing_stream_synchronization() {
886            if let Some(write) = self.write.as_ref() {
887                stream.ctx.record_err(stream.wait(write));
888            }
889        }
890        (self.ptr, SyncOnDrop::record_event(self.read, stream))
891    }
892}
893
894impl<T> DevicePtr<T> for CudaViewMut<'_, T> {
895    fn device_ptr<'a>(&'a self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
896        if self.stream.context().is_managing_stream_synchronization() {
897            if let Some(write) = self.write.as_ref() {
898                stream.ctx.record_err(stream.wait(write));
899            }
900        }
901        (self.ptr, SyncOnDrop::record_event(self.read, stream))
902    }
903}
904
905/// Abstraction over [CudaSlice]/[CudaViewMut]
906pub trait DevicePtrMut<T>: DeviceSlice<T> {
907    /// Retrieve the device pointer with the intent to modify the device memory
908    /// associated with it.
909    ///
910    /// Implementations of this method should ensure `stream` waits for any previous
911    /// reads/writes of this memory before continuing.
912    ///
913    /// The [SyncOnDrop] item of the return tuple should be dropped **after** the write of
914    /// the [sys::CUdeviceptr] is scheduled.
915    ///
916    /// In most cases you can use like:
917    /// ```ignore
918    /// let (src, _record_src) = src.device_ptr_mut(&stream);
919    /// ```
920    /// Which will drop the [SyncOnDrop] at the end of the scope.
921    fn device_ptr_mut<'a>(
922        &'a mut self,
923        stream: &'a CudaStream,
924    ) -> (sys::CUdeviceptr, SyncOnDrop<'a>);
925}
926
927impl<T> DevicePtrMut<T> for CudaSlice<T> {
928    fn device_ptr_mut<'a>(
929        &'a mut self,
930        stream: &'a CudaStream,
931    ) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
932        if self.stream.context().is_managing_stream_synchronization() {
933            if let Some(read) = self.read.as_ref() {
934                stream.ctx.record_err(stream.wait(read));
935            }
936            if let Some(write) = self.write.as_ref() {
937                stream.ctx.record_err(stream.wait(write));
938            }
939        }
940        (
941            self.cu_device_ptr,
942            SyncOnDrop::record_event(&self.write, stream),
943        )
944    }
945}
946
947impl<T> DevicePtrMut<T> for CudaViewMut<'_, T> {
948    fn device_ptr_mut<'a>(
949        &'a mut self,
950        stream: &'a CudaStream,
951    ) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
952        if self.stream.context().is_managing_stream_synchronization() {
953            if let Some(read) = self.read.as_ref() {
954                stream.ctx.record_err(stream.wait(read));
955            }
956            if let Some(write) = self.write.as_ref() {
957                stream.ctx.record_err(stream.wait(write));
958            }
959        }
960        (self.ptr, SyncOnDrop::record_event(self.write, stream))
961    }
962}
963
964/// Abstraction over `&[T]`, `&Vec<T>` and [`PinnedHostSlice<T>`].
965pub trait HostSlice<T> {
966    fn len(&self) -> usize;
967    fn is_empty(&self) -> bool {
968        self.len() == 0
969    }
970
971    /// # Safety
972    /// This is **only** safe if the resulting slice is used with `stream`. Otherwise
973    /// You may run into device synchronization errors
974    unsafe fn stream_synced_slice<'a>(
975        &'a self,
976        stream: &'a CudaStream,
977    ) -> (&'a [T], SyncOnDrop<'a>);
978
979    /// # Safety
980    /// This is **only** safe if the resulting slice is used with `stream`. Otherwise
981    /// You may run into device synchronization errors
982    unsafe fn stream_synced_mut_slice<'a>(
983        &'a mut self,
984        stream: &'a CudaStream,
985    ) -> (&'a mut [T], SyncOnDrop<'a>);
986}
987
988impl<T, const N: usize> HostSlice<T> for [T; N] {
989    fn len(&self) -> usize {
990        N
991    }
992    unsafe fn stream_synced_slice<'a>(
993        &'a self,
994        _stream: &'a CudaStream,
995    ) -> (&'a [T], SyncOnDrop<'a>) {
996        (self, SyncOnDrop::Sync(None))
997    }
998    unsafe fn stream_synced_mut_slice<'a>(
999        &'a mut self,
1000        _stream: &'a CudaStream,
1001    ) -> (&'a mut [T], SyncOnDrop<'a>) {
1002        (self, SyncOnDrop::Sync(None))
1003    }
1004}
1005
1006impl<T> HostSlice<T> for [T] {
1007    fn len(&self) -> usize {
1008        self.len()
1009    }
1010    unsafe fn stream_synced_slice<'a>(
1011        &'a self,
1012        _stream: &'a CudaStream,
1013    ) -> (&'a [T], SyncOnDrop<'a>) {
1014        (self, SyncOnDrop::Sync(None))
1015    }
1016    unsafe fn stream_synced_mut_slice<'a>(
1017        &'a mut self,
1018        _stream: &'a CudaStream,
1019    ) -> (&'a mut [T], SyncOnDrop<'a>) {
1020        (self, SyncOnDrop::Sync(None))
1021    }
1022}
1023
1024impl<T> HostSlice<T> for Vec<T> {
1025    fn len(&self) -> usize {
1026        self.len()
1027    }
1028    unsafe fn stream_synced_slice<'a>(
1029        &'a self,
1030        _stream: &'a CudaStream,
1031    ) -> (&'a [T], SyncOnDrop<'a>) {
1032        (self, SyncOnDrop::Sync(None))
1033    }
1034    unsafe fn stream_synced_mut_slice<'a>(
1035        &'a mut self,
1036        _stream: &'a CudaStream,
1037    ) -> (&'a mut [T], SyncOnDrop<'a>) {
1038        (self, SyncOnDrop::Sync(None))
1039    }
1040}
1041
1042/// Rust side data that the `cuda` driver knows is pinned. This is different
1043/// than `Pin<Vec<T>>` mainly because cuda driver manages this memory and ensures
1044/// it is page locked.
1045///
1046/// Allocate this with [CudaContext::alloc_pinned()], and do device copies with
1047/// [CudaStream::memcpy_stod()]/[CudaStream::memcpy_htod()]/[CudaStream::memcpy_dtoh()]
1048#[derive(Debug)]
1049pub struct PinnedHostSlice<T> {
1050    pub(crate) ptr: *mut T,
1051    pub(crate) len: usize,
1052    pub(crate) event: CudaEvent,
1053}
1054
1055unsafe impl<T> Send for PinnedHostSlice<T> {}
1056unsafe impl<T> Sync for PinnedHostSlice<T> {}
1057
1058impl<T> Drop for PinnedHostSlice<T> {
1059    fn drop(&mut self) {
1060        let ctx = &self.event.ctx;
1061        ctx.record_err(self.event.synchronize());
1062        ctx.record_err(unsafe { result::free_host(self.ptr as _) });
1063    }
1064}
1065
1066impl CudaContext {
1067    /// Allocates page locked host memory with [sys::CU_MEMHOSTALLOC_WRITECOMBINED] flags.
1068    ///
1069    /// See [cuda docs](https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__MEM.html#group__CUDA__MEM_1g572ca4011bfcb25034888a14d4e035b9)
1070    ///
1071    /// # Safety
1072    /// 1. This is unsafe because the memory is unset after this call.
1073    pub unsafe fn alloc_pinned<T: DeviceRepr>(
1074        self: &Arc<Self>,
1075        len: usize,
1076    ) -> Result<PinnedHostSlice<T>, DriverError> {
1077        self.bind_to_thread()?;
1078        let ptr = result::malloc_host(
1079            len * std::mem::size_of::<T>(),
1080            sys::CU_MEMHOSTALLOC_WRITECOMBINED,
1081        )?;
1082        let ptr = ptr as *mut T;
1083        assert!(!ptr.is_null());
1084        assert!(len * std::mem::size_of::<T>() < isize::MAX as usize);
1085        assert!(ptr.is_aligned());
1086        let event = self.new_event(Some(sys::CUevent_flags::CU_EVENT_BLOCKING_SYNC))?;
1087        Ok(PinnedHostSlice { ptr, len, event })
1088    }
1089}
1090
1091impl<T> PinnedHostSlice<T> {
1092    /// The context this was created in.
1093    pub fn context(&self) -> &Arc<CudaContext> {
1094        &self.event.ctx
1095    }
1096
1097    /// The number of elements `T` in this slice.
1098    pub fn len(&self) -> usize {
1099        self.len
1100    }
1101
1102    /// The number of bytes in this slice.
1103    pub fn num_bytes(&self) -> usize {
1104        self.len * std::mem::size_of::<T>()
1105    }
1106
1107    pub fn is_empty(&self) -> bool {
1108        self.len() == 0
1109    }
1110}
1111
1112impl<T: ValidAsZeroBits> PinnedHostSlice<T> {
1113    /// Waits for any scheduled work to complete and then returns a refernce
1114    /// to the host side data.
1115    pub fn as_ptr(&self) -> Result<*const T, DriverError> {
1116        self.event.synchronize()?;
1117        Ok(self.ptr)
1118    }
1119
1120    /// Waits for any scheduled work to complete and then returns a refernce
1121    /// to the host side data.
1122    pub fn as_mut_ptr(&mut self) -> Result<*mut T, DriverError> {
1123        self.event.synchronize()?;
1124        Ok(self.ptr)
1125    }
1126
1127    /// Waits for any scheduled work to complete and then returns a refernce
1128    /// to the host side data.
1129    pub fn as_slice(&self) -> Result<&[T], DriverError> {
1130        self.event.synchronize()?;
1131        Ok(unsafe { std::slice::from_raw_parts(self.ptr, self.len) })
1132    }
1133
1134    /// Waits for any scheduled work to complete and then returns a refernce
1135    /// to the host side data.
1136    pub fn as_mut_slice(&mut self) -> Result<&mut [T], DriverError> {
1137        self.event.synchronize()?;
1138        Ok(unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) })
1139    }
1140}
1141
1142impl<T> HostSlice<T> for PinnedHostSlice<T> {
1143    fn len(&self) -> usize {
1144        self.len
1145    }
1146
1147    unsafe fn stream_synced_slice<'a>(
1148        &'a self,
1149        stream: &'a CudaStream,
1150    ) -> (&'a [T], SyncOnDrop<'a>) {
1151        stream.ctx.record_err(stream.wait(&self.event));
1152        (
1153            std::slice::from_raw_parts(self.ptr, self.len),
1154            SyncOnDrop::Record(Some((&self.event, stream))),
1155        )
1156    }
1157    unsafe fn stream_synced_mut_slice<'a>(
1158        &'a mut self,
1159        stream: &'a CudaStream,
1160    ) -> (&'a mut [T], SyncOnDrop<'a>) {
1161        stream.ctx.record_err(stream.wait(&self.event));
1162        (
1163            std::slice::from_raw_parts_mut(self.ptr, self.len),
1164            SyncOnDrop::Record(Some((&self.event, stream))),
1165        )
1166    }
1167}
1168
1169impl CudaStream {
1170    /// Allocates an empty [CudaSlice] with 0 length.
1171    pub fn null<T>(self: &Arc<Self>) -> Result<CudaSlice<T>, result::DriverError> {
1172        self.ctx.bind_to_thread()?;
1173        let cu_device_ptr = if self.ctx.has_async_alloc {
1174            unsafe { result::malloc_async(self.cu_stream, 0) }?
1175        } else {
1176            unsafe { result::malloc_sync(0) }?
1177        };
1178        Ok(CudaSlice {
1179            cu_device_ptr,
1180            len: 0,
1181            read: None,
1182            write: None,
1183            stream: self.clone(),
1184            marker: PhantomData,
1185        })
1186    }
1187
1188    /// Allocates a [CudaSlice] with `len` elements of type `T`.
1189    /// # Safety
1190    /// This is unsafe because the memory is unset.
1191    pub unsafe fn alloc<T: DeviceRepr>(
1192        self: &Arc<Self>,
1193        len: usize,
1194    ) -> Result<CudaSlice<T>, DriverError> {
1195        self.ctx.bind_to_thread()?;
1196        let cu_device_ptr = if self.ctx.has_async_alloc {
1197            result::malloc_async(self.cu_stream, len * std::mem::size_of::<T>())?
1198        } else {
1199            result::malloc_sync(len * std::mem::size_of::<T>())?
1200        };
1201        let (read, write) = if self.ctx.is_event_tracking() {
1202            (
1203                Some(self.ctx.new_event(None)?),
1204                Some(self.ctx.new_event(None)?),
1205            )
1206        } else {
1207            (None, None)
1208        };
1209        Ok(CudaSlice {
1210            cu_device_ptr,
1211            len,
1212            read,
1213            write,
1214            stream: self.clone(),
1215            marker: PhantomData,
1216        })
1217    }
1218
1219    /// Allocates a [CudaSlice] with `len` elements of type `T`. All values are zero'd out.
1220    pub fn alloc_zeros<T: DeviceRepr + ValidAsZeroBits>(
1221        self: &Arc<Self>,
1222        len: usize,
1223    ) -> Result<CudaSlice<T>, DriverError> {
1224        let mut dst = unsafe { self.alloc(len) }?;
1225        self.memset_zeros(&mut dst)?;
1226        Ok(dst)
1227    }
1228
1229    /// Set's all the memory in `dst` to 0. `dst` can be a [CudaSlice] or [CudaViewMut]
1230    pub fn memset_zeros<T: DeviceRepr + ValidAsZeroBits, Dst: DevicePtrMut<T>>(
1231        self: &Arc<Self>,
1232        dst: &mut Dst,
1233    ) -> Result<(), DriverError> {
1234        let num_bytes = dst.num_bytes();
1235        let (dptr, _record) = dst.device_ptr_mut(self);
1236        unsafe { result::memset_d8_async(dptr, 0, num_bytes, self.cu_stream) }?;
1237        Ok(())
1238    }
1239
1240    /// Copy a `[T]`/`Vec<T>`/[`PinnedHostSlice<T>`] to a new [`CudaSlice`].
1241    pub fn memcpy_stod<T: DeviceRepr, Src: HostSlice<T> + ?Sized>(
1242        self: &Arc<Self>,
1243        src: &Src,
1244    ) -> Result<CudaSlice<T>, DriverError> {
1245        let mut dst = unsafe { self.alloc(src.len()) }?;
1246        self.memcpy_htod(src, &mut dst)?;
1247        Ok(dst)
1248    }
1249
1250    /// Copy a `[T]`/`Vec<T>`/[`PinnedHostSlice<T>`] into an existing [`CudaSlice`]/[`CudaViewMut`].
1251    pub fn memcpy_htod<T: DeviceRepr, Src: HostSlice<T> + ?Sized, Dst: DevicePtrMut<T>>(
1252        self: &Arc<Self>,
1253        src: &Src,
1254        dst: &mut Dst,
1255    ) -> Result<(), DriverError> {
1256        assert!(dst.len() >= src.len());
1257        let (src, _record_src) = unsafe { src.stream_synced_slice(self) };
1258        let (dst, _record_dst) = dst.device_ptr_mut(self);
1259        unsafe { result::memcpy_htod_async(dst, src, self.cu_stream) }
1260    }
1261
1262    /// Copy a [`CudaSlice`]/[`CudaView`] to a new [`Vec<T>`].
1263    pub fn memcpy_dtov<T: DeviceRepr, Src: DevicePtr<T>>(
1264        self: &Arc<Self>,
1265        src: &Src,
1266    ) -> Result<Vec<T>, DriverError> {
1267        let mut dst = Vec::with_capacity(src.len());
1268        #[allow(clippy::uninit_vec)]
1269        unsafe {
1270            dst.set_len(src.len())
1271        };
1272        self.memcpy_dtoh(src, &mut dst)?;
1273        Ok(dst)
1274    }
1275
1276    /// Copy a [`CudaSlice`]/[`CudaView`] to a existing `[T]`/[`Vec<T>`]/[`PinnedHostSlice<T>`].
1277    pub fn memcpy_dtoh<T: DeviceRepr, Src: DevicePtr<T>, Dst: HostSlice<T> + ?Sized>(
1278        self: &Arc<Self>,
1279        src: &Src,
1280        dst: &mut Dst,
1281    ) -> Result<(), DriverError> {
1282        assert!(dst.len() >= src.len());
1283        let (src, _record_src) = src.device_ptr(self);
1284        let (dst, _record_dst) = unsafe { dst.stream_synced_mut_slice(self) };
1285        unsafe { result::memcpy_dtoh_async(dst, src, self.cu_stream) }
1286    }
1287
1288    /// Copy a [`CudaSlice`]/[`CudaView`] to a existing [`CudaSlice`]/[`CudaViewMut`].
1289    pub fn memcpy_dtod<T, Src: DevicePtr<T>, Dst: DevicePtrMut<T>>(
1290        self: &Arc<Self>,
1291        src: &Src,
1292        dst: &mut Dst,
1293    ) -> Result<(), DriverError> {
1294        assert!(dst.len() >= src.len());
1295        let num_bytes = src.num_bytes();
1296        let (src, _record_src) = src.device_ptr(self);
1297        let (dst, _record_dst) = dst.device_ptr_mut(self);
1298        unsafe { result::memcpy_dtod_async(dst, src, num_bytes, self.cu_stream) }
1299    }
1300
1301    /// Copy a [`CudaSlice`]/[`CudaView`] to a new [`CudaSlice`].
1302    pub fn clone_dtod<T: DeviceRepr, Src: DevicePtr<T>>(
1303        self: &Arc<Self>,
1304        src: &Src,
1305    ) -> Result<CudaSlice<T>, DriverError> {
1306        let mut dst = unsafe { self.alloc(src.len()) }?;
1307        self.memcpy_dtod(src, &mut dst)?;
1308        Ok(dst)
1309    }
1310}
1311
1312impl<T> CudaSlice<T> {
1313    /// Creates a [CudaView] at the specified offset from the start of `self`.
1314    ///
1315    /// Panics if `range.start >= self.len`.
1316    ///
1317    /// # Example
1318    ///
1319    /// ```rust
1320    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaView};
1321    /// # fn do_something(view: &CudaView<u8>) {}
1322    /// # let ctx = CudaContext::new(0).unwrap();
1323    /// # let stream = ctx.default_stream();
1324    /// let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1325    /// let mut view = slice.slice(0..50);
1326    /// do_something(&view);
1327    /// ```
1328    ///
1329    /// Like a normal slice, borrow checking prevents the underlying [CudaSlice] from being dropped.
1330    /// ```rust,compile_fail
1331    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaView};
1332    /// # fn do_something(view: &CudaView<u8>) {}
1333    /// # let ctx = CudaContext::new(0).unwrap();
1334    /// # let stream = ctx.default_stream();
1335    /// let view = {
1336    ///     let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1337    ///     // cannot return view, since it borrows from slice
1338    ///     slice.slice(0..50)
1339    /// };
1340    /// do_something(&view);
1341    /// ```
1342    pub fn slice(&self, bounds: impl RangeBounds<usize>) -> CudaView<'_, T> {
1343        self.as_view().slice(bounds)
1344    }
1345
1346    /// Fallible version of [CudaSlice::slice()].
1347    pub fn try_slice(&self, bounds: impl RangeBounds<usize>) -> Option<CudaView<'_, T>> {
1348        self.as_view().try_slice(bounds)
1349    }
1350
1351    /// Creates a [CudaViewMut] at the specified offset from the start of `self`.
1352    ///
1353    /// Panics if `range` and `0...self.len()` are not overlapping.
1354    ///
1355    /// # Example
1356    ///
1357    /// ```rust
1358    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaViewMut};
1359    /// # fn do_something(view: &mut CudaViewMut<u8>) {}
1360    /// # let ctx = CudaContext::new(0).unwrap();
1361    /// # let stream = ctx.default_stream();
1362    /// let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1363    /// let mut view = slice.slice_mut(0..50);
1364    /// do_something(&mut view);
1365    /// ```
1366    ///
1367    /// Like a normal mutable slice, borrow checking prevents the underlying [CudaSlice] from being dropped.
1368    /// ```rust,compile_fail
1369    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaViewMut};
1370    /// # fn do_something(view: &mut CudaViewMut<u8>) {}
1371    /// # let ctx = CudaContext::new(0).unwrap();
1372    /// # let stream = ctx.default_stream();
1373    /// let mut view = {
1374    ///     let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1375    ///     // cannot return view, since it borrows from slice
1376    ///     slice.slice_mut(0..50)
1377    /// };
1378    /// do_something(&mut view);
1379    /// ```
1380    ///
1381    /// Like with normal mutable slices, one cannot mutably slice twice into the same [CudaSlice]:
1382    /// ```rust,compile_fail
1383    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaViewMut};
1384    /// # fn do_something(view: CudaViewMut<u8>, view2: CudaViewMut<u8>) {}
1385    /// # let ctx = CudaContext::new(0).unwrap();
1386    /// # let stream = ctx.default_stream();
1387    /// let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1388    /// let mut view1 = slice.slice_mut(0..50);
1389    /// // cannot borrow twice from slice
1390    /// let mut view2 = slice.slice_mut(50..100);
1391    /// do_something(view1, view2);
1392    /// ```
1393    /// If you need non-overlapping mutable views into a [CudaSlice], you can use [CudaSlice::split_at_mut()].
1394    pub fn slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> CudaViewMut<'_, T> {
1395        self.try_slice_mut(bounds).unwrap()
1396    }
1397
1398    /// Fallible version of [CudaSlice::slice_mut]
1399    pub fn try_slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> Option<CudaViewMut<'_, T>> {
1400        to_range(bounds, self.len).map(|(start, end)| self.as_view_mut().resize(start, end))
1401    }
1402
1403    /// Reinterprets the slice of memory into a different type. `len` is the number
1404    /// of elements of the new type `S` that are expected. If not enough bytes
1405    /// are allocated in `self` for the view, then this returns `None`.
1406    ///
1407    /// # Safety
1408    /// This is unsafe because not the memory for the view may not be a valid interpretation
1409    /// for the type `S`.
1410    pub unsafe fn transmute<S>(&self, len: usize) -> Option<CudaView<'_, S>> {
1411        (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
1412            CudaView {
1413                ptr: self.cu_device_ptr,
1414                len,
1415                read: &self.read,
1416                write: &self.write,
1417                stream: &self.stream,
1418                marker: PhantomData,
1419            },
1420        )
1421    }
1422
1423    /// Reinterprets the slice of memory into a different type. `len` is the number
1424    /// of elements of the new type `S` that are expected. If not enough bytes
1425    /// are allocated in `self` for the view, then this returns `None`.
1426    ///
1427    /// # Safety
1428    /// This is unsafe because not the memory for the view may not be a valid interpretation
1429    /// for the type `S`.
1430    pub unsafe fn transmute_mut<S>(&mut self, len: usize) -> Option<CudaViewMut<'_, S>> {
1431        (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
1432            CudaViewMut {
1433                ptr: self.cu_device_ptr,
1434                len,
1435                read: &self.read,
1436                write: &self.write,
1437                stream: &self.stream,
1438                marker: PhantomData,
1439            },
1440        )
1441    }
1442
1443    pub fn split_at(&self, mid: usize) -> (CudaView<'_, T>, CudaView<'_, T>) {
1444        self.try_split_at(mid).unwrap()
1445    }
1446
1447    /// Fallible version of [CudaSlice::split_at]. Returns `None` if `mid > self.len`.
1448    pub fn try_split_at(&self, mid: usize) -> Option<(CudaView<'_, T>, CudaView<'_, T>)> {
1449        (mid <= self.len()).then(|| {
1450            let view = self.as_view();
1451            (view.resize(0, mid), view.resize(mid, self.len))
1452        })
1453    }
1454
1455    /// Splits the [CudaSlice] into two at the given index, returning two [CudaViewMut] for the two halves.
1456    ///
1457    /// Panics if `mid > self.len`.
1458    ///
1459    /// This method can be used to create non-overlapping mutable views into a [CudaSlice].
1460    /// ```rust
1461    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaViewMut};
1462    /// # fn do_something(view: CudaViewMut<u8>, view2: CudaViewMut<u8>) {}
1463    /// # let ctx = CudaContext::new(0).unwrap();
1464    /// # let stream = ctx.default_stream();
1465    /// let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1466    /// // split the slice into two non-overlapping, mutable views
1467    /// let (mut view1, mut view2) = slice.split_at_mut(50);
1468    /// do_something(view1, view2);
1469    /// ```
1470    pub fn split_at_mut(&mut self, mid: usize) -> (CudaViewMut<'_, T>, CudaViewMut<'_, T>) {
1471        self.try_split_at_mut(mid).unwrap()
1472    }
1473
1474    /// Fallible version of [CudaSlice::split_at_mut].
1475    ///
1476    /// Returns `None` if `mid > self.len`.
1477    pub fn try_split_at_mut(
1478        &mut self,
1479        mid: usize,
1480    ) -> Option<(CudaViewMut<'_, T>, CudaViewMut<'_, T>)> {
1481        let length = self.len;
1482        (mid <= length).then(|| {
1483            let view = self.as_view_mut();
1484            (view.resize(0, mid), view.resize(mid, length))
1485        })
1486    }
1487}
1488
1489impl<'a, T> CudaView<'a, T> {
1490    /// Creates a [CudaView] at the specified offset from the start of `self`.
1491    ///
1492    /// Panics if `range.start >= self.len`.
1493    ///
1494    /// # Example
1495    ///
1496    /// ```rust
1497    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaView};
1498    /// # fn do_something(view: &CudaView<u8>) {}
1499    /// # let ctx = CudaContext::new(0).unwrap();
1500    /// # let stream = ctx.default_stream();
1501    /// let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1502    /// let mut view = slice.slice(0..50);
1503    /// let mut view2 = view.slice(0..25);
1504    /// do_something(&view);
1505    /// ```
1506    pub fn slice(&self, bounds: impl RangeBounds<usize>) -> Self {
1507        self.try_slice(bounds).unwrap()
1508    }
1509
1510    /// Fallible version of [CudaView::slice]
1511    pub fn try_slice(&self, bounds: impl RangeBounds<usize>) -> Option<Self> {
1512        to_range(bounds, self.len).map(|(start, end)| self.resize(start, end))
1513    }
1514
1515    /// Reinterprets the slice of memory into a different type. `len` is the number
1516    /// of elements of the new type `S` that are expected. If not enough bytes
1517    /// are allocated in `self` for the view, then this returns `None`.
1518    ///
1519    /// # Safety
1520    /// This is unsafe because not the memory for the view may not be a valid interpretation
1521    /// for the type `S`.
1522    pub unsafe fn transmute<S>(&self, len: usize) -> Option<CudaView<'a, S>> {
1523        (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
1524            CudaView {
1525                ptr: self.ptr,
1526                len,
1527                read: self.read,
1528                write: self.write,
1529                stream: self.stream,
1530                marker: PhantomData,
1531            },
1532        )
1533    }
1534
1535    pub fn split_at(&self, mid: usize) -> (Self, Self) {
1536        self.try_split_at(mid).unwrap()
1537    }
1538
1539    /// Fallible version of [CudaSlice::split_at].
1540    ///
1541    /// Returns `None` if `mid > self.len`.
1542    pub fn try_split_at(&self, mid: usize) -> Option<(Self, Self)> {
1543        (mid <= self.len()).then(|| (self.resize(0, mid), self.resize(mid, self.len)))
1544    }
1545}
1546
1547impl<'a, T> CudaViewMut<'a, T> {
1548    /// Creates a [CudaView] at the specified offset from the start of `self`.
1549    ///
1550    /// Panics if `range` and `0...self.len()` are not overlapping.
1551    ///
1552    /// # Example
1553    ///
1554    /// ```rust
1555    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaViewMut};
1556    /// # fn do_something(view: &mut CudaViewMut<u8>) {}
1557    /// # let ctx = CudaContext::new(0).unwrap();
1558    /// # let stream = ctx.default_stream();
1559    /// let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1560    /// let mut view = slice.slice_mut(0..50);
1561    /// let mut view2 = view.slice_mut(0..25);
1562    /// do_something(&mut view2);
1563    /// ```
1564    ///
1565    /// One cannot slice twice into the same [CudaViewMut]:
1566    /// ```rust,compile_fail
1567    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaViewMut};
1568    /// # fn do_something(view: CudaViewMut<u8>, view2: CudaViewMut<u8>) {}
1569    /// # let ctx = CudaContext::new(0).unwrap();
1570    /// # let stream = ctx.default_stream();
1571    /// let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1572    /// let mut view = slice.slice_mut(0..50);
1573    /// // cannot borrow twice from same view
1574    /// let mut view1 = slice.slice_mut(0..25);
1575    /// let mut view2 = slice.slice_mut(25..50);
1576    /// do_something(view1, view2);
1577    /// ```
1578    /// If you need non-overlapping mutable views into a [CudaViewMut], you can use [CudaViewMut::split_at_mut()].
1579    pub fn slice<'b: 'a>(&'b self, bounds: impl RangeBounds<usize>) -> CudaView<'a, T> {
1580        self.try_slice(bounds).unwrap()
1581    }
1582
1583    /// Fallible version of [CudaViewMut::slice]
1584    pub fn try_slice<'b: 'a>(&'b self, bounds: impl RangeBounds<usize>) -> Option<CudaView<'a, T>> {
1585        to_range(bounds, self.len).map(|(start, end)| self.as_view().resize(start, end))
1586    }
1587
1588    /// Reinterprets the slice of memory into a different type. `len` is the number
1589    /// of elements of the new type `S` that are expected. If not enough bytes
1590    /// are allocated in `self` for the view, then this returns `None`.
1591    ///
1592    /// # Safety
1593    /// This is unsafe because not the memory for the view may not be a valid interpretation
1594    /// for the type `S`.
1595    pub unsafe fn transmute<S>(&self, len: usize) -> Option<CudaView<'a, S>> {
1596        (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
1597            CudaView {
1598                ptr: self.ptr,
1599                len,
1600                read: self.read,
1601                write: self.write,
1602                stream: self.stream,
1603                marker: PhantomData,
1604            },
1605        )
1606    }
1607
1608    /// Creates a [CudaViewMut] at the specified offset from the start of `self`.
1609    ///
1610    /// Panics if `range` and `0...self.len()` are not overlapping.
1611    pub fn slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> Self {
1612        self.try_slice_mut(bounds).unwrap()
1613    }
1614
1615    /// Fallible version of [CudaViewMut::slice_mut]
1616    pub fn try_slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> Option<Self> {
1617        to_range(bounds, self.len).map(|(start, end)| self.resize(start, end))
1618    }
1619
1620    /// Splits the [CudaViewMut] into two at the given index.
1621    ///
1622    /// Panics if `mid > self.len`.
1623    ///
1624    /// This method can be used to create non-overlapping mutable views into a [CudaViewMut].
1625    /// ```rust
1626    /// # use cudarc::driver::safe::{CudaContext, CudaSlice, CudaViewMut};
1627    /// # fn do_something(view: CudaViewMut<u8>, view2: CudaViewMut<u8>) {}
1628    /// # let ctx = CudaContext::new(0).unwrap();
1629    /// # let stream = ctx.default_stream();
1630    /// let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1631    /// let mut view = slice.slice_mut(0..50);
1632    /// // split the view into two non-overlapping, mutable views
1633    /// let (mut view1, mut view2) = view.split_at_mut(25);
1634    /// do_something(view1, view2);
1635    pub fn split_at_mut(&mut self, mid: usize) -> (Self, Self) {
1636        self.try_split_at_mut(mid).unwrap()
1637    }
1638
1639    /// Fallible version of [CudaViewMut::split_at_mut].
1640    ///
1641    /// Returns `None` if `mid > self.len`
1642    pub fn try_split_at_mut(&mut self, mid: usize) -> Option<(Self, Self)> {
1643        (mid <= self.len()).then(|| (self.resize(0, mid), self.resize(mid, self.len)))
1644    }
1645
1646    /// Reinterprets the slice of memory into a different type. `len` is the number
1647    /// of elements of the new type `S` that are expected. If not enough bytes
1648    /// are allocated in `self` for the view, then this returns `None`.
1649    ///
1650    /// # Safety
1651    /// This is unsafe because not the memory for the view may not be a valid interpretation
1652    /// for the type `S`.
1653    pub unsafe fn transmute_mut<S>(&mut self, len: usize) -> Option<CudaViewMut<'a, S>> {
1654        (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
1655            CudaViewMut {
1656                ptr: self.ptr,
1657                len,
1658                read: self.read,
1659                write: self.write,
1660                stream: self.stream,
1661                marker: PhantomData,
1662            },
1663        )
1664    }
1665}
1666
1667fn to_range(range: impl RangeBounds<usize>, len: usize) -> Option<(usize, usize)> {
1668    let start = match range.start_bound() {
1669        Bound::Included(&n) => n,
1670        Bound::Excluded(&n) => n + 1,
1671        Bound::Unbounded => 0,
1672    };
1673    let end = match range.end_bound() {
1674        Bound::Included(&n) => n + 1,
1675        Bound::Excluded(&n) => n,
1676        Bound::Unbounded => len,
1677    };
1678    (end <= len).then_some((start, end))
1679}
1680
1681/// Wrapper around [sys::CUmodule]. Create with [CudaContext::load_module()].
1682///
1683/// Call [CudaModule::load_function] to load a [CudaFunction].
1684#[derive(Debug)]
1685pub struct CudaModule {
1686    pub(crate) cu_module: sys::CUmodule,
1687    pub(crate) ctx: Arc<CudaContext>,
1688}
1689
1690unsafe impl Send for CudaModule {}
1691unsafe impl Sync for CudaModule {}
1692
1693impl Drop for CudaModule {
1694    fn drop(&mut self) {
1695        self.ctx.record_err(self.ctx.bind_to_thread());
1696        self.ctx
1697            .record_err(unsafe { result::module::unload(self.cu_module) });
1698    }
1699}
1700
1701impl CudaContext {
1702    /// Dynamically load a compiled ptx into this context.
1703    ///
1704    /// - `ptx` contains the compiled ptx
1705    pub fn load_module(
1706        self: &Arc<Self>,
1707        ptx: crate::nvrtc::Ptx,
1708    ) -> Result<Arc<CudaModule>, result::DriverError> {
1709        self.bind_to_thread()?;
1710
1711        let cu_module = match ptx.0 {
1712            crate::nvrtc::PtxKind::Image(image) => unsafe {
1713                result::module::load_data(image.as_ptr() as *const _)
1714            },
1715            crate::nvrtc::PtxKind::Src(src) => {
1716                let c_src = CString::new(src).unwrap();
1717                unsafe { result::module::load_data(c_src.as_ptr() as *const _) }
1718            }
1719            crate::nvrtc::PtxKind::File(path) => {
1720                let name_c = CString::new(path.to_str().unwrap()).unwrap();
1721                result::module::load(name_c)
1722            }
1723        }?;
1724        Ok(Arc::new(CudaModule {
1725            cu_module,
1726            ctx: self.clone(),
1727        }))
1728    }
1729}
1730
1731/// Wrapper around [sys::CUfunction]. Used by [CudaStream::launch_builder] to execute kernels.
1732#[derive(Debug, Clone)]
1733pub struct CudaFunction {
1734    pub(crate) cu_function: sys::CUfunction,
1735    #[allow(unused)]
1736    pub(crate) module: Arc<CudaModule>,
1737}
1738
1739unsafe impl Send for CudaFunction {}
1740unsafe impl Sync for CudaFunction {}
1741
1742impl CudaModule {
1743    /// Loads a function from the loaded module with the given name.
1744    pub fn load_function(self: &Arc<Self>, fn_name: &str) -> Result<CudaFunction, DriverError> {
1745        let fn_name_c = CString::new(fn_name).unwrap();
1746        let cu_function = unsafe { result::module::get_function(self.cu_module, fn_name_c) }?;
1747        Ok(CudaFunction {
1748            cu_function,
1749            module: self.clone(),
1750        })
1751    }
1752}
1753
1754impl CudaFunction {
1755    pub fn occupancy_available_dynamic_smem_per_block(
1756        &self,
1757        num_blocks: u32,
1758        block_size: u32,
1759    ) -> Result<usize, result::DriverError> {
1760        let mut dynamic_smem_size: usize = 0;
1761
1762        unsafe {
1763            sys::cuOccupancyAvailableDynamicSMemPerBlock(
1764                &mut dynamic_smem_size,
1765                self.cu_function,
1766                num_blocks as std::ffi::c_int,
1767                block_size as std::ffi::c_int,
1768            )
1769            .result()?
1770        };
1771
1772        Ok(dynamic_smem_size)
1773    }
1774
1775    pub fn occupancy_max_active_blocks_per_multiprocessor(
1776        &self,
1777        block_size: u32,
1778        dynamic_smem_size: usize,
1779        flags: Option<sys::CUoccupancy_flags_enum>,
1780    ) -> Result<u32, result::DriverError> {
1781        let mut num_blocks: std::ffi::c_int = 0;
1782        let flags = flags.unwrap_or(sys::CUoccupancy_flags_enum::CU_OCCUPANCY_DEFAULT);
1783
1784        unsafe {
1785            sys::cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags(
1786                &mut num_blocks,
1787                self.cu_function,
1788                block_size as std::ffi::c_int,
1789                dynamic_smem_size,
1790                flags as std::ffi::c_uint,
1791            )
1792            .result()?
1793        };
1794
1795        Ok(num_blocks as u32)
1796    }
1797
1798    #[cfg(not(any(
1799        feature = "cuda-11070",
1800        feature = "cuda-11060",
1801        feature = "cuda-11050",
1802        feature = "cuda-11040"
1803    )))]
1804    pub fn occupancy_max_active_clusters(
1805        &self,
1806        config: crate::driver::LaunchConfig,
1807        stream: &CudaStream,
1808    ) -> Result<u32, result::DriverError> {
1809        let mut num_clusters: std::ffi::c_int = 0;
1810
1811        let cfg = sys::CUlaunchConfig {
1812            gridDimX: config.grid_dim.0,
1813            gridDimY: config.grid_dim.1,
1814            gridDimZ: config.grid_dim.2,
1815            blockDimX: config.block_dim.0,
1816            blockDimY: config.block_dim.1,
1817            blockDimZ: config.block_dim.2,
1818            sharedMemBytes: config.shared_mem_bytes,
1819            hStream: stream.cu_stream,
1820            attrs: std::ptr::null_mut(),
1821            numAttrs: 0,
1822        };
1823
1824        unsafe {
1825            sys::cuOccupancyMaxActiveClusters(&mut num_clusters, self.cu_function, &cfg).result()?
1826        };
1827
1828        Ok(num_clusters as u32)
1829    }
1830
1831    pub fn occupancy_max_potential_block_size(
1832        &self,
1833        block_size_to_dynamic_smem_size: extern "C" fn(block_size: std::ffi::c_int) -> usize,
1834        dynamic_smem_size: usize,
1835        block_size_limit: u32,
1836        flags: Option<sys::CUoccupancy_flags_enum>,
1837    ) -> Result<(u32, u32), result::DriverError> {
1838        let mut min_grid_size: std::ffi::c_int = 0;
1839        let mut block_size: std::ffi::c_int = 0;
1840        let flags = flags.unwrap_or(sys::CUoccupancy_flags_enum::CU_OCCUPANCY_DEFAULT);
1841
1842        unsafe {
1843            sys::cuOccupancyMaxPotentialBlockSizeWithFlags(
1844                &mut min_grid_size,
1845                &mut block_size,
1846                self.cu_function,
1847                Some(block_size_to_dynamic_smem_size),
1848                dynamic_smem_size,
1849                block_size_limit as std::ffi::c_int,
1850                flags as std::ffi::c_uint,
1851            )
1852            .result()?
1853        };
1854
1855        Ok((min_grid_size as u32, block_size as u32))
1856    }
1857
1858    #[cfg(not(any(
1859        feature = "cuda-11070",
1860        feature = "cuda-11060",
1861        feature = "cuda-11050",
1862        feature = "cuda-11040"
1863    )))]
1864    pub fn occupancy_max_potential_cluster_size(
1865        &self,
1866        config: crate::driver::LaunchConfig,
1867        stream: &CudaStream,
1868    ) -> Result<u32, result::DriverError> {
1869        let mut cluster_size: std::ffi::c_int = 0;
1870
1871        let cfg = sys::CUlaunchConfig {
1872            gridDimX: config.grid_dim.0,
1873            gridDimY: config.grid_dim.1,
1874            gridDimZ: config.grid_dim.2,
1875            blockDimX: config.block_dim.0,
1876            blockDimY: config.block_dim.1,
1877            blockDimZ: config.block_dim.2,
1878            sharedMemBytes: config.shared_mem_bytes,
1879            hStream: stream.cu_stream,
1880            attrs: std::ptr::null_mut(),
1881            numAttrs: 0,
1882        };
1883
1884        unsafe {
1885            sys::cuOccupancyMaxPotentialClusterSize(&mut cluster_size, self.cu_function, &cfg)
1886                .result()?
1887        };
1888
1889        Ok(cluster_size as u32)
1890    }
1891
1892    /// Set the value of a specific attribute of this [CudaFunction].
1893    pub fn set_attribute(
1894        &self,
1895        attribute: CUfunction_attribute_enum,
1896        value: i32,
1897    ) -> Result<(), result::DriverError> {
1898        unsafe { result::function::set_function_attribute(self.cu_function, attribute, value) }
1899    }
1900
1901    /// Set the cache config of this [CudaFunction].
1902    pub fn set_function_cache_config(
1903        &self,
1904        attribute: CUfunc_cache_enum,
1905    ) -> Result<(), result::DriverError> {
1906        unsafe { result::function::set_function_cache_config(self.cu_function, attribute) }
1907    }
1908}
1909
1910impl<T> CudaSlice<T> {
1911    /// Takes ownership of the underlying [sys::CUdeviceptr]. **It is up
1912    /// to the owner to free this value**.
1913    ///
1914    /// Drops the underlying host_buf if there is one.
1915    pub fn leak(self) -> sys::CUdeviceptr {
1916        let ctx = &self.stream.ctx;
1917        // drop self.read
1918        if let Some(read) = self.read.as_ref() {
1919            ctx.record_err(self.stream.wait(read));
1920            ctx.record_err(unsafe { result::event::destroy(read.cu_event) });
1921            unsafe { Arc::decrement_strong_count(Arc::as_ptr(&read.ctx)) };
1922        }
1923
1924        // drop self.write
1925        if let Some(write) = self.write.as_ref() {
1926            ctx.record_err(self.stream.wait(write));
1927            ctx.record_err(unsafe { result::event::destroy(write.cu_event) });
1928            unsafe { Arc::decrement_strong_count(Arc::as_ptr(&write.ctx)) };
1929        }
1930
1931        // drop self.stream
1932        unsafe { Arc::decrement_strong_count(Arc::as_ptr(&self.stream)) };
1933
1934        let ptr = self.cu_device_ptr;
1935        std::mem::forget(self);
1936        ptr
1937    }
1938}
1939
1940impl CudaStream {
1941    /// Creates a [CudaSlice] from a [sys::CUdeviceptr]. Useful in conjunction with
1942    /// [`CudaSlice::leak()`].
1943    ///
1944    /// # Safety
1945    /// - `cu_device_ptr` must be a valid allocation
1946    /// - `cu_device_ptr` must space for `len * std::mem::size_of<T>()` bytes
1947    /// - The memory may not be valid for type `T`, so some sort of memset operation
1948    ///   should be called on the memory.
1949    pub unsafe fn upgrade_device_ptr<T>(
1950        self: &Arc<Self>,
1951        cu_device_ptr: sys::CUdeviceptr,
1952        len: usize,
1953    ) -> CudaSlice<T> {
1954        let (read, write) = if self.ctx.is_event_tracking() {
1955            (
1956                Some(self.ctx.new_event(None).unwrap()),
1957                Some(self.ctx.new_event(None).unwrap()),
1958            )
1959        } else {
1960            (None, None)
1961        };
1962        CudaSlice {
1963            cu_device_ptr,
1964            len,
1965            read,
1966            write,
1967            stream: self.clone(),
1968            marker: PhantomData,
1969        }
1970    }
1971}
1972
1973#[cfg(test)]
1974mod tests {
1975    use std::time::Instant;
1976
1977    use super::*;
1978
1979    #[test]
1980    fn test_transmutes() {
1981        let ctx = CudaContext::new(0).unwrap();
1982        let stream = ctx.default_stream();
1983        let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
1984        assert!(unsafe { slice.transmute::<f32>(25) }.is_some());
1985        assert!(unsafe { slice.transmute::<f32>(26) }.is_none());
1986        assert!(unsafe { slice.transmute_mut::<f32>(25) }.is_some());
1987        assert!(unsafe { slice.transmute_mut::<f32>(26) }.is_none());
1988
1989        {
1990            let view = slice.slice(0..100);
1991            assert!(unsafe { view.transmute::<f32>(25) }.is_some());
1992            assert!(unsafe { view.transmute::<f32>(26) }.is_none());
1993        }
1994
1995        {
1996            let mut view_mut = slice.slice_mut(0..100);
1997            assert!(unsafe { view_mut.transmute::<f32>(25) }.is_some());
1998            assert!(unsafe { view_mut.transmute::<f32>(26) }.is_none());
1999            assert!(unsafe { view_mut.transmute_mut::<f32>(25) }.is_some());
2000            assert!(unsafe { view_mut.transmute_mut::<f32>(26) }.is_none());
2001        }
2002    }
2003
2004    #[test]
2005    fn test_threading() {
2006        let ctx1 = CudaContext::new(0).unwrap();
2007        let ctx2 = ctx1.clone();
2008
2009        let thread1 = std::thread::spawn(move || {
2010            ctx1.bind_to_thread()?;
2011            ctx1.default_stream().alloc_zeros::<f32>(10)
2012        });
2013        let thread2 = std::thread::spawn(move || {
2014            ctx2.bind_to_thread()?;
2015            ctx2.default_stream().alloc_zeros::<f32>(10)
2016        });
2017
2018        let _: crate::driver::CudaSlice<f32> = thread1.join().unwrap().unwrap();
2019        let _: crate::driver::CudaSlice<f32> = thread2.join().unwrap().unwrap();
2020    }
2021
2022    #[test]
2023    fn test_post_build_arc_count() {
2024        let ctx = CudaContext::new(0).unwrap();
2025        assert_eq!(Arc::strong_count(&ctx), 1);
2026    }
2027
2028    #[test]
2029    fn test_post_alloc_arc_counts() {
2030        let ctx = CudaContext::new(0).unwrap();
2031        assert_eq!(Arc::strong_count(&ctx), 1);
2032        let stream = ctx.default_stream();
2033        assert_eq!(Arc::strong_count(&ctx), 2);
2034        let t = stream.alloc_zeros::<f32>(1).unwrap();
2035        assert_eq!(Arc::strong_count(&ctx), 4);
2036        assert_eq!(Arc::strong_count(&stream), 2);
2037        drop(t);
2038        assert_eq!(Arc::strong_count(&ctx), 2);
2039        assert_eq!(Arc::strong_count(&stream), 1);
2040        drop(stream);
2041        assert_eq!(Arc::strong_count(&ctx), 1);
2042    }
2043
2044    #[test]
2045    #[ignore = "must be executed by itself"]
2046    fn test_post_alloc_memory() {
2047        let ctx = CudaContext::new(0).unwrap();
2048        let stream = ctx.default_stream();
2049
2050        let (free1, total1) = result::mem_get_info().unwrap();
2051
2052        let t = stream.memcpy_stod(&[0.0f32; 5]).unwrap();
2053        let (free2, total2) = result::mem_get_info().unwrap();
2054        assert_eq!(total1, total2);
2055        assert!(free2 < free1);
2056
2057        drop(t);
2058        ctx.synchronize().unwrap();
2059
2060        let (free3, total3) = result::mem_get_info().unwrap();
2061        assert_eq!(total2, total3);
2062        assert!(free3 > free2);
2063        assert_eq!(free3, free1);
2064    }
2065
2066    #[test]
2067    fn test_ctx_copy_to_views() {
2068        let ctx = CudaContext::new(0).unwrap();
2069        let stream = ctx.default_stream();
2070
2071        let smalls = [
2072            stream.memcpy_stod(&[-1.0f32, -0.8]).unwrap(),
2073            stream.memcpy_stod(&[-0.6, -0.4]).unwrap(),
2074            stream.memcpy_stod(&[-0.2, 0.0]).unwrap(),
2075            stream.memcpy_stod(&[0.2, 0.4]).unwrap(),
2076            stream.memcpy_stod(&[0.6, 0.8]).unwrap(),
2077        ];
2078        let mut big = stream.alloc_zeros::<f32>(10).unwrap();
2079
2080        let mut offset = 0;
2081        for small in smalls.iter() {
2082            let mut sub = big.slice_mut(offset..offset + small.len());
2083            stream.memcpy_dtod(small, &mut sub).unwrap();
2084            offset += small.len();
2085        }
2086
2087        assert_eq!(
2088            stream.memcpy_dtov(&big).unwrap(),
2089            [-1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8]
2090        );
2091    }
2092
2093    #[test]
2094    fn test_leak_and_upgrade() {
2095        let ctx = CudaContext::new(0).unwrap();
2096        let stream = ctx.default_stream();
2097
2098        let a = stream.memcpy_stod(&[1.0f32, 2.0, 3.0, 4.0, 5.0]).unwrap();
2099
2100        let ptr = a.leak();
2101        let b = unsafe { stream.upgrade_device_ptr::<f32>(ptr, 3) };
2102        assert_eq!(stream.memcpy_dtov(&b).unwrap(), &[1.0, 2.0, 3.0]);
2103
2104        let ptr = b.leak();
2105        let c = unsafe { stream.upgrade_device_ptr::<f32>(ptr, 5) };
2106        assert_eq!(stream.memcpy_dtov(&c).unwrap(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
2107    }
2108
2109    /// See https://github.com/coreylowman/cudarc/issues/160
2110    #[test]
2111    fn test_slice_is_freed_with_correct_context() {
2112        let ctx0 = CudaContext::new(0).unwrap();
2113        let slice = ctx0.default_stream().memcpy_stod(&[1.0; 10]).unwrap();
2114        let ctx1 = CudaContext::new(0).unwrap();
2115        ctx1.bind_to_thread().unwrap();
2116        drop(ctx0);
2117        drop(slice);
2118        drop(ctx1);
2119    }
2120
2121    /// See https://github.com/coreylowman/cudarc/issues/161
2122    #[test]
2123    fn test_copy_uses_correct_context() {
2124        let ctx0 = CudaContext::new(0).unwrap();
2125        let _ctx1 = CudaContext::new(0).unwrap();
2126        let slice = ctx0.default_stream().memcpy_stod(&[1.0; 10]).unwrap();
2127        let _out = ctx0.default_stream().memcpy_dtov(&slice).unwrap();
2128    }
2129
2130    #[test]
2131    fn test_htod_copy_pinned() {
2132        let truth = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
2133        let ctx = CudaContext::new(0).unwrap();
2134        let stream = ctx.default_stream();
2135        let mut pinned = unsafe { ctx.alloc_pinned::<f32>(10) }.unwrap();
2136        pinned.as_mut_slice().unwrap().clone_from_slice(&truth);
2137        assert_eq!(pinned.as_slice().unwrap(), &truth);
2138        let dst = stream.memcpy_stod(&pinned).unwrap();
2139        let host = stream.memcpy_dtov(&dst).unwrap();
2140        assert_eq!(&host, &truth);
2141    }
2142
2143    #[test]
2144    fn test_pinned_copy_is_faster() {
2145        let ctx = CudaContext::new(0).unwrap();
2146        let stream = ctx.new_stream().unwrap();
2147
2148        let n = 100_000;
2149        let n_samples = 5;
2150        let not_pinned = std::vec![0.0f32; n];
2151
2152        let start = Instant::now();
2153        for _ in 0..n_samples {
2154            let _ = stream.memcpy_stod(&not_pinned).unwrap();
2155            stream.synchronize().unwrap();
2156        }
2157        let unpinned_elapsed = start.elapsed() / n_samples;
2158
2159        let pinned = unsafe { ctx.alloc_pinned::<f32>(n) }.unwrap();
2160
2161        let start = Instant::now();
2162        for _ in 0..n_samples {
2163            let _ = stream.memcpy_stod(&pinned).unwrap();
2164            stream.synchronize().unwrap();
2165        }
2166        let pinned_elapsed = start.elapsed() / n_samples;
2167
2168        // pinned memory transfer speed should be at least 2x faster, but this depends
2169        // on device
2170        assert!(
2171            pinned_elapsed.as_secs_f32() * 1.5 < unpinned_elapsed.as_secs_f32(),
2172            "{unpinned_elapsed:?} vs {pinned_elapsed:?}"
2173        );
2174    }
2175}