1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! Runtime-API library + kernel loading.
//!
//! Uses `cudaLibraryLoadData` (CUDA 12.0+) to load PTX / CUBIN at runtime,
//! exposing kernels as [`Kernel`] handles. On CUDA < 12 this returns
//! [`crate::Error::FeatureNotSupported`].
use core::ffi::{c_char, c_void};
use std::ffi::CString;
use std::sync::Arc;
use baracuda_cuda_sys::runtime::{cudaKernel_t, cudaLibrary_t, runtime};
use baracuda_types::{Feature, supports};
use crate::error::{Error, Result, check};
/// A loaded CUDA library (CUDA 12.0+).
#[derive(Clone)]
pub struct Library {
inner: Arc<LibraryInner>,
}
struct LibraryInner {
handle: cudaLibrary_t,
}
unsafe impl Send for LibraryInner {}
unsafe impl Sync for LibraryInner {}
impl core::fmt::Debug for LibraryInner {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Library")
.field("handle", &self.handle)
.finish_non_exhaustive()
}
}
impl core::fmt::Debug for Library {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.inner.fmt(f)
}
}
impl Library {
/// Load a library from a raw binary image (CUBIN, fatbin, or null-terminated PTX).
pub fn load_raw(image: &[u8]) -> Result<Self> {
let installed = crate::init::driver_version()?;
if !supports(installed, Feature::LibraryManagement) {
return Err(Error::FeatureNotSupported {
api: "cudaLibraryLoadData",
since: Feature::LibraryManagement.required_version(),
});
}
let r = runtime()?;
let cu = r.cuda_library_load_data()?;
let mut lib: cudaLibrary_t = core::ptr::null_mut();
check(unsafe {
cu(
&mut lib,
image.as_ptr() as *const c_void,
core::ptr::null_mut(), // jit_options
core::ptr::null_mut(), // jit_option_values
0, // num_jit_options
core::ptr::null_mut(), // library_options
core::ptr::null_mut(), // library_option_values
0, // num_library_options
)
})?;
Ok(Self {
inner: Arc::new(LibraryInner { handle: lib }),
})
}
/// Load a library from a PTX source string.
pub fn load_ptx(ptx_source: &str) -> Result<Self> {
let c_src = CString::new(ptx_source).map_err(|_| {
Error::Loader(baracuda_core::LoaderError::SymbolNotFound {
library: "cuda-runtime",
symbol: "cudaLibraryLoadData(PTX input contained a NUL byte)",
})
})?;
Self::load_raw(c_src.as_bytes_with_nul())
}
/// Look up a kernel entry point by name.
pub fn get_kernel(&self, name: &str) -> Result<Kernel> {
let r = runtime()?;
let cu = r.cuda_library_get_kernel()?;
let c_name = CString::new(name).map_err(|_| {
Error::Loader(baracuda_core::LoaderError::SymbolNotFound {
library: "cuda-runtime",
symbol: "cudaLibraryGetKernel(kernel name contained a NUL byte)",
})
})?;
let mut kernel: cudaKernel_t = core::ptr::null_mut();
check(unsafe {
cu(
&mut kernel,
self.inner.handle,
c_name.as_ptr() as *const c_char,
)
})?;
Ok(Kernel {
handle: kernel,
_library: self.clone(),
})
}
/// Raw `cudaLibrary_t`. Use with care.
#[inline]
pub fn as_raw(&self) -> cudaLibrary_t {
self.inner.handle
}
}
impl Drop for LibraryInner {
fn drop(&mut self) {
if let Ok(r) = runtime()
&& let Ok(cu) = r.cuda_library_unload()
{
let _ = unsafe { cu(self.handle) };
}
}
}
/// A kernel entry point inside a [`Library`].
#[derive(Clone, Debug)]
pub struct Kernel {
handle: cudaKernel_t,
// Keeps the library alive for the lifetime of the kernel.
_library: Library,
}
unsafe impl Send for Kernel {}
unsafe impl Sync for Kernel {}
impl Kernel {
/// Raw `cudaKernel_t`. Use with care.
#[inline]
pub fn as_raw(&self) -> cudaKernel_t {
self.handle
}
/// Returns the raw kernel handle cast to a `const void*` — the form
/// expected by `cudaLaunchKernel`. Library-loaded kernels can be
/// launched through the standard runtime launch function by
/// passing this pointer.
#[inline]
pub fn as_launch_ptr(&self) -> *const c_void {
self.handle as *const c_void
}
/// `cudaOccupancyMaxActiveBlocksPerMultiprocessor` — how many blocks
/// of size `block_size` can run concurrently per SM given
/// `dynamic_smem_bytes` of dynamic shared memory.
pub fn max_active_blocks_per_multiprocessor(
&self,
block_size: i32,
dynamic_smem_bytes: usize,
) -> Result<i32> {
let r = runtime()?;
let cu = r.cuda_occupancy_max_active_blocks_per_multiprocessor()?;
let mut n: core::ffi::c_int = 0;
check(unsafe { cu(&mut n, self.as_launch_ptr(), block_size, dynamic_smem_bytes) })?;
Ok(n)
}
/// `cudaOccupancyMaxActiveBlocksPerMultiprocessorWithFlags` — same
/// as [`Self::max_active_blocks_per_multiprocessor`] but accepting
/// occupancy-flag bits (0 = default, 1 = disable shared-memory
/// carveout adjustment).
pub fn max_active_blocks_per_multiprocessor_with_flags(
&self,
block_size: i32,
dynamic_smem_bytes: usize,
flags: u32,
) -> Result<i32> {
let r = runtime()?;
let cu = r.cuda_occupancy_max_active_blocks_per_multiprocessor_with_flags()?;
let mut n: core::ffi::c_int = 0;
check(unsafe {
cu(
&mut n,
self.as_launch_ptr(),
block_size,
dynamic_smem_bytes,
flags,
)
})?;
Ok(n)
}
/// `cudaOccupancyAvailableDynamicSMemPerBlock` — how much dynamic
/// shared memory can each block use if `num_blocks` of `block_size`
/// threads run concurrently on each SM.
pub fn available_dynamic_smem_per_block(
&self,
num_blocks: i32,
block_size: i32,
) -> Result<usize> {
let r = runtime()?;
let cu = r.cuda_occupancy_available_dynamic_smem_per_block()?;
let mut n: usize = 0;
check(unsafe { cu(&mut n, self.as_launch_ptr(), num_blocks, block_size) })?;
Ok(n)
}
/// Set a writable `cudaFuncAttribute`. The common one is
/// `cudaFuncAttributeMaxDynamicSharedMemorySize = 8`.
pub fn set_attribute(&self, attr: i32, value: i32) -> Result<()> {
let r = runtime()?;
let cu = r.cuda_func_set_attribute()?;
check(unsafe { cu(self.as_launch_ptr(), attr, value) })
}
}