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#[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 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 pub fn device_count() -> Result<i32, DriverError> {
87 result::init()?;
88 result::device::get_count()
89 }
90
91 pub fn ordinal(&self) -> usize {
93 self.ordinal
94 }
95
96 pub fn name(&self) -> Result<String, result::DriverError> {
98 self.check_err()?;
99 result::device::get_name(self.cu_device)
100 }
101
102 pub fn uuid(&self) -> Result<sys::CUuuid, result::DriverError> {
104 self.check_err()?;
105 result::device::get_uuid(self.cu_device)
106 }
107
108 pub fn cu_device(&self) -> sys::CUdevice {
117 self.cu_device
118 }
119
120 pub fn cu_ctx(&self) -> sys::CUcontext {
129 self.cu_ctx
130 }
131
132 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 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 pub fn synchronize(&self) -> Result<(), DriverError> {
153 self.bind_to_thread()?;
154 result::ctx::synchronize()
155 }
156
157 #[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 #[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 pub fn is_in_multi_stream_mode(&self) -> bool {
191 self.num_streams.load(Ordering::Relaxed) > 0
192 }
193
194 pub fn is_event_tracking(&self) -> bool {
199 self.event_tracking.load(Ordering::Relaxed)
200 }
201
202 pub fn is_managing_stream_synchronization(&self) -> bool {
207 self.is_in_multi_stream_mode() && self.is_event_tracking()
208 }
209
210 pub unsafe fn enable_event_tracking(&self) {
220 self.event_tracking.store(true, Ordering::Relaxed);
221 }
222
223 pub unsafe fn disable_event_tracking(&self) {
234 self.event_tracking.store(false, Ordering::Relaxed);
235 }
236
237 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 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#[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 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 pub fn cu_event(&self) -> sys::CUevent {
311 self.cu_event
312 }
313
314 pub fn context(&self) -> &Arc<CudaContext> {
316 &self.ctx
317 }
318
319 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 pub fn synchronize(&self) -> Result<(), DriverError> {
337 self.ctx.bind_to_thread()?;
338 unsafe { result::event::synchronize(self.cu_event) }
339 }
340
341 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 pub fn is_complete(&self) -> bool {
355 unsafe { result::event::query(self.cu_event) }.is_ok()
356 }
357}
358
359#[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 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 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 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 pub fn cu_stream(&self) -> sys::CUstream {
436 self.cu_stream
437 }
438
439 pub fn context(&self) -> &Arc<CudaContext> {
441 &self.ctx
442 }
443
444 pub fn synchronize(&self) -> Result<(), DriverError> {
449 self.ctx.bind_to_thread()?;
450 unsafe { result::stream::synchronize(self.cu_stream) }
451 }
452
453 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 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 pub fn join(&self, other: &CudaStream) -> Result<(), DriverError> {
486 self.wait(&other.record_event(None)?)
487 }
488}
489
490#[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 pub fn len(&self) -> usize {
522 self.len
523 }
524
525 pub fn num_bytes(&self) -> usize {
527 self.len * std::mem::size_of::<T>()
528 }
529
530 pub fn is_empty(&self) -> bool {
532 self.len == 0
533 }
534
535 pub fn ordinal(&self) -> usize {
537 self.stream.ctx.ordinal
538 }
539
540 pub fn context(&self) -> &Arc<CudaContext> {
542 &self.stream.ctx
543 }
544
545 pub fn stream(&self) -> &Arc<CudaStream> {
547 &self.stream
548 }
549}
550
551impl<T: DeviceRepr> CudaSlice<T> {
552 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#[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 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#[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 pub fn len(&self) -> usize {
645 self.len
646 }
647 pub fn is_empty(&self) -> bool {
648 self.len == 0
649 }
650
651 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
675pub 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] {}
702macro_rules! impl_tuples {
707 ($t:tt) => {
708 impl_tuples!(@ $t);
709 };
710 ($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
721pub 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
770pub 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#[derive(Debug)]
814#[must_use]
815pub enum SyncOnDrop<'a> {
816 Record(Option<(&'a CudaEvent, &'a CudaStream)>),
818 Sync(Option<&'a CudaStream>),
820}
821
822impl<'a> SyncOnDrop<'a> {
823 pub fn record_event(event: &'a Option<CudaEvent>, stream: &'a CudaStream) -> Self {
825 SyncOnDrop::Record(event.as_ref().map(|e| (e, stream)))
826 }
827 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
850pub trait DevicePtr<T>: DeviceSlice<T> {
852 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
905pub trait DevicePtrMut<T>: DeviceSlice<T> {
907 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
964pub trait HostSlice<T> {
966 fn len(&self) -> usize;
967 fn is_empty(&self) -> bool {
968 self.len() == 0
969 }
970
971 unsafe fn stream_synced_slice<'a>(
975 &'a self,
976 stream: &'a CudaStream,
977 ) -> (&'a [T], SyncOnDrop<'a>);
978
979 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#[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 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 pub fn context(&self) -> &Arc<CudaContext> {
1094 &self.event.ctx
1095 }
1096
1097 pub fn len(&self) -> usize {
1099 self.len
1100 }
1101
1102 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 pub fn as_ptr(&self) -> Result<*const T, DriverError> {
1116 self.event.synchronize()?;
1117 Ok(self.ptr)
1118 }
1119
1120 pub fn as_mut_ptr(&mut self) -> Result<*mut T, DriverError> {
1123 self.event.synchronize()?;
1124 Ok(self.ptr)
1125 }
1126
1127 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 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 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 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 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 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 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 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 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 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 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 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 pub fn slice(&self, bounds: impl RangeBounds<usize>) -> CudaView<'_, T> {
1343 self.as_view().slice(bounds)
1344 }
1345
1346 pub fn try_slice(&self, bounds: impl RangeBounds<usize>) -> Option<CudaView<'_, T>> {
1348 self.as_view().try_slice(bounds)
1349 }
1350
1351 pub fn slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> CudaViewMut<'_, T> {
1395 self.try_slice_mut(bounds).unwrap()
1396 }
1397
1398 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 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 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 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 pub fn split_at_mut(&mut self, mid: usize) -> (CudaViewMut<'_, T>, CudaViewMut<'_, T>) {
1471 self.try_split_at_mut(mid).unwrap()
1472 }
1473
1474 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 pub fn slice(&self, bounds: impl RangeBounds<usize>) -> Self {
1507 self.try_slice(bounds).unwrap()
1508 }
1509
1510 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 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 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 pub fn slice<'b: 'a>(&'b self, bounds: impl RangeBounds<usize>) -> CudaView<'a, T> {
1580 self.try_slice(bounds).unwrap()
1581 }
1582
1583 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 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 pub fn slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> Self {
1612 self.try_slice_mut(bounds).unwrap()
1613 }
1614
1615 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 pub fn split_at_mut(&mut self, mid: usize) -> (Self, Self) {
1636 self.try_split_at_mut(mid).unwrap()
1637 }
1638
1639 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 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#[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 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#[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 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 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 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 pub fn leak(self) -> sys::CUdeviceptr {
1916 let ctx = &self.stream.ctx;
1917 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 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 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 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 #[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 #[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(¬_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 assert!(
2171 pinned_elapsed.as_secs_f32() * 1.5 < unpinned_elapsed.as_secs_f32(),
2172 "{unpinned_elapsed:?} vs {pinned_elapsed:?}"
2173 );
2174 }
2175}