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)]
31pub struct CudaContext {
32 pub(crate) cu_device: sys::CUdevice,
33 pub(crate) cu_ctx: sys::CUcontext,
34 pub(crate) ordinal: usize,
35 pub(crate) has_async_alloc: bool,
36 pub(crate) is_primary: bool,
40 pub(crate) num_streams: AtomicUsize,
41 pub(crate) event_tracking: AtomicBool,
42 pub(crate) error_state: AtomicU32,
43}
44
45unsafe impl Send for CudaContext {}
46unsafe impl Sync for CudaContext {}
47
48impl Drop for CudaContext {
49 fn drop(&mut self) {
50 self.record_err(self.bind_to_thread());
51 let ctx = std::mem::replace(&mut self.cu_ctx, std::ptr::null_mut());
52 if !ctx.is_null() {
53 if self.is_primary {
54 self.record_err(unsafe { result::primary_ctx::release(self.cu_device) });
55 } else {
56 self.record_err(unsafe { sys::cuCtxDestroy_v2(ctx).result() });
58 }
59 }
60 }
61}
62
63impl PartialEq for CudaContext {
64 fn eq(&self, other: &Self) -> bool {
65 self.cu_device == other.cu_device
66 && self.cu_ctx == other.cu_ctx
67 && self.ordinal == other.ordinal
68 }
69}
70impl Eq for CudaContext {}
71
72impl CudaContext {
73 pub fn new(ordinal: usize) -> Result<Arc<Self>, DriverError> {
75 result::init()?;
76 let cu_device = result::device::get(ordinal as i32)?;
77 let cu_ctx = unsafe { result::primary_ctx::retain(cu_device) }?;
78 let has_async_alloc = unsafe {
79 let memory_pools_supported = result::device::get_attribute(
80 cu_device,
81 sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED,
82 )?;
83 memory_pools_supported > 0
84 };
85 let ctx = Arc::new(CudaContext {
86 cu_device,
87 cu_ctx,
88 ordinal,
89 has_async_alloc,
90 is_primary: true,
91 num_streams: AtomicUsize::new(0),
92 event_tracking: AtomicBool::new(true),
93 error_state: AtomicU32::new(0),
94 });
95 ctx.bind_to_thread()?;
96 Ok(ctx)
97 }
98
99 #[cfg(any(
109 feature = "cuda-11040",
110 feature = "cuda-11050",
111 feature = "cuda-11060",
112 feature = "cuda-11070",
113 feature = "cuda-11080",
114 feature = "cuda-12000",
115 feature = "cuda-12010",
116 feature = "cuda-12020",
117 feature = "cuda-12030",
118 feature = "cuda-12040",
119 feature = "cuda-12050",
120 feature = "cuda-12060",
121 feature = "cuda-12080",
122 feature = "cuda-12090",
123 feature = "cuda-13000",
124 feature = "cuda-13010"
125 ))]
126 pub fn new_non_primary(ordinal: usize, flags: u32) -> Result<Arc<Self>, DriverError> {
127 result::init()?;
128 let cu_device = result::device::get(ordinal as i32)?;
129
130 #[cfg(any(
131 feature = "cuda-12050",
132 feature = "cuda-12060",
133 feature = "cuda-12080",
134 feature = "cuda-12090",
135 feature = "cuda-13000",
136 feature = "cuda-13010"
137 ))]
138 let cu_ctx = unsafe { result::ctx::create_v4(std::ptr::null_mut(), flags, cu_device) }?;
139
140 #[cfg(not(any(
141 feature = "cuda-12050",
142 feature = "cuda-12060",
143 feature = "cuda-12080",
144 feature = "cuda-12090",
145 feature = "cuda-13000",
146 feature = "cuda-13010"
147 )))]
148 let cu_ctx = unsafe { result::ctx::create_v3(flags, cu_device) }?;
149
150 let has_async_alloc = unsafe {
151 let memory_pools_supported = result::device::get_attribute(
152 cu_device,
153 sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED,
154 )?;
155 memory_pools_supported > 0
156 };
157 let ctx = Arc::new(CudaContext {
158 cu_device,
159 cu_ctx,
160 ordinal,
161 has_async_alloc,
162 is_primary: false,
163 num_streams: AtomicUsize::new(0),
164 event_tracking: AtomicBool::new(true),
165 error_state: AtomicU32::new(0),
166 });
167 ctx.bind_to_thread()?;
168 Ok(ctx)
169 }
170
171 #[cfg(any(
181 feature = "cuda-12050",
182 feature = "cuda-12060",
183 feature = "cuda-12080",
184 feature = "cuda-12090",
185 feature = "cuda-13000",
186 feature = "cuda-13010"
187 ))]
188 pub fn new_cig(
189 ordinal: usize,
190 flags: u32,
191 cig_params: &mut sys::CUctxCigParam,
192 ) -> Result<Arc<Self>, DriverError> {
193 result::init()?;
194 let cu_device = result::device::get(ordinal as i32)?;
195 let mut ctx_create_params = sys::CUctxCreateParams_st {
196 execAffinityParams: std::ptr::null_mut(),
197 numExecAffinityParams: 0,
198 cigParams: cig_params as *mut sys::CUctxCigParam,
199 };
200 let cu_ctx = unsafe { result::ctx::create_v4(&mut ctx_create_params, flags, cu_device) }?;
201 let has_async_alloc = unsafe {
202 let memory_pools_supported = result::device::get_attribute(
203 cu_device,
204 sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED,
205 )?;
206 memory_pools_supported > 0
207 };
208 let ctx = Arc::new(CudaContext {
209 cu_device,
210 cu_ctx,
211 ordinal,
212 has_async_alloc,
213 is_primary: false,
214 num_streams: AtomicUsize::new(0),
215 event_tracking: AtomicBool::new(true),
216 error_state: AtomicU32::new(0),
217 });
218 ctx.bind_to_thread()?;
219 Ok(ctx)
220 }
221
222 pub unsafe fn from_raw_context(
234 ordinal: usize,
235 cu_device: sys::CUdevice,
236 cu_ctx: sys::CUcontext,
237 ) -> Result<Arc<Self>, DriverError> {
238 let has_async_alloc = {
239 let memory_pools_supported = result::device::get_attribute(
240 cu_device,
241 sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED,
242 )?;
243 memory_pools_supported > 0
244 };
245 let ctx = Arc::new(CudaContext {
246 cu_device,
247 cu_ctx,
248 ordinal,
249 has_async_alloc,
250 is_primary: false,
251 num_streams: AtomicUsize::new(0),
252 event_tracking: AtomicBool::new(true),
253 error_state: AtomicU32::new(0),
254 });
255 ctx.bind_to_thread()?;
256 Ok(ctx)
257 }
258
259 pub fn is_primary(&self) -> bool {
264 self.is_primary
265 }
266
267 pub fn has_async_alloc(&self) -> bool {
275 self.has_async_alloc
276 }
277
278 pub fn device_count() -> Result<i32, DriverError> {
280 result::init()?;
281 result::device::get_count()
282 }
283
284 pub fn ordinal(&self) -> usize {
286 self.ordinal
287 }
288
289 pub fn name(&self) -> Result<String, result::DriverError> {
291 self.check_err()?;
292 result::device::get_name(self.cu_device)
293 }
294
295 pub fn uuid(&self) -> Result<sys::CUuuid, result::DriverError> {
297 self.check_err()?;
298 result::device::get_uuid(self.cu_device)
299 }
300
301 pub fn compute_capability(&self) -> Result<(i32, i32), result::DriverError> {
303 self.check_err()?;
304 let capability_major =
305 self.attribute(sys::CUdevice_attribute::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR)?;
306 let capability_minor =
307 self.attribute(sys::CUdevice_attribute::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR)?;
308
309 Ok((capability_major, capability_minor))
310 }
311
312 pub fn total_mem(&self) -> Result<usize, DriverError> {
314 self.check_err()?;
315 unsafe { result::device::total_mem(self.cu_device) }
316 }
317
318 pub fn mem_get_info(&self) -> Result<(usize, usize), DriverError> {
322 self.bind_to_thread()?;
323 result::mem_get_info()
324 }
325 pub fn cu_device(&self) -> sys::CUdevice {
334 self.cu_device
335 }
336
337 pub fn cu_ctx(&self) -> sys::CUcontext {
346 self.cu_ctx
347 }
348
349 pub fn bind_to_thread(&self) -> Result<(), DriverError> {
351 self.check_err()?;
352 if match result::ctx::get_current()? {
353 Some(curr_ctx) => curr_ctx != self.cu_ctx,
354 None => true,
355 } {
356 unsafe { result::ctx::set_current(self.cu_ctx) }?;
357 }
358 Ok(())
359 }
360
361 pub fn attribute(&self, attrib: sys::CUdevice_attribute) -> Result<i32, result::DriverError> {
363 self.check_err()?;
364 unsafe { result::device::get_attribute(self.cu_device, attrib) }
365 }
366
367 pub fn synchronize(&self) -> Result<(), DriverError> {
370 self.bind_to_thread()?;
371 result::ctx::synchronize()
372 }
373
374 #[cfg(not(any(
378 feature = "cuda-11040",
379 feature = "cuda-11050",
380 feature = "cuda-11060",
381 feature = "cuda-11070",
382 feature = "cuda-11080",
383 feature = "cuda-12000"
384 )))]
385 pub fn set_blocking_synchronize(&self) -> Result<(), DriverError> {
386 self.set_flags(sys::CUctx_flags::CU_CTX_SCHED_BLOCKING_SYNC)
387 }
388
389 #[cfg(not(any(
391 feature = "cuda-11040",
392 feature = "cuda-11050",
393 feature = "cuda-11060",
394 feature = "cuda-11070",
395 feature = "cuda-11080",
396 feature = "cuda-12000"
397 )))]
398 pub fn set_flags(&self, flags: sys::CUctx_flags) -> Result<(), DriverError> {
399 self.bind_to_thread()?;
400 result::ctx::set_flags(flags)
401 }
402
403 pub fn get_limit(&self, limit: sys::CUlimit) -> Result<usize, DriverError> {
407 self.bind_to_thread()?;
408 result::ctx::get_limit(limit)
409 }
410
411 pub fn set_limit(&self, limit: sys::CUlimit, value: usize) -> Result<(), DriverError> {
420 self.bind_to_thread()?;
421 result::ctx::set_limit(limit, value)
422 }
423
424 pub fn get_cache_config(&self) -> Result<sys::CUfunc_cache, DriverError> {
428 self.bind_to_thread()?;
429 result::ctx::get_cache_config()
430 }
431
432 pub fn set_cache_config(&self, config: sys::CUfunc_cache) -> Result<(), DriverError> {
442 self.bind_to_thread()?;
443 result::ctx::set_cache_config(config)
444 }
445
446 pub fn is_in_multi_stream_mode(&self) -> bool {
451 self.num_streams.load(Ordering::Relaxed) > 0
452 }
453
454 pub fn is_event_tracking(&self) -> bool {
459 self.event_tracking.load(Ordering::Relaxed)
460 }
461
462 pub fn is_managing_stream_synchronization(&self) -> bool {
467 self.is_in_multi_stream_mode() && self.is_event_tracking()
468 }
469
470 pub unsafe fn enable_event_tracking(&self) {
480 self.event_tracking.store(true, Ordering::Relaxed);
481 }
482
483 pub unsafe fn disable_event_tracking(&self) {
494 self.event_tracking.store(false, Ordering::Relaxed);
495 }
496
497 pub fn check_err(&self) -> Result<(), DriverError> {
503 let error_state = self.error_state.swap(0, Ordering::Relaxed);
504 if error_state == 0 {
505 Ok(())
506 } else {
507 Err(result::DriverError(unsafe {
508 std::mem::transmute::<u32, sys::cudaError_enum>(error_state)
509 }))
510 }
511 }
512
513 pub fn record_err<T>(&self, result: Result<T, DriverError>) {
515 if let Err(err) = result {
516 self.error_state.store(err.0 as u32, Ordering::Relaxed)
517 }
518 }
519}
520
521#[derive(Debug)]
532pub struct CudaEvent {
533 pub(crate) cu_event: sys::CUevent,
534 pub(crate) ctx: Arc<CudaContext>,
535}
536
537unsafe impl Send for CudaEvent {}
538unsafe impl Sync for CudaEvent {}
539
540impl Drop for CudaEvent {
541 fn drop(&mut self) {
542 self.ctx.record_err(self.ctx.bind_to_thread());
543 self.ctx
544 .record_err(unsafe { result::event::destroy(self.cu_event) });
545 }
546}
547
548impl CudaContext {
549 pub fn new_event(
552 self: &Arc<Self>,
553 flags: Option<sys::CUevent_flags>,
554 ) -> Result<CudaEvent, DriverError> {
555 let flags = flags.unwrap_or(sys::CUevent_flags::CU_EVENT_DISABLE_TIMING);
556 self.bind_to_thread()?;
557 let cu_event = result::event::create(flags)?;
558 Ok(CudaEvent {
559 cu_event,
560 ctx: self.clone(),
561 })
562 }
563}
564
565impl CudaEvent {
566 pub fn cu_event(&self) -> sys::CUevent {
571 self.cu_event
572 }
573
574 pub fn context(&self) -> &Arc<CudaContext> {
576 &self.ctx
577 }
578
579 pub fn record(&self, stream: &CudaStream) -> Result<(), DriverError> {
588 if self.ctx != stream.ctx {
589 return Err(DriverError(sys::cudaError_enum::CUDA_ERROR_INVALID_CONTEXT));
590 }
591 self.ctx.bind_to_thread()?;
592 unsafe { result::event::record(self.cu_event, stream.cu_stream) }
593 }
594
595 pub fn synchronize(&self) -> Result<(), DriverError> {
597 self.ctx.bind_to_thread()?;
598 unsafe { result::event::synchronize(self.cu_event) }
599 }
600
601 pub fn elapsed_ms(&self, end: &Self) -> Result<f32, DriverError> {
604 if self.ctx != end.ctx {
605 return Err(DriverError(sys::cudaError_enum::CUDA_ERROR_INVALID_CONTEXT));
606 }
607 self.ctx.bind_to_thread()?;
608 self.synchronize()?;
609 end.synchronize()?;
610 unsafe { result::event::elapsed(self.cu_event, end.cu_event) }
611 }
612
613 pub fn is_complete(&self) -> bool {
615 unsafe { result::event::query(self.cu_event) }.is_ok()
616 }
617}
618
619#[derive(Debug, PartialEq, Eq)]
631pub struct CudaStream {
632 pub(crate) cu_stream: sys::CUstream,
633 pub(crate) ctx: Arc<CudaContext>,
634}
635
636unsafe impl Send for CudaStream {}
637unsafe impl Sync for CudaStream {}
638
639impl Drop for CudaStream {
640 fn drop(&mut self) {
641 self.ctx.record_err(self.ctx.bind_to_thread());
642 let cu_stream = std::mem::replace(&mut self.cu_stream, std::ptr::null_mut());
643 if !cu_stream.is_null() && cu_stream != (0x2 as _) {
644 self.ctx.num_streams.fetch_sub(1, Ordering::Relaxed);
645 self.ctx
646 .record_err(unsafe { result::stream::destroy(cu_stream) });
647 }
648 }
649}
650
651impl CudaContext {
652 pub fn default_stream(self: &Arc<Self>) -> Arc<CudaStream> {
655 Arc::new(CudaStream {
656 cu_stream: std::ptr::null_mut(),
657 ctx: self.clone(),
658 })
659 }
660
661 pub fn per_thread_stream(self: &Arc<Self>) -> Arc<CudaStream> {
663 Arc::new(CudaStream {
664 cu_stream: 0x2 as _,
666 ctx: self.clone(),
667 })
668 }
669
670 pub fn new_stream(self: &Arc<Self>) -> Result<Arc<CudaStream>, DriverError> {
675 self.bind_to_thread()?;
676 let prev_num_streams = self.num_streams.fetch_add(1, Ordering::Relaxed);
677 if prev_num_streams == 0 && self.is_event_tracking() {
678 self.synchronize()?;
679 }
680 let cu_stream = result::stream::create(result::stream::StreamKind::NonBlocking)?;
681 Ok(Arc::new(CudaStream {
682 cu_stream,
683 ctx: self.clone(),
684 }))
685 }
686
687 pub fn new_stream_with_priority(
696 self: &Arc<Self>,
697 priority: i32,
698 ) -> Result<Arc<CudaStream>, DriverError> {
699 self.bind_to_thread()?;
700 let prev_num_streams = self.num_streams.fetch_add(1, Ordering::Relaxed);
701 if prev_num_streams == 0 && self.is_event_tracking() {
702 self.synchronize()?;
703 }
704 let cu_stream = result::stream::create_with_priority(
705 result::stream::StreamKind::NonBlocking,
706 priority,
707 )?;
708 Ok(Arc::new(CudaStream {
709 cu_stream,
710 ctx: self.clone(),
711 }))
712 }
713}
714
715impl CudaStream {
716 pub fn fork(&self) -> Result<Arc<Self>, DriverError> {
718 self.ctx.bind_to_thread()?;
719 self.ctx.num_streams.fetch_add(1, Ordering::Relaxed);
720 let cu_stream = result::stream::create(result::stream::StreamKind::NonBlocking)?;
721 let stream = Arc::new(CudaStream {
722 cu_stream,
723 ctx: self.ctx.clone(),
724 });
725 stream.join(self)?;
726 Ok(stream)
727 }
728
729 pub fn cu_stream(&self) -> sys::CUstream {
733 self.cu_stream
734 }
735
736 pub fn context(&self) -> &Arc<CudaContext> {
738 &self.ctx
739 }
740
741 pub fn synchronize(&self) -> Result<(), DriverError> {
746 self.ctx.bind_to_thread()?;
747 unsafe { result::stream::synchronize(self.cu_stream) }
748 }
749
750 pub fn record_event(
752 &self,
753 flags: Option<sys::CUevent_flags>,
754 ) -> Result<CudaEvent, DriverError> {
755 let event = self.ctx.new_event(flags)?;
756 event.record(self)?;
757 Ok(event)
758 }
759
760 pub fn wait(&self, event: &CudaEvent) -> Result<(), DriverError> {
767 self.ctx.bind_to_thread()?;
768 unsafe {
769 result::stream::wait_event(
770 self.cu_stream,
771 event.cu_event,
772 sys::CUevent_wait_flags::CU_EVENT_WAIT_DEFAULT,
773 )
774 }
775 }
776
777 pub fn join(&self, other: &CudaStream) -> Result<(), DriverError> {
780 self.wait(&other.record_event(None)?)
781 }
782}
783
784#[derive(Debug)]
788pub struct CudaSlice<T> {
789 pub(crate) cu_device_ptr: sys::CUdeviceptr,
790 pub(crate) len: usize,
791 pub(crate) read: Option<CudaEvent>,
792 pub(crate) write: Option<CudaEvent>,
793 pub(crate) stream: Arc<CudaStream>,
794 pub(crate) marker: PhantomData<*const T>,
795}
796
797unsafe impl<T> Send for CudaSlice<T> {}
798unsafe impl<T> Sync for CudaSlice<T> {}
799
800impl<T> Drop for CudaSlice<T> {
801 fn drop(&mut self) {
802 let ctx = &self.stream.ctx;
803 if let Some(read) = self.read.as_ref() {
804 ctx.record_err(self.stream.wait(read));
805 }
806 if let Some(write) = self.write.as_ref() {
807 ctx.record_err(self.stream.wait(write));
808 }
809 if ctx.has_async_alloc {
810 ctx.record_err(unsafe {
811 result::free_async(self.cu_device_ptr, self.stream.cu_stream)
812 });
813 } else {
814 ctx.record_err(self.stream.synchronize());
815 ctx.record_err(unsafe { result::free_sync(self.cu_device_ptr) });
816 }
817 }
818}
819
820impl<T> CudaSlice<T> {
821 pub fn len(&self) -> usize {
823 self.len
824 }
825
826 pub fn num_bytes(&self) -> usize {
828 self.len * std::mem::size_of::<T>()
829 }
830
831 pub fn is_empty(&self) -> bool {
833 self.len == 0
834 }
835
836 pub fn ordinal(&self) -> usize {
838 self.stream.ctx.ordinal
839 }
840
841 pub fn context(&self) -> &Arc<CudaContext> {
843 &self.stream.ctx
844 }
845
846 pub fn stream(&self) -> &Arc<CudaStream> {
848 &self.stream
849 }
850}
851
852impl<T: DeviceRepr> CudaSlice<T> {
853 pub fn try_clone(&self) -> Result<Self, result::DriverError> {
855 self.stream.clone_dtod(self)
856 }
857}
858
859impl<T: DeviceRepr> Clone for CudaSlice<T> {
860 fn clone(&self) -> Self {
861 self.try_clone().unwrap()
862 }
863}
864
865impl<T: Clone + Default + DeviceRepr> TryFrom<CudaSlice<T>> for Vec<T> {
866 type Error = result::DriverError;
867 fn try_from(value: CudaSlice<T>) -> Result<Self, Self::Error> {
868 value.stream.clone_dtoh(&value)
869 }
870}
871
872#[derive(Debug)]
874pub struct CudaView<'a, T> {
875 pub(crate) ptr: sys::CUdeviceptr,
876 pub(crate) len: usize,
877 pub(crate) read: &'a Option<CudaEvent>,
878 pub(crate) write: &'a Option<CudaEvent>,
879 pub(crate) stream: &'a Arc<CudaStream>,
880 marker: PhantomData<&'a [T]>,
881}
882
883impl<T> CudaSlice<T> {
884 pub fn as_view(&self) -> CudaView<'_, T> {
885 CudaView {
886 ptr: self.cu_device_ptr,
887 len: self.len,
888 read: &self.read,
889 write: &self.write,
890 stream: &self.stream,
891 marker: PhantomData,
892 }
893 }
894}
895
896impl<T> CudaView<'_, T> {
897 pub fn len(&self) -> usize {
899 self.len
900 }
901
902 pub fn is_empty(&self) -> bool {
903 self.len == 0
904 }
905
906 fn resize(&self, start: usize, end: usize) -> Self {
907 assert!(start <= end && end <= self.len);
908 Self {
909 ptr: self.ptr + (start * std::mem::size_of::<T>()) as u64,
910 len: end - start,
911 read: self.read,
912 write: self.write,
913 stream: self.stream,
914 marker: PhantomData,
915 }
916 }
917}
918
919#[derive(Debug)]
921pub struct CudaViewMut<'a, T> {
922 pub(crate) ptr: sys::CUdeviceptr,
923 pub(crate) len: usize,
924 pub(crate) read: &'a Option<CudaEvent>,
925 pub(crate) write: &'a Option<CudaEvent>,
926 pub(crate) stream: &'a Arc<CudaStream>,
927 marker: PhantomData<&'a mut [T]>,
928}
929
930impl<T> CudaSlice<T> {
931 pub fn as_view_mut(&mut self) -> CudaViewMut<'_, T> {
932 CudaViewMut {
933 ptr: self.cu_device_ptr,
934 len: self.len,
935 read: &self.read,
936 write: &self.write,
937 stream: &self.stream,
938 marker: PhantomData,
939 }
940 }
941}
942
943impl<T> CudaViewMut<'_, T> {
944 pub fn len(&self) -> usize {
946 self.len
947 }
948 pub fn is_empty(&self) -> bool {
949 self.len == 0
950 }
951
952 pub fn as_view<'b>(&'b self) -> CudaView<'b, T> {
954 CudaView {
955 ptr: self.ptr,
956 len: self.len,
957 read: self.read,
958 write: self.write,
959 stream: self.stream,
960 marker: PhantomData,
961 }
962 }
963}
964
965pub unsafe trait ValidAsZeroBits {}
972unsafe impl ValidAsZeroBits for bool {}
973unsafe impl ValidAsZeroBits for i8 {}
974unsafe impl ValidAsZeroBits for i16 {}
975unsafe impl ValidAsZeroBits for i32 {}
976unsafe impl ValidAsZeroBits for i64 {}
977unsafe impl ValidAsZeroBits for i128 {}
978unsafe impl ValidAsZeroBits for isize {}
979unsafe impl ValidAsZeroBits for u8 {}
980unsafe impl ValidAsZeroBits for u16 {}
981unsafe impl ValidAsZeroBits for u32 {}
982unsafe impl ValidAsZeroBits for u64 {}
983unsafe impl ValidAsZeroBits for u128 {}
984unsafe impl ValidAsZeroBits for usize {}
985unsafe impl ValidAsZeroBits for f32 {}
986unsafe impl ValidAsZeroBits for f64 {}
987#[cfg(feature = "f16")]
988unsafe impl ValidAsZeroBits for half::f16 {}
989#[cfg(feature = "f16")]
990unsafe impl ValidAsZeroBits for half::bf16 {}
991unsafe impl<T: ValidAsZeroBits, const M: usize> ValidAsZeroBits for [T; M] {}
992macro_rules! impl_tuples {
997 ($t:tt) => {
998 impl_tuples!(@ $t);
999 };
1000 ($l:tt $(,$t:tt)+) => {
1002 impl_tuples!($($t),+);
1003 impl_tuples!(@ $l $(,$t)+);
1004 };
1005 (@ $($t:tt),+) => {
1006 unsafe impl<$($t: ValidAsZeroBits,)+> ValidAsZeroBits for ($($t,)+) {}
1007 };
1008}
1009impl_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);
1010
1011pub unsafe trait DeviceRepr {}
1020unsafe impl DeviceRepr for bool {}
1021unsafe impl DeviceRepr for i8 {}
1022unsafe impl DeviceRepr for i16 {}
1023unsafe impl DeviceRepr for i32 {}
1024unsafe impl DeviceRepr for i64 {}
1025unsafe impl DeviceRepr for i128 {}
1026unsafe impl DeviceRepr for isize {}
1027unsafe impl DeviceRepr for u8 {}
1028unsafe impl DeviceRepr for u16 {}
1029unsafe impl DeviceRepr for u32 {}
1030unsafe impl DeviceRepr for u64 {}
1031unsafe impl DeviceRepr for u128 {}
1032unsafe impl DeviceRepr for usize {}
1033unsafe impl DeviceRepr for f32 {}
1034unsafe impl DeviceRepr for f64 {}
1035#[cfg(feature = "f16")]
1036unsafe impl DeviceRepr for half::f16 {}
1037#[cfg(feature = "f16")]
1038unsafe impl DeviceRepr for half::bf16 {}
1039
1040#[cfg(feature = "f8")]
1041unsafe impl DeviceRepr for float8::F8E4M3 {}
1042#[cfg(feature = "f8")]
1043unsafe impl ValidAsZeroBits for float8::F8E4M3 {}
1044
1045#[cfg(feature = "f8")]
1046unsafe impl DeviceRepr for float8::F8E5M2 {}
1047#[cfg(feature = "f8")]
1048unsafe impl ValidAsZeroBits for float8::F8E5M2 {}
1049
1050#[cfg(feature = "f4")]
1051unsafe impl DeviceRepr for float4::F4E2M1 {}
1052#[cfg(feature = "f4")]
1053unsafe impl ValidAsZeroBits for float4::F4E2M1 {}
1054
1055#[cfg(feature = "f4")]
1056unsafe impl DeviceRepr for float4::E8M0 {}
1057#[cfg(feature = "f4")]
1058unsafe impl ValidAsZeroBits for float4::E8M0 {}
1059
1060#[cfg(feature = "f4")]
1061unsafe impl DeviceRepr for float4::F4E2M1x2 {}
1062#[cfg(feature = "f4")]
1063unsafe impl ValidAsZeroBits for float4::F4E2M1x2 {}
1064
1065unsafe impl<const N: usize, T> DeviceRepr for [T; N] where T: DeviceRepr {}
1066
1067pub trait DeviceSlice<T> {
1071 fn len(&self) -> usize;
1072 fn num_bytes(&self) -> usize {
1073 self.len() * std::mem::size_of::<T>()
1074 }
1075 fn is_empty(&self) -> bool {
1076 self.len() == 0
1077 }
1078 fn stream(&self) -> &Arc<CudaStream>;
1079}
1080
1081impl<T> DeviceSlice<T> for CudaSlice<T> {
1082 fn len(&self) -> usize {
1083 self.len
1084 }
1085 fn stream(&self) -> &Arc<CudaStream> {
1086 &self.stream
1087 }
1088}
1089
1090impl<T> DeviceSlice<T> for CudaView<'_, T> {
1091 fn len(&self) -> usize {
1092 self.len
1093 }
1094 fn stream(&self) -> &Arc<CudaStream> {
1095 self.stream
1096 }
1097}
1098
1099impl<T> DeviceSlice<T> for CudaViewMut<'_, T> {
1100 fn len(&self) -> usize {
1101 self.len
1102 }
1103 fn stream(&self) -> &Arc<CudaStream> {
1104 self.stream
1105 }
1106}
1107
1108#[derive(Debug)]
1111#[must_use]
1112pub enum SyncOnDrop<'a> {
1113 Record(Option<(&'a CudaEvent, &'a CudaStream)>),
1115 Sync(Option<&'a CudaStream>),
1117}
1118
1119impl<'a> SyncOnDrop<'a> {
1120 pub fn record_event(event: &'a Option<CudaEvent>, stream: &'a CudaStream) -> Self {
1122 SyncOnDrop::Record(event.as_ref().map(|e| (e, stream)))
1123 }
1124 pub fn sync_stream(stream: &'a CudaStream) -> Self {
1126 SyncOnDrop::Sync(Some(stream))
1127 }
1128}
1129
1130impl Drop for SyncOnDrop<'_> {
1131 fn drop(&mut self) {
1132 match self {
1133 SyncOnDrop::Record(target) => {
1134 if let Some((event, stream)) = std::mem::take(target) {
1135 stream.ctx.record_err(event.record(stream));
1136 }
1137 }
1138 SyncOnDrop::Sync(target) => {
1139 if let Some(stream) = std::mem::take(target) {
1140 stream.ctx.record_err(stream.synchronize());
1141 }
1142 }
1143 }
1144 }
1145}
1146
1147pub trait DevicePtr<T>: DeviceSlice<T> {
1149 fn device_ptr<'a>(&'a self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>);
1164}
1165
1166impl<T> DevicePtr<T> for CudaSlice<T> {
1167 fn device_ptr<'a>(&'a self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
1168 if self.stream.context().is_managing_stream_synchronization() {
1169 if let Some(write) = self.write.as_ref() {
1170 stream.ctx.record_err(stream.wait(write));
1171 }
1172 }
1173 (
1174 self.cu_device_ptr,
1175 SyncOnDrop::record_event(&self.read, stream),
1176 )
1177 }
1178}
1179
1180impl<T> DevicePtr<T> for CudaView<'_, T> {
1181 fn device_ptr<'a>(&'a self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
1182 if self.stream.context().is_managing_stream_synchronization() {
1183 if let Some(write) = self.write.as_ref() {
1184 stream.ctx.record_err(stream.wait(write));
1185 }
1186 }
1187 (self.ptr, SyncOnDrop::record_event(self.read, stream))
1188 }
1189}
1190
1191impl<'a, T> CudaView<'a, T> {
1192 pub fn view_ptr(self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
1195 if self.stream.context().is_managing_stream_synchronization() {
1196 if let Some(write) = self.write.as_ref() {
1197 stream.ctx.record_err(stream.wait(write));
1198 }
1199 }
1200 (self.ptr, SyncOnDrop::record_event(self.read, stream))
1201 }
1202}
1203
1204impl<T> DevicePtr<T> for CudaViewMut<'_, T> {
1205 fn device_ptr<'a>(&'a self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
1206 if self.stream.context().is_managing_stream_synchronization() {
1207 if let Some(write) = self.write.as_ref() {
1208 stream.ctx.record_err(stream.wait(write));
1209 }
1210 }
1211 (self.ptr, SyncOnDrop::record_event(self.read, stream))
1212 }
1213}
1214
1215impl<'a, T> CudaViewMut<'a, T> {
1216 pub fn view_ptr(self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
1219 if self.stream.context().is_managing_stream_synchronization() {
1220 if let Some(write) = self.write.as_ref() {
1221 stream.ctx.record_err(stream.wait(write));
1222 }
1223 }
1224 (self.ptr, SyncOnDrop::record_event(self.read, stream))
1225 }
1226}
1227
1228pub trait DevicePtrMut<T>: DeviceSlice<T> {
1230 fn device_ptr_mut<'a>(
1245 &'a mut self,
1246 stream: &'a CudaStream,
1247 ) -> (sys::CUdeviceptr, SyncOnDrop<'a>);
1248}
1249
1250impl<T> DevicePtrMut<T> for CudaSlice<T> {
1251 fn device_ptr_mut<'a>(
1252 &'a mut self,
1253 stream: &'a CudaStream,
1254 ) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
1255 if self.stream.context().is_managing_stream_synchronization() {
1256 if let Some(read) = self.read.as_ref() {
1257 stream.ctx.record_err(stream.wait(read));
1258 }
1259 if let Some(write) = self.write.as_ref() {
1260 stream.ctx.record_err(stream.wait(write));
1261 }
1262 }
1263 (
1264 self.cu_device_ptr,
1265 SyncOnDrop::record_event(&self.write, stream),
1266 )
1267 }
1268}
1269
1270impl<T> DevicePtrMut<T> for CudaViewMut<'_, T> {
1271 fn device_ptr_mut<'a>(
1272 &'a mut self,
1273 stream: &'a CudaStream,
1274 ) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
1275 if self.stream.context().is_managing_stream_synchronization() {
1276 if let Some(read) = self.read.as_ref() {
1277 stream.ctx.record_err(stream.wait(read));
1278 }
1279 if let Some(write) = self.write.as_ref() {
1280 stream.ctx.record_err(stream.wait(write));
1281 }
1282 }
1283 (self.ptr, SyncOnDrop::record_event(self.write, stream))
1284 }
1285}
1286
1287impl<'a, T> CudaViewMut<'a, T> {
1288 pub fn view_ptr_mut(self, stream: &'a CudaStream) -> (sys::CUdeviceptr, SyncOnDrop<'a>) {
1291 if self.stream.context().is_managing_stream_synchronization() {
1292 if let Some(read) = self.read.as_ref() {
1293 stream.ctx.record_err(stream.wait(read));
1294 }
1295 if let Some(write) = self.write.as_ref() {
1296 stream.ctx.record_err(stream.wait(write));
1297 }
1298 }
1299 (self.ptr, SyncOnDrop::record_event(self.write, stream))
1300 }
1301}
1302
1303pub trait HostSlice<T> {
1305 fn len(&self) -> usize;
1306 fn is_empty(&self) -> bool {
1307 self.len() == 0
1308 }
1309
1310 unsafe fn stream_synced_slice<'a>(
1314 &'a self,
1315 stream: &'a CudaStream,
1316 ) -> (&'a [T], SyncOnDrop<'a>);
1317
1318 unsafe fn stream_synced_mut_slice<'a>(
1322 &'a mut self,
1323 stream: &'a CudaStream,
1324 ) -> (&'a mut [T], SyncOnDrop<'a>);
1325}
1326
1327impl<T, const N: usize> HostSlice<T> for [T; N] {
1328 fn len(&self) -> usize {
1329 N
1330 }
1331 unsafe fn stream_synced_slice<'a>(
1332 &'a self,
1333 _stream: &'a CudaStream,
1334 ) -> (&'a [T], SyncOnDrop<'a>) {
1335 (self, SyncOnDrop::Sync(None))
1336 }
1337 unsafe fn stream_synced_mut_slice<'a>(
1338 &'a mut self,
1339 _stream: &'a CudaStream,
1340 ) -> (&'a mut [T], SyncOnDrop<'a>) {
1341 (self, SyncOnDrop::Sync(None))
1342 }
1343}
1344
1345impl<T> HostSlice<T> for [T] {
1346 fn len(&self) -> usize {
1347 self.len()
1348 }
1349 unsafe fn stream_synced_slice<'a>(
1350 &'a self,
1351 _stream: &'a CudaStream,
1352 ) -> (&'a [T], SyncOnDrop<'a>) {
1353 (self, SyncOnDrop::Sync(None))
1354 }
1355 unsafe fn stream_synced_mut_slice<'a>(
1356 &'a mut self,
1357 _stream: &'a CudaStream,
1358 ) -> (&'a mut [T], SyncOnDrop<'a>) {
1359 (self, SyncOnDrop::Sync(None))
1360 }
1361}
1362
1363impl<T> HostSlice<T> for Vec<T> {
1364 fn len(&self) -> usize {
1365 self.len()
1366 }
1367 unsafe fn stream_synced_slice<'a>(
1368 &'a self,
1369 _stream: &'a CudaStream,
1370 ) -> (&'a [T], SyncOnDrop<'a>) {
1371 (self, SyncOnDrop::Sync(None))
1372 }
1373 unsafe fn stream_synced_mut_slice<'a>(
1374 &'a mut self,
1375 _stream: &'a CudaStream,
1376 ) -> (&'a mut [T], SyncOnDrop<'a>) {
1377 (self, SyncOnDrop::Sync(None))
1378 }
1379}
1380
1381#[derive(Debug)]
1388pub struct PinnedHostSlice<T> {
1389 pub(crate) ptr: *mut T,
1390 pub(crate) len: usize,
1391 pub(crate) event: CudaEvent,
1392}
1393
1394unsafe impl<T> Send for PinnedHostSlice<T> {}
1395unsafe impl<T> Sync for PinnedHostSlice<T> {}
1396
1397impl<T> Drop for PinnedHostSlice<T> {
1398 fn drop(&mut self) {
1399 let ctx = &self.event.ctx;
1400 ctx.record_err(self.event.synchronize());
1401 ctx.record_err(unsafe { result::free_host(self.ptr as _) });
1402 }
1403}
1404
1405impl CudaContext {
1406 pub unsafe fn alloc_pinned<T: DeviceRepr>(
1413 self: &Arc<Self>,
1414 len: usize,
1415 ) -> Result<PinnedHostSlice<T>, DriverError> {
1416 self.bind_to_thread()?;
1417 let ptr = result::malloc_host(
1418 len * std::mem::size_of::<T>(),
1419 sys::CU_MEMHOSTALLOC_WRITECOMBINED,
1420 )?;
1421 let ptr = ptr as *mut T;
1422 assert!(!ptr.is_null());
1423 assert!(len * std::mem::size_of::<T>() < isize::MAX as usize);
1424 assert!(ptr.is_aligned());
1425 let event = self.new_event(Some(sys::CUevent_flags::CU_EVENT_BLOCKING_SYNC))?;
1426 Ok(PinnedHostSlice { ptr, len, event })
1427 }
1428}
1429
1430impl<T> PinnedHostSlice<T> {
1431 pub fn context(&self) -> &Arc<CudaContext> {
1433 &self.event.ctx
1434 }
1435
1436 pub fn len(&self) -> usize {
1438 self.len
1439 }
1440
1441 pub fn num_bytes(&self) -> usize {
1443 self.len * std::mem::size_of::<T>()
1444 }
1445
1446 pub fn is_empty(&self) -> bool {
1447 self.len() == 0
1448 }
1449}
1450
1451impl<T: ValidAsZeroBits> PinnedHostSlice<T> {
1452 pub fn as_ptr(&self) -> Result<*const T, DriverError> {
1455 self.event.synchronize()?;
1456 Ok(self.ptr)
1457 }
1458
1459 pub fn as_mut_ptr(&mut self) -> Result<*mut T, DriverError> {
1462 self.event.synchronize()?;
1463 Ok(self.ptr)
1464 }
1465
1466 pub fn as_slice(&self) -> Result<&[T], DriverError> {
1469 self.event.synchronize()?;
1470 Ok(unsafe { std::slice::from_raw_parts(self.ptr, self.len) })
1471 }
1472
1473 pub fn as_mut_slice(&mut self) -> Result<&mut [T], DriverError> {
1476 self.event.synchronize()?;
1477 Ok(unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) })
1478 }
1479}
1480
1481impl<T> HostSlice<T> for PinnedHostSlice<T> {
1482 fn len(&self) -> usize {
1483 self.len
1484 }
1485
1486 unsafe fn stream_synced_slice<'a>(
1487 &'a self,
1488 stream: &'a CudaStream,
1489 ) -> (&'a [T], SyncOnDrop<'a>) {
1490 stream.ctx.record_err(stream.wait(&self.event));
1491 (
1492 std::slice::from_raw_parts(self.ptr, self.len),
1493 SyncOnDrop::Record(Some((&self.event, stream))),
1494 )
1495 }
1496 unsafe fn stream_synced_mut_slice<'a>(
1497 &'a mut self,
1498 stream: &'a CudaStream,
1499 ) -> (&'a mut [T], SyncOnDrop<'a>) {
1500 stream.ctx.record_err(stream.wait(&self.event));
1501 (
1502 std::slice::from_raw_parts_mut(self.ptr, self.len),
1503 SyncOnDrop::Record(Some((&self.event, stream))),
1504 )
1505 }
1506}
1507
1508impl CudaStream {
1509 pub fn null<T>(self: &Arc<Self>) -> Result<CudaSlice<T>, result::DriverError> {
1511 self.ctx.bind_to_thread()?;
1512 let cu_device_ptr = if self.ctx.has_async_alloc {
1513 unsafe { result::malloc_async(self.cu_stream, 0) }?
1514 } else {
1515 unsafe { result::malloc_sync(0) }?
1516 };
1517 Ok(CudaSlice {
1518 cu_device_ptr,
1519 len: 0,
1520 read: None,
1521 write: None,
1522 stream: self.clone(),
1523 marker: PhantomData,
1524 })
1525 }
1526
1527 pub unsafe fn alloc<T: DeviceRepr>(
1531 self: &Arc<Self>,
1532 len: usize,
1533 ) -> Result<CudaSlice<T>, DriverError> {
1534 self.ctx.bind_to_thread()?;
1535 let cu_device_ptr = if self.ctx.has_async_alloc {
1536 result::malloc_async(self.cu_stream, len * std::mem::size_of::<T>())?
1537 } else {
1538 result::malloc_sync(len * std::mem::size_of::<T>())?
1539 };
1540 let (read, write) = if self.ctx.is_event_tracking() {
1541 (
1542 Some(self.ctx.new_event(None)?),
1543 Some(self.ctx.new_event(None)?),
1544 )
1545 } else {
1546 (None, None)
1547 };
1548 Ok(CudaSlice {
1549 cu_device_ptr,
1550 len,
1551 read,
1552 write,
1553 stream: self.clone(),
1554 marker: PhantomData,
1555 })
1556 }
1557
1558 pub fn alloc_zeros<T: DeviceRepr + ValidAsZeroBits>(
1560 self: &Arc<Self>,
1561 len: usize,
1562 ) -> Result<CudaSlice<T>, DriverError> {
1563 let mut dst = unsafe { self.alloc(len) }?;
1564 self.memset_zeros(&mut dst)?;
1565 Ok(dst)
1566 }
1567
1568 pub fn memset_zeros<T: DeviceRepr + ValidAsZeroBits, Dst: DevicePtrMut<T>>(
1570 self: &Arc<Self>,
1571 dst: &mut Dst,
1572 ) -> Result<(), DriverError> {
1573 self.ctx.bind_to_thread()?;
1574 let num_bytes = dst.num_bytes();
1575 let (dptr, _record) = dst.device_ptr_mut(self);
1576 unsafe { result::memset_d8_async(dptr, 0, num_bytes, self.cu_stream) }?;
1577 Ok(())
1578 }
1579
1580 #[deprecated = "Use clone_htod"]
1582 pub fn memcpy_stod<T: DeviceRepr, Src: HostSlice<T> + ?Sized>(
1583 self: &Arc<Self>,
1584 src: &Src,
1585 ) -> Result<CudaSlice<T>, DriverError> {
1586 let mut dst = unsafe { self.alloc(src.len()) }?;
1587 self.memcpy_htod(src, &mut dst)?;
1588 Ok(dst)
1589 }
1590
1591 pub fn clone_htod<T: DeviceRepr, Src: HostSlice<T> + ?Sized>(
1593 self: &Arc<Self>,
1594 src: &Src,
1595 ) -> Result<CudaSlice<T>, DriverError> {
1596 let mut dst = unsafe { self.alloc(src.len()) }?;
1597 self.memcpy_htod(src, &mut dst)?;
1598 Ok(dst)
1599 }
1600
1601 pub fn memcpy_htod<T: DeviceRepr, Src: HostSlice<T> + ?Sized, Dst: DevicePtrMut<T>>(
1603 self: &Arc<Self>,
1604 src: &Src,
1605 dst: &mut Dst,
1606 ) -> Result<(), DriverError> {
1607 assert!(dst.len() >= src.len());
1608 self.ctx.bind_to_thread()?;
1609 let (src, _record_src) = unsafe { src.stream_synced_slice(self) };
1610 let (dst, _record_dst) = dst.device_ptr_mut(self);
1611 unsafe { result::memcpy_htod_async(dst, src, self.cu_stream) }
1612 }
1613
1614 #[deprecated = "Use clone_dtoh"]
1616 pub fn memcpy_dtov<T: DeviceRepr, Src: DevicePtr<T>>(
1617 self: &Arc<Self>,
1618 src: &Src,
1619 ) -> Result<Vec<T>, DriverError> {
1620 let mut dst = Vec::with_capacity(src.len());
1621 #[allow(clippy::uninit_vec)]
1622 unsafe {
1623 dst.set_len(src.len())
1624 };
1625 self.memcpy_dtoh(src, &mut dst)?;
1626 Ok(dst)
1627 }
1628
1629 pub fn clone_dtoh<T: DeviceRepr, Src: DevicePtr<T>>(
1631 self: &Arc<Self>,
1632 src: &Src,
1633 ) -> Result<Vec<T>, DriverError> {
1634 let mut dst = Vec::with_capacity(src.len());
1635 #[allow(clippy::uninit_vec)]
1636 unsafe {
1637 dst.set_len(src.len())
1638 };
1639 self.memcpy_dtoh(src, &mut dst)?;
1640 Ok(dst)
1641 }
1642
1643 pub fn memcpy_dtoh<T: DeviceRepr, Src: DevicePtr<T>, Dst: HostSlice<T> + ?Sized>(
1645 self: &Arc<Self>,
1646 src: &Src,
1647 dst: &mut Dst,
1648 ) -> Result<(), DriverError> {
1649 assert!(dst.len() >= src.len());
1650 self.ctx.bind_to_thread()?;
1651 let (src, _record_src) = src.device_ptr(self);
1652 let (dst, _record_dst) = unsafe { dst.stream_synced_mut_slice(self) };
1653 unsafe { result::memcpy_dtoh_async(dst, src, self.cu_stream) }
1654 }
1655
1656 pub fn memcpy_dtod<T, Src: DevicePtr<T>, Dst: DevicePtrMut<T>>(
1658 self: &Arc<Self>,
1659 src: &Src,
1660 dst: &mut Dst,
1661 ) -> Result<(), DriverError> {
1662 assert!(dst.len() >= src.len());
1663 self.ctx.bind_to_thread()?;
1664
1665 let num_bytes = src.num_bytes();
1666
1667 let src_ctx = src.stream().context();
1668 let dst_ctx = self.context();
1669
1670 if src_ctx == dst_ctx {
1671 let (src_ptr, _record_src) = src.device_ptr(self);
1672 let (dst_ptr, _record_dst) = dst.device_ptr_mut(self);
1673 unsafe { result::memcpy_dtod_async(dst_ptr, src_ptr, num_bytes, self.cu_stream) }
1674 } else {
1675 let (src_ptr, _record_src) = src.device_ptr(src.stream());
1681 let (dst_ptr, _record_dst) = dst.device_ptr_mut(self);
1682 self.wait(&src.stream().record_event(None)?)?;
1688 unsafe {
1689 result::memcpy_peer_async(
1690 dst_ctx.cu_ctx,
1691 dst_ptr,
1692 src_ctx.cu_ctx,
1693 src_ptr,
1694 num_bytes,
1695 self.cu_stream,
1696 )
1697 }
1698 }
1699 }
1700
1701 pub fn clone_dtod<T: DeviceRepr, Src: DevicePtr<T>>(
1703 self: &Arc<Self>,
1704 src: &Src,
1705 ) -> Result<CudaSlice<T>, DriverError> {
1706 let mut dst = unsafe { self.alloc(src.len()) }?;
1707 self.memcpy_dtod(src, &mut dst)?;
1708 Ok(dst)
1709 }
1710}
1711
1712impl<T> CudaSlice<T> {
1713 pub fn slice(&self, bounds: impl RangeBounds<usize>) -> CudaView<'_, T> {
1743 self.as_view().slice(bounds)
1744 }
1745
1746 pub fn try_slice(&self, bounds: impl RangeBounds<usize>) -> Option<CudaView<'_, T>> {
1748 self.as_view().try_slice(bounds)
1749 }
1750
1751 pub fn slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> CudaViewMut<'_, T> {
1795 self.try_slice_mut(bounds).unwrap()
1796 }
1797
1798 pub fn try_slice_mut(&mut self, bounds: impl RangeBounds<usize>) -> Option<CudaViewMut<'_, T>> {
1800 to_range(bounds, self.len).map(|(start, end)| CudaViewMut {
1801 ptr: self.cu_device_ptr + (start * std::mem::size_of::<T>()) as u64,
1802 len: end - start,
1803 read: &self.read,
1804 write: &self.write,
1805 stream: &self.stream,
1806 marker: PhantomData,
1807 })
1808 }
1809
1810 pub unsafe fn transmute<S>(&self, len: usize) -> Option<CudaView<'_, S>> {
1818 self.as_view().transmute(len)
1819 }
1820
1821 pub unsafe fn transmute_mut<S>(&mut self, len: usize) -> Option<CudaViewMut<'_, S>> {
1829 (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
1830 CudaViewMut {
1831 ptr: self.cu_device_ptr,
1832 len,
1833 read: &self.read,
1834 write: &self.write,
1835 stream: &self.stream,
1836 marker: PhantomData,
1837 },
1838 )
1839 }
1840
1841 pub fn split_at(&self, mid: usize) -> (CudaView<'_, T>, CudaView<'_, T>) {
1842 self.as_view().split_at(mid)
1843 }
1844
1845 pub fn try_split_at(&self, mid: usize) -> Option<(CudaView<'_, T>, CudaView<'_, T>)> {
1847 self.as_view().try_split_at(mid)
1848 }
1849
1850 pub fn split_at_mut(&mut self, mid: usize) -> (CudaViewMut<'_, T>, CudaViewMut<'_, T>) {
1866 self.try_split_at_mut(mid).unwrap()
1867 }
1868
1869 pub fn try_split_at_mut(
1873 &mut self,
1874 mid: usize,
1875 ) -> Option<(CudaViewMut<'_, T>, CudaViewMut<'_, T>)> {
1876 let length = self.len;
1877 (mid <= length).then(|| {
1878 let a = CudaViewMut {
1879 ptr: self.cu_device_ptr,
1880 len: mid,
1881 read: &self.read,
1882 write: &self.write,
1883 stream: &self.stream,
1884 marker: PhantomData,
1885 };
1886 let b = CudaViewMut {
1887 ptr: self.cu_device_ptr + (mid * std::mem::size_of::<T>()) as u64,
1888 len: length - mid,
1889 read: &self.read,
1890 write: &self.write,
1891 stream: &self.stream,
1892 marker: PhantomData,
1893 };
1894 (a, b)
1895 })
1896 }
1897}
1898
1899impl<'a, T> CudaView<'a, T> {
1900 pub fn slice(&self, bounds: impl RangeBounds<usize>) -> Self {
1917 self.try_slice(bounds).unwrap()
1918 }
1919
1920 pub fn try_slice(&self, bounds: impl RangeBounds<usize>) -> Option<Self> {
1922 to_range(bounds, self.len).map(|(start, end)| self.resize(start, end))
1923 }
1924
1925 pub unsafe fn transmute<S>(&self, len: usize) -> Option<CudaView<'a, S>> {
1933 (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
1934 CudaView {
1935 ptr: self.ptr,
1936 len,
1937 read: self.read,
1938 write: self.write,
1939 stream: self.stream,
1940 marker: PhantomData,
1941 },
1942 )
1943 }
1944
1945 pub fn split_at(&self, mid: usize) -> (Self, Self) {
1946 self.try_split_at(mid).unwrap()
1947 }
1948
1949 pub fn try_split_at(&self, mid: usize) -> Option<(Self, Self)> {
1953 (mid <= self.len()).then(|| (self.resize(0, mid), self.resize(mid, self.len)))
1954 }
1955
1956 pub fn chunks_exact(&self, chunk_size: usize) -> impl Iterator<Item = CudaView<'a, T>> + '_ {
1960 assert!(self.len.is_multiple_of(chunk_size));
1961 let num_chunks = self.len / chunk_size;
1962 (0..num_chunks).map(move |i| self.resize(i * chunk_size, (i + 1) * chunk_size))
1963 }
1964}
1965
1966impl<'a, T> CudaViewMut<'a, T> {
1967 pub fn slice<'b>(&'b self, bounds: impl RangeBounds<usize>) -> CudaView<'b, T> {
1999 self.try_slice(bounds).unwrap()
2000 }
2001
2002 pub fn try_slice<'b>(&'b self, bounds: impl RangeBounds<usize>) -> Option<CudaView<'b, T>> {
2004 to_range(bounds, self.len).map(move |(start, end)| self.as_view().resize(start, end))
2005 }
2006
2007 pub unsafe fn transmute<'b, S>(&'b self, len: usize) -> Option<CudaView<'b, S>> {
2015 (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
2016 CudaView {
2017 ptr: self.ptr,
2018 len,
2019 read: self.read,
2020 write: self.write,
2021 stream: self.stream,
2022 marker: PhantomData,
2023 },
2024 )
2025 }
2026
2027 pub fn slice_mut<'b>(&'b mut self, bounds: impl RangeBounds<usize>) -> CudaViewMut<'b, T> {
2031 self.try_slice_mut(bounds).unwrap()
2032 }
2033
2034 pub fn try_slice_mut<'b>(
2036 &'b mut self,
2037 bounds: impl RangeBounds<usize>,
2038 ) -> Option<CudaViewMut<'b, T>> {
2039 to_range(bounds, self.len).map(|(start, end)| CudaViewMut {
2040 ptr: self.ptr + (start * std::mem::size_of::<T>()) as u64,
2041 len: end - start,
2042 read: self.read,
2043 write: self.write,
2044 stream: self.stream,
2045 marker: PhantomData,
2046 })
2047 }
2048
2049 pub fn split_at_mut<'b>(&'b mut self, mid: usize) -> (CudaViewMut<'b, T>, CudaViewMut<'b, T>) {
2065 self.try_split_at_mut(mid).unwrap()
2066 }
2067
2068 pub fn try_split_at_mut<'b>(
2072 &'b mut self,
2073 mid: usize,
2074 ) -> Option<(CudaViewMut<'b, T>, CudaViewMut<'b, T>)> {
2075 let length = self.len;
2076 (mid <= length).then(|| {
2077 let a = CudaViewMut {
2078 ptr: self.ptr,
2079 len: mid,
2080 read: self.read,
2081 write: self.write,
2082 stream: self.stream,
2083 marker: PhantomData,
2084 };
2085 let b = CudaViewMut {
2086 ptr: self.ptr + (mid * std::mem::size_of::<T>()) as u64,
2087 len: length - mid,
2088 read: self.read,
2089 write: self.write,
2090 stream: self.stream,
2091 marker: PhantomData,
2092 };
2093 (a, b)
2094 })
2095 }
2096
2097 pub fn chunks_exact_mut(self, chunk_size: usize) -> impl Iterator<Item = CudaViewMut<'a, T>> {
2101 assert!(self.len.is_multiple_of(chunk_size));
2102 let num_chunks = self.len / chunk_size;
2103 (0..num_chunks).map(move |i| CudaViewMut {
2104 ptr: self.ptr + (i * chunk_size * std::mem::size_of::<T>()) as u64,
2105 len: chunk_size,
2106 read: self.read,
2107 write: self.write,
2108 stream: self.stream,
2109 marker: PhantomData,
2110 })
2111 }
2112
2113 pub unsafe fn transmute_mut<'b, S>(&'b mut self, len: usize) -> Option<CudaViewMut<'b, S>> {
2121 (len * std::mem::size_of::<S>() <= self.len * std::mem::size_of::<T>()).then_some(
2122 CudaViewMut {
2123 ptr: self.ptr,
2124 len,
2125 read: self.read,
2126 write: self.write,
2127 stream: self.stream,
2128 marker: PhantomData,
2129 },
2130 )
2131 }
2132}
2133
2134pub(super) fn to_range(range: impl RangeBounds<usize>, len: usize) -> Option<(usize, usize)> {
2135 let start = match range.start_bound() {
2136 Bound::Included(&n) => n,
2137 Bound::Excluded(&n) => n + 1,
2138 Bound::Unbounded => 0,
2139 };
2140 let end = match range.end_bound() {
2141 Bound::Included(&n) => n + 1,
2142 Bound::Excluded(&n) => n,
2143 Bound::Unbounded => len,
2144 };
2145 (start <= end && end <= len).then_some((start, end))
2146}
2147
2148#[derive(Debug)]
2152pub struct CudaModule {
2153 pub(crate) cu_module: sys::CUmodule,
2154 pub(crate) ctx: Arc<CudaContext>,
2155}
2156
2157unsafe impl Send for CudaModule {}
2158unsafe impl Sync for CudaModule {}
2159
2160impl Drop for CudaModule {
2161 fn drop(&mut self) {
2162 self.ctx.record_err(self.ctx.bind_to_thread());
2163 self.ctx
2164 .record_err(unsafe { result::module::unload(self.cu_module) });
2165 }
2166}
2167
2168impl CudaContext {
2169 #[cfg(feature = "nvrtc")]
2173 pub fn load_module(
2174 self: &Arc<Self>,
2175 ptx: crate::nvrtc::Ptx,
2176 ) -> Result<Arc<CudaModule>, result::DriverError> {
2177 self.bind_to_thread()?;
2178
2179 let cu_module = match ptx.0 {
2180 crate::nvrtc::PtxKind::Image(image) => unsafe {
2181 result::module::load_data(image.as_ptr() as *const _)
2182 },
2183 crate::nvrtc::PtxKind::Src(src) => {
2184 let c_src = CString::new(src).unwrap();
2185 unsafe { result::module::load_data(c_src.as_ptr() as *const _) }
2186 }
2187 crate::nvrtc::PtxKind::File(path) => {
2188 let name_c = CString::new(path.to_str().unwrap()).unwrap();
2189 result::module::load(name_c)
2190 }
2191 crate::nvrtc::PtxKind::Binary(data) => unsafe {
2192 result::module::load_data(data.as_ptr() as *const _)
2193 },
2194 }?;
2195 Ok(Arc::new(CudaModule {
2196 cu_module,
2197 ctx: self.clone(),
2198 }))
2199 }
2200}
2201
2202#[derive(Debug, Clone)]
2204pub struct CudaFunction {
2205 pub(crate) cu_function: sys::CUfunction,
2206 #[allow(unused)]
2207 pub(crate) module: Arc<CudaModule>,
2208}
2209
2210unsafe impl Send for CudaFunction {}
2211unsafe impl Sync for CudaFunction {}
2212
2213impl CudaModule {
2214 pub fn load_function(self: &Arc<Self>, fn_name: &str) -> Result<CudaFunction, DriverError> {
2216 let fn_name_c = CString::new(fn_name).unwrap();
2217 let cu_function = unsafe { result::module::get_function(self.cu_module, fn_name_c) }?;
2218 Ok(CudaFunction {
2219 cu_function,
2220 module: self.clone(),
2221 })
2222 }
2223
2224 pub fn get_global<'a>(
2239 self: &'a Arc<Self>,
2240 name: &str,
2241 stream: &'a Arc<CudaStream>,
2242 ) -> Result<CudaViewMut<'a, u8>, DriverError> {
2243 let name_c =
2244 CString::new(name).map_err(|_| DriverError(sys::CUresult::CUDA_ERROR_INVALID_VALUE))?;
2245 let (cu_device_ptr, bytes) = unsafe { result::module::get_global(self.cu_module, name_c) }?;
2246 Ok(CudaViewMut {
2247 ptr: cu_device_ptr,
2248 len: bytes,
2249 read: &None,
2250 write: &None,
2251 stream,
2252 marker: PhantomData,
2253 })
2254 }
2255}
2256
2257impl CudaFunction {
2258 pub fn occupancy_available_dynamic_smem_per_block(
2259 &self,
2260 num_blocks: u32,
2261 block_size: u32,
2262 ) -> Result<usize, result::DriverError> {
2263 let mut dynamic_smem_size: usize = 0;
2264
2265 unsafe {
2266 sys::cuOccupancyAvailableDynamicSMemPerBlock(
2267 &mut dynamic_smem_size,
2268 self.cu_function,
2269 num_blocks as std::ffi::c_int,
2270 block_size as std::ffi::c_int,
2271 )
2272 .result()?
2273 };
2274
2275 Ok(dynamic_smem_size)
2276 }
2277
2278 pub fn occupancy_max_active_blocks_per_multiprocessor(
2279 &self,
2280 block_size: u32,
2281 dynamic_smem_size: usize,
2282 flags: Option<sys::CUoccupancy_flags_enum>,
2283 ) -> Result<u32, result::DriverError> {
2284 let mut num_blocks: std::ffi::c_int = 0;
2285 let flags = flags.unwrap_or(sys::CUoccupancy_flags_enum::CU_OCCUPANCY_DEFAULT);
2286
2287 unsafe {
2288 sys::cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags(
2289 &mut num_blocks,
2290 self.cu_function,
2291 block_size as std::ffi::c_int,
2292 dynamic_smem_size,
2293 flags as std::ffi::c_uint,
2294 )
2295 .result()?
2296 };
2297
2298 Ok(num_blocks as u32)
2299 }
2300
2301 #[cfg(not(any(
2302 feature = "cuda-11070",
2303 feature = "cuda-11060",
2304 feature = "cuda-11050",
2305 feature = "cuda-11040"
2306 )))]
2307 pub fn occupancy_max_active_clusters(
2308 &self,
2309 config: crate::driver::LaunchConfig,
2310 stream: &CudaStream,
2311 ) -> Result<u32, result::DriverError> {
2312 let mut num_clusters: std::ffi::c_int = 0;
2313
2314 let cfg = sys::CUlaunchConfig {
2315 gridDimX: config.grid_dim.0,
2316 gridDimY: config.grid_dim.1,
2317 gridDimZ: config.grid_dim.2,
2318 blockDimX: config.block_dim.0,
2319 blockDimY: config.block_dim.1,
2320 blockDimZ: config.block_dim.2,
2321 sharedMemBytes: config.shared_mem_bytes,
2322 hStream: stream.cu_stream,
2323 attrs: std::ptr::null_mut(),
2324 numAttrs: 0,
2325 };
2326
2327 unsafe {
2328 sys::cuOccupancyMaxActiveClusters(&mut num_clusters, self.cu_function, &cfg).result()?
2329 };
2330
2331 Ok(num_clusters as u32)
2332 }
2333
2334 pub fn occupancy_max_potential_block_size(
2335 &self,
2336 block_size_to_dynamic_smem_size: extern "C" fn(block_size: std::ffi::c_int) -> usize,
2337 dynamic_smem_size: usize,
2338 block_size_limit: u32,
2339 flags: Option<sys::CUoccupancy_flags_enum>,
2340 ) -> Result<(u32, u32), result::DriverError> {
2341 let mut min_grid_size: std::ffi::c_int = 0;
2342 let mut block_size: std::ffi::c_int = 0;
2343 let flags = flags.unwrap_or(sys::CUoccupancy_flags_enum::CU_OCCUPANCY_DEFAULT);
2344
2345 unsafe {
2346 sys::cuOccupancyMaxPotentialBlockSizeWithFlags(
2347 &mut min_grid_size,
2348 &mut block_size,
2349 self.cu_function,
2350 Some(block_size_to_dynamic_smem_size),
2351 dynamic_smem_size,
2352 block_size_limit as std::ffi::c_int,
2353 flags as std::ffi::c_uint,
2354 )
2355 .result()?
2356 };
2357
2358 Ok((min_grid_size as u32, block_size as u32))
2359 }
2360
2361 #[cfg(not(any(
2362 feature = "cuda-11070",
2363 feature = "cuda-11060",
2364 feature = "cuda-11050",
2365 feature = "cuda-11040"
2366 )))]
2367 pub fn occupancy_max_potential_cluster_size(
2368 &self,
2369 config: crate::driver::LaunchConfig,
2370 stream: &CudaStream,
2371 ) -> Result<u32, result::DriverError> {
2372 let mut cluster_size: std::ffi::c_int = 0;
2373
2374 let cfg = sys::CUlaunchConfig {
2375 gridDimX: config.grid_dim.0,
2376 gridDimY: config.grid_dim.1,
2377 gridDimZ: config.grid_dim.2,
2378 blockDimX: config.block_dim.0,
2379 blockDimY: config.block_dim.1,
2380 blockDimZ: config.block_dim.2,
2381 sharedMemBytes: config.shared_mem_bytes,
2382 hStream: stream.cu_stream,
2383 attrs: std::ptr::null_mut(),
2384 numAttrs: 0,
2385 };
2386
2387 unsafe {
2388 sys::cuOccupancyMaxPotentialClusterSize(&mut cluster_size, self.cu_function, &cfg)
2389 .result()?
2390 };
2391
2392 Ok(cluster_size as u32)
2393 }
2394
2395 pub fn get_attribute(
2399 &self,
2400 attribute: CUfunction_attribute_enum,
2401 ) -> Result<i32, result::DriverError> {
2402 self.module.ctx.bind_to_thread()?;
2403 unsafe { result::function::get_function_attribute(self.cu_function, attribute) }
2404 }
2405
2406 pub fn num_regs(&self) -> Result<i32, result::DriverError> {
2408 self.get_attribute(CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_NUM_REGS)
2409 }
2410
2411 pub fn shared_size_bytes(&self) -> Result<i32, result::DriverError> {
2413 self.get_attribute(CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES)
2414 }
2415
2416 pub fn const_size_bytes(&self) -> Result<i32, result::DriverError> {
2418 self.get_attribute(CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_CONST_SIZE_BYTES)
2419 }
2420
2421 pub fn local_size_bytes(&self) -> Result<i32, result::DriverError> {
2423 self.get_attribute(CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES)
2424 }
2425
2426 pub fn max_threads_per_block(&self) -> Result<i32, result::DriverError> {
2428 self.get_attribute(CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK)
2429 }
2430
2431 pub fn ptx_version(&self) -> Result<i32, result::DriverError> {
2433 self.get_attribute(CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_PTX_VERSION)
2434 }
2435
2436 pub fn binary_version(&self) -> Result<i32, result::DriverError> {
2438 self.get_attribute(CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_BINARY_VERSION)
2439 }
2440
2441 pub fn set_attribute(
2443 &self,
2444 attribute: CUfunction_attribute_enum,
2445 value: i32,
2446 ) -> Result<(), result::DriverError> {
2447 unsafe { result::function::set_function_attribute(self.cu_function, attribute, value) }
2448 }
2449
2450 pub fn set_function_cache_config(
2452 &self,
2453 attribute: CUfunc_cache_enum,
2454 ) -> Result<(), result::DriverError> {
2455 unsafe { result::function::set_function_cache_config(self.cu_function, attribute) }
2456 }
2457}
2458
2459impl<T> CudaSlice<T> {
2460 pub fn leak(self) -> sys::CUdeviceptr {
2465 let mut s = std::mem::ManuallyDrop::new(self);
2466 let ptr = s.cu_device_ptr;
2467
2468 if let Some(read) = s.read.as_ref() {
2470 s.stream.ctx.record_err(s.stream.wait(read));
2471 }
2472 if let Some(write) = s.write.as_ref() {
2473 s.stream.ctx.record_err(s.stream.wait(write));
2474 }
2475
2476 unsafe {
2478 std::ptr::drop_in_place(&mut s.read);
2479 std::ptr::drop_in_place(&mut s.write);
2480 std::ptr::drop_in_place(&mut s.stream);
2481 }
2482
2483 ptr
2484 }
2485}
2486
2487impl CudaStream {
2488 pub unsafe fn upgrade_device_ptr<T>(
2497 self: &Arc<Self>,
2498 cu_device_ptr: sys::CUdeviceptr,
2499 len: usize,
2500 ) -> CudaSlice<T> {
2501 let (read, write) = if self.ctx.is_event_tracking() {
2502 (
2503 Some(self.ctx.new_event(None).unwrap()),
2504 Some(self.ctx.new_event(None).unwrap()),
2505 )
2506 } else {
2507 (None, None)
2508 };
2509 CudaSlice {
2510 cu_device_ptr,
2511 len,
2512 read,
2513 write,
2514 stream: self.clone(),
2515 marker: PhantomData,
2516 }
2517 }
2518}
2519
2520#[cfg(test)]
2521mod tests {
2522 use std::time::Instant;
2523
2524 use super::*;
2525
2526 #[test]
2527 fn test_transmutes() {
2528 let ctx = CudaContext::new(0).unwrap();
2529 let stream = ctx.default_stream();
2530 let mut slice = stream.alloc_zeros::<u8>(100).unwrap();
2531 assert!(unsafe { slice.transmute::<f32>(25) }.is_some());
2532 assert!(unsafe { slice.transmute::<f32>(26) }.is_none());
2533 assert!(unsafe { slice.transmute_mut::<f32>(25) }.is_some());
2534 assert!(unsafe { slice.transmute_mut::<f32>(26) }.is_none());
2535
2536 {
2537 let view = slice.slice(0..100);
2538 assert!(unsafe { view.transmute::<f32>(25) }.is_some());
2539 assert!(unsafe { view.transmute::<f32>(26) }.is_none());
2540 }
2541
2542 {
2543 let mut view_mut = slice.slice_mut(0..100);
2544 assert!(unsafe { view_mut.transmute::<f32>(25) }.is_some());
2545 assert!(unsafe { view_mut.transmute::<f32>(26) }.is_none());
2546 assert!(unsafe { view_mut.transmute_mut::<f32>(25) }.is_some());
2547 assert!(unsafe { view_mut.transmute_mut::<f32>(26) }.is_none());
2548 }
2549 }
2550
2551 #[test]
2552 fn test_threading() {
2553 let ctx1 = CudaContext::new(0).unwrap();
2554 let ctx2 = ctx1.clone();
2555
2556 let thread1 = std::thread::spawn(move || {
2557 ctx1.bind_to_thread()?;
2558 ctx1.default_stream().alloc_zeros::<f32>(10)
2559 });
2560 let thread2 = std::thread::spawn(move || {
2561 ctx2.bind_to_thread()?;
2562 ctx2.default_stream().alloc_zeros::<f32>(10)
2563 });
2564
2565 let _: crate::driver::CudaSlice<f32> = thread1.join().unwrap().unwrap();
2566 let _: crate::driver::CudaSlice<f32> = thread2.join().unwrap().unwrap();
2567 }
2568
2569 #[test]
2570 fn test_post_build_arc_count() {
2571 let ctx = CudaContext::new(0).unwrap();
2572 assert_eq!(Arc::strong_count(&ctx), 1);
2573 }
2574
2575 #[test]
2576 fn test_post_alloc_arc_counts() {
2577 let ctx = CudaContext::new(0).unwrap();
2578 assert_eq!(Arc::strong_count(&ctx), 1);
2579 let stream = ctx.default_stream();
2580 assert_eq!(Arc::strong_count(&ctx), 2);
2581 let t = stream.alloc_zeros::<f32>(1).unwrap();
2582 assert_eq!(Arc::strong_count(&ctx), 4);
2583 assert_eq!(Arc::strong_count(&stream), 2);
2584 drop(t);
2585 assert_eq!(Arc::strong_count(&ctx), 2);
2586 assert_eq!(Arc::strong_count(&stream), 1);
2587 drop(stream);
2588 assert_eq!(Arc::strong_count(&ctx), 1);
2589 }
2590
2591 #[test]
2592 #[ignore = "must be executed by itself"]
2593 fn test_post_alloc_memory() {
2594 let ctx = CudaContext::new(0).unwrap();
2595 let stream = ctx.default_stream();
2596
2597 let (free1, total1) = ctx.mem_get_info().unwrap();
2598
2599 let t = stream.clone_htod(&[0.0f32; 5]).unwrap();
2600 let (free2, total2) = ctx.mem_get_info().unwrap();
2601 assert_eq!(total1, total2);
2602 assert!(free2 < free1);
2603
2604 drop(t);
2605 ctx.synchronize().unwrap();
2606
2607 let (free3, total3) = ctx.mem_get_info().unwrap();
2608 assert_eq!(total2, total3);
2609 assert!(free3 > free2);
2610 assert_eq!(free3, free1);
2611 }
2612
2613 #[test]
2614 fn test_ctx_copy_to_views() {
2615 let ctx = CudaContext::new(0).unwrap();
2616 let stream = ctx.default_stream();
2617
2618 let smalls = [
2619 stream.clone_htod(&[-1.0f32, -0.8]).unwrap(),
2620 stream.clone_htod(&[-0.6, -0.4]).unwrap(),
2621 stream.clone_htod(&[-0.2, 0.0]).unwrap(),
2622 stream.clone_htod(&[0.2, 0.4]).unwrap(),
2623 stream.clone_htod(&[0.6, 0.8]).unwrap(),
2624 ];
2625 let mut big = stream.alloc_zeros::<f32>(10).unwrap();
2626
2627 let mut offset = 0;
2628 for small in smalls.iter() {
2629 let mut sub = big.slice_mut(offset..offset + small.len());
2630 stream.memcpy_dtod(small, &mut sub).unwrap();
2631 offset += small.len();
2632 }
2633
2634 assert_eq!(
2635 stream.clone_dtoh(&big).unwrap(),
2636 [-1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8]
2637 );
2638 }
2639
2640 #[test]
2641 fn test_leak_and_upgrade() {
2642 let ctx = CudaContext::new(0).unwrap();
2643 let stream = ctx.default_stream();
2644
2645 let a = stream.clone_htod(&[1.0f32, 2.0, 3.0, 4.0, 5.0]).unwrap();
2646
2647 let ptr = a.leak();
2648 let b = unsafe { stream.upgrade_device_ptr::<f32>(ptr, 3) };
2649 assert_eq!(stream.clone_dtoh(&b).unwrap(), &[1.0, 2.0, 3.0]);
2650
2651 let ptr = b.leak();
2652 let c = unsafe { stream.upgrade_device_ptr::<f32>(ptr, 5) };
2653 assert_eq!(stream.clone_dtoh(&c).unwrap(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
2654 }
2655
2656 #[test]
2658 fn test_slice_is_freed_with_correct_context() {
2659 let ctx0 = CudaContext::new(0).unwrap();
2660 let slice = ctx0.default_stream().clone_htod(&[1.0; 10]).unwrap();
2661 let ctx1 = CudaContext::new(0).unwrap();
2662 ctx1.bind_to_thread().unwrap();
2663 drop(ctx0);
2664 drop(slice);
2665 drop(ctx1);
2666 }
2667
2668 #[test]
2670 fn test_copy_uses_correct_context() {
2671 let ctx0 = CudaContext::new(0).unwrap();
2672 let _ctx1 = CudaContext::new(0).unwrap();
2673 let slice = ctx0.default_stream().clone_htod(&[1.0; 10]).unwrap();
2674 let _out = ctx0.default_stream().clone_dtoh(&slice).unwrap();
2675 }
2676
2677 #[test]
2678 fn test_htod_copy_pinned() {
2679 let truth = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
2680 let ctx = CudaContext::new(0).unwrap();
2681 let stream = ctx.default_stream();
2682 let mut pinned = unsafe { ctx.alloc_pinned::<f32>(10) }.unwrap();
2683 pinned.as_mut_slice().unwrap().clone_from_slice(&truth);
2684 assert_eq!(pinned.as_slice().unwrap(), &truth);
2685 let dst = stream.clone_htod(&pinned).unwrap();
2686 let host = stream.clone_dtoh(&dst).unwrap();
2687 assert_eq!(&host, &truth);
2688 }
2689
2690 #[test]
2691 fn test_pinned_copy_is_faster() {
2692 let ctx = CudaContext::new(0).unwrap();
2693 let stream = ctx.new_stream().unwrap();
2694
2695 let n = 100_000;
2696 let n_samples = 5;
2697 let not_pinned = std::vec![0.0f32; n];
2698
2699 let start = Instant::now();
2700 for _ in 0..n_samples {
2701 let _ = stream.clone_htod(¬_pinned).unwrap();
2702 stream.synchronize().unwrap();
2703 }
2704 let unpinned_elapsed = start.elapsed() / n_samples;
2705
2706 let pinned = unsafe { ctx.alloc_pinned::<f32>(n) }.unwrap();
2707
2708 let start = Instant::now();
2709 for _ in 0..n_samples {
2710 let _ = stream.clone_htod(&pinned).unwrap();
2711 stream.synchronize().unwrap();
2712 }
2713 let pinned_elapsed = start.elapsed() / n_samples;
2714
2715 assert!(
2718 pinned_elapsed.as_secs_f32() * 1.5 < unpinned_elapsed.as_secs_f32(),
2719 "{unpinned_elapsed:?} vs {pinned_elapsed:?}"
2720 );
2721 }
2722
2723 #[test]
2724 fn test_primary_context_is_primary() {
2725 let ctx = CudaContext::new(0).unwrap();
2726 assert!(ctx.is_primary());
2727 }
2728
2729 #[cfg(any(
2732 feature = "cuda-11040",
2733 feature = "cuda-11050",
2734 feature = "cuda-11060",
2735 feature = "cuda-11070",
2736 feature = "cuda-11080",
2737 feature = "cuda-12000",
2738 feature = "cuda-12010",
2739 feature = "cuda-12020",
2740 feature = "cuda-12030",
2741 feature = "cuda-12040",
2742 feature = "cuda-12050",
2743 feature = "cuda-12060",
2744 feature = "cuda-12080",
2745 feature = "cuda-12090",
2746 ))]
2747 fn create_non_primary_context() -> (sys::CUdevice, sys::CUcontext) {
2748 result::init().unwrap();
2749 let cu_device = result::device::get(0).unwrap();
2750
2751 #[cfg(any(
2752 feature = "cuda-12050",
2753 feature = "cuda-12060",
2754 feature = "cuda-12080",
2755 feature = "cuda-12090",
2756 feature = "cuda-13000",
2757 feature = "cuda-13010",
2758 ))]
2759 let cu_ctx = unsafe { result::ctx::create_v4(std::ptr::null_mut(), 0, cu_device) }
2760 .expect("cuCtxCreate_v4 failed");
2761
2762 #[cfg(not(any(
2763 feature = "cuda-12050",
2764 feature = "cuda-12060",
2765 feature = "cuda-12080",
2766 feature = "cuda-12090",
2767 feature = "cuda-13000",
2768 feature = "cuda-13010",
2769 )))]
2770 let cu_ctx =
2771 unsafe { result::ctx::create_v3(0, cu_device) }.expect("cuCtxCreate_v3 failed");
2772
2773 assert!(!cu_ctx.is_null());
2774 (cu_device, cu_ctx)
2775 }
2776
2777 #[test]
2778 #[cfg(any(
2779 feature = "cuda-11040",
2780 feature = "cuda-11050",
2781 feature = "cuda-11060",
2782 feature = "cuda-11070",
2783 feature = "cuda-11080",
2784 feature = "cuda-12000",
2785 feature = "cuda-12010",
2786 feature = "cuda-12020",
2787 feature = "cuda-12030",
2788 feature = "cuda-12040",
2789 feature = "cuda-12050",
2790 feature = "cuda-12060",
2791 feature = "cuda-12080",
2792 feature = "cuda-12090",
2793 ))]
2794 fn test_from_raw_context_creates_and_destroys() {
2795 let (cu_device, cu_ctx) = create_non_primary_context();
2796
2797 let ctx = unsafe { CudaContext::from_raw_context(0, cu_device, cu_ctx) }.unwrap();
2798 assert!(!ctx.is_primary());
2799 ctx.bind_to_thread().unwrap();
2801 drop(ctx);
2803 }
2804
2805 #[test]
2806 #[cfg(any(
2807 feature = "cuda-11040",
2808 feature = "cuda-11050",
2809 feature = "cuda-11060",
2810 feature = "cuda-11070",
2811 feature = "cuda-11080",
2812 feature = "cuda-12000",
2813 feature = "cuda-12010",
2814 feature = "cuda-12020",
2815 feature = "cuda-12030",
2816 feature = "cuda-12040",
2817 feature = "cuda-12050",
2818 feature = "cuda-12060",
2819 feature = "cuda-12080",
2820 feature = "cuda-12090",
2821 ))]
2822 fn test_from_raw_context_bind_to_thread() {
2823 let (cu_device, cu_ctx) = create_non_primary_context();
2824
2825 let ctx = unsafe { CudaContext::from_raw_context(0, cu_device, cu_ctx) }.unwrap();
2826
2827 let ctx2 = ctx.clone();
2829 let handle = std::thread::spawn(move || {
2830 ctx2.bind_to_thread().unwrap();
2831 let stream = ctx2.default_stream();
2832 let data = stream.clone_htod(&[1.0f32, 2.0, 3.0]).unwrap();
2833 let result = stream.clone_dtoh(&data).unwrap();
2834 assert_eq!(result, std::vec![1.0f32, 2.0, 3.0]);
2835 });
2836 handle.join().unwrap();
2837 }
2838
2839 #[test]
2840 #[cfg(any(
2841 feature = "cuda-11040",
2842 feature = "cuda-11050",
2843 feature = "cuda-11060",
2844 feature = "cuda-11070",
2845 feature = "cuda-11080",
2846 feature = "cuda-12000",
2847 feature = "cuda-12010",
2848 feature = "cuda-12020",
2849 feature = "cuda-12030",
2850 feature = "cuda-12040",
2851 feature = "cuda-12050",
2852 feature = "cuda-12060",
2853 feature = "cuda-12080",
2854 feature = "cuda-12090",
2855 feature = "cuda-13000",
2856 feature = "cuda-13010",
2857 ))]
2858 fn test_new_non_primary_creates_and_destroys() {
2859 let ctx = CudaContext::new_non_primary(0, 0).unwrap();
2860 assert!(!ctx.is_primary());
2861 ctx.bind_to_thread().unwrap();
2862 drop(ctx);
2863 }
2864
2865 #[test]
2866 #[cfg(any(
2867 feature = "cuda-11040",
2868 feature = "cuda-11050",
2869 feature = "cuda-11060",
2870 feature = "cuda-11070",
2871 feature = "cuda-11080",
2872 feature = "cuda-12000",
2873 feature = "cuda-12010",
2874 feature = "cuda-12020",
2875 feature = "cuda-12030",
2876 feature = "cuda-12040",
2877 feature = "cuda-12050",
2878 feature = "cuda-12060",
2879 feature = "cuda-12080",
2880 feature = "cuda-12090",
2881 feature = "cuda-13000",
2882 feature = "cuda-13010",
2883 ))]
2884 fn test_new_non_primary_htod_dtoh() {
2885 let ctx = CudaContext::new_non_primary(0, 0).unwrap();
2886 let stream = ctx.default_stream();
2887 let data = stream.clone_htod(&[1.0f32, 2.0, 3.0]).unwrap();
2888 let result = stream.clone_dtoh(&data).unwrap();
2889 assert_eq!(result, std::vec![1.0f32, 2.0, 3.0]);
2890 }
2891
2892 #[test]
2893 #[cfg(any(
2894 feature = "cuda-11040",
2895 feature = "cuda-11050",
2896 feature = "cuda-11060",
2897 feature = "cuda-11070",
2898 feature = "cuda-11080",
2899 feature = "cuda-12000",
2900 feature = "cuda-12010",
2901 feature = "cuda-12020",
2902 feature = "cuda-12030",
2903 feature = "cuda-12040",
2904 feature = "cuda-12050",
2905 feature = "cuda-12060",
2906 feature = "cuda-12080",
2907 feature = "cuda-12090",
2908 feature = "cuda-13000",
2909 feature = "cuda-13010",
2910 ))]
2911 fn test_new_non_primary_cross_thread() {
2912 let ctx = CudaContext::new_non_primary(0, 0).unwrap();
2913 let ctx2 = ctx.clone();
2914 let handle = std::thread::spawn(move || {
2915 ctx2.bind_to_thread().unwrap();
2916 let stream = ctx2.default_stream();
2917 let data = stream.clone_htod(&[4.0f32, 5.0, 6.0]).unwrap();
2918 let result = stream.clone_dtoh(&data).unwrap();
2919 assert_eq!(result, std::vec![4.0f32, 5.0, 6.0]);
2920 });
2921 handle.join().unwrap();
2922 }
2923}