1use std::{marker::PhantomData, mem::ManuallyDrop, ptr, sync::Arc};
2
3use singe_cuda::context::Context as CudaContext;
4use singe_cuda::data_type::DataType;
5use singe_cuda::types::DevicePtr;
6
7use crate::{
8 context::Context,
9 error::{Error, Result},
10 matrix::{DenseMatrixDescriptor, SparseMatrixDescriptor},
11 sys, try_ffi,
12 types::{Operation, SpMmOpAlgorithm},
13};
14
15#[derive(Debug)]
16pub struct SpGemmDescriptor {
17 handle: sys::cusparseSpGEMMDescr_t,
18 cuda_ctx: Arc<CudaContext>,
19}
20
21#[derive(Debug)]
22pub struct SpSvDescriptor {
23 handle: sys::cusparseSpSVDescr_t,
24 cuda_ctx: Arc<CudaContext>,
25}
26
27#[derive(Debug)]
28pub struct SpSmDescriptor {
29 handle: sys::cusparseSpSMDescr_t,
30 cuda_ctx: Arc<CudaContext>,
31}
32
33#[derive(Debug)]
34pub struct SpMmOpPlan<'a> {
35 context: &'a Context,
36 handle: sys::cusparseSpMMOpPlan_t,
37 _matrix_a: PhantomData<&'a SparseMatrixDescriptor<'a>>,
38 _matrix_b: PhantomData<&'a DenseMatrixDescriptor<'a>>,
39 _matrix_c: PhantomData<&'a DenseMatrixDescriptor<'a>>,
40}
41
42unsafe impl Send for SpGemmDescriptor {}
45unsafe impl Sync for SpGemmDescriptor {}
46unsafe impl Send for SpSvDescriptor {}
47unsafe impl Sync for SpSvDescriptor {}
48unsafe impl Send for SpSmDescriptor {}
49unsafe impl Sync for SpSmDescriptor {}
50unsafe impl Send for SpMmOpPlan<'_> {}
51unsafe impl Sync for SpMmOpPlan<'_> {}
52
53impl SpGemmDescriptor {
54 pub fn create(ctx: &Context) -> Result<Self> {
55 ctx.bind()?;
56
57 let mut handle = ptr::null_mut();
58 unsafe {
59 try_ffi!(sys::cusparseSpGEMM_createDescr(&raw mut handle))?;
60 }
61
62 if handle.is_null() {
63 return Err(Error::NullHandle);
64 }
65
66 Ok(Self {
67 handle,
68 cuda_ctx: Arc::clone(ctx.cuda_context()),
69 })
70 }
71
72 pub fn num_products(&self) -> Result<usize> {
73 let mut value = 0_i64;
74 unsafe {
75 try_ffi!(sys::cusparseSpGEMM_getNumProducts(
76 self.as_raw(),
77 &raw mut value,
78 ))?;
79 }
80
81 usize::try_from(value).map_err(|_| Error::OutOfRange {
82 name: "num_products".into(),
83 })
84 }
85
86 pub fn as_raw(&self) -> sys::cusparseSpGEMMDescr_t {
87 self.handle
88 }
89
90 pub(crate) fn ensure_context(&self, ctx: &Context) -> Result<()> {
91 if self.cuda_ctx.as_ref() != ctx.cuda_context().as_ref() {
92 return Err(Error::PlanContextMismatch);
93 }
94 Ok(())
95 }
96
97 pub unsafe fn from_raw(handle: sys::cusparseSpGEMMDescr_t, ctx: &Context) -> Result<Self> {
105 if handle.is_null() {
106 return Err(Error::NullHandle);
107 }
108
109 Ok(Self {
110 handle,
111 cuda_ctx: Arc::clone(ctx.cuda_context()),
112 })
113 }
114
115 pub fn into_raw(self) -> sys::cusparseSpGEMMDescr_t {
121 let descriptor = ManuallyDrop::new(self);
122 descriptor.handle
123 }
124}
125
126impl Drop for SpGemmDescriptor {
127 fn drop(&mut self) {
128 unsafe {
129 if let Err(err) = try_ffi!(sys::cusparseSpGEMM_destroyDescr(self.handle)) {
130 #[cfg(debug_assertions)]
131 eprintln!("failed to destroy cusparse spgemm descriptor: {err}");
132 }
133 }
134 }
135}
136
137impl SpSvDescriptor {
138 pub fn create(ctx: &Context) -> Result<Self> {
139 ctx.bind()?;
140
141 let mut handle = ptr::null_mut();
142 unsafe {
143 try_ffi!(sys::cusparseSpSV_createDescr(&raw mut handle))?;
144 }
145
146 if handle.is_null() {
147 return Err(Error::NullHandle);
148 }
149
150 Ok(Self {
151 handle,
152 cuda_ctx: Arc::clone(ctx.cuda_context()),
153 })
154 }
155
156 pub fn as_raw(&self) -> sys::cusparseSpSVDescr_t {
157 self.handle
158 }
159
160 pub(crate) fn ensure_context(&self, ctx: &Context) -> Result<()> {
161 if self.cuda_ctx.as_ref() != ctx.cuda_context().as_ref() {
162 return Err(Error::PlanContextMismatch);
163 }
164 Ok(())
165 }
166
167 pub unsafe fn from_raw(handle: sys::cusparseSpSVDescr_t, ctx: &Context) -> Result<Self> {
175 if handle.is_null() {
176 return Err(Error::NullHandle);
177 }
178
179 Ok(Self {
180 handle,
181 cuda_ctx: Arc::clone(ctx.cuda_context()),
182 })
183 }
184
185 pub fn into_raw(self) -> sys::cusparseSpSVDescr_t {
191 let descriptor = ManuallyDrop::new(self);
192 descriptor.handle
193 }
194}
195
196impl Drop for SpSvDescriptor {
197 fn drop(&mut self) {
198 unsafe {
199 if let Err(err) = try_ffi!(sys::cusparseSpSV_destroyDescr(self.handle)) {
200 #[cfg(debug_assertions)]
201 eprintln!("failed to destroy cusparse spsv descriptor: {err}");
202 }
203 }
204 }
205}
206
207impl SpSmDescriptor {
208 pub fn create(ctx: &Context) -> Result<Self> {
209 ctx.bind()?;
210
211 let mut handle = ptr::null_mut();
212 unsafe {
213 try_ffi!(sys::cusparseSpSM_createDescr(&raw mut handle))?;
214 }
215
216 if handle.is_null() {
217 return Err(Error::NullHandle);
218 }
219
220 Ok(Self {
221 handle,
222 cuda_ctx: Arc::clone(ctx.cuda_context()),
223 })
224 }
225
226 pub fn as_raw(&self) -> sys::cusparseSpSMDescr_t {
227 self.handle
228 }
229
230 pub(crate) fn ensure_context(&self, ctx: &Context) -> Result<()> {
231 if self.cuda_ctx.as_ref() != ctx.cuda_context().as_ref() {
232 return Err(Error::PlanContextMismatch);
233 }
234 Ok(())
235 }
236
237 pub unsafe fn from_raw(handle: sys::cusparseSpSMDescr_t, ctx: &Context) -> Result<Self> {
245 if handle.is_null() {
246 return Err(Error::NullHandle);
247 }
248
249 Ok(Self {
250 handle,
251 cuda_ctx: Arc::clone(ctx.cuda_context()),
252 })
253 }
254
255 pub fn into_raw(self) -> sys::cusparseSpSMDescr_t {
261 let descriptor = ManuallyDrop::new(self);
262 descriptor.handle
263 }
264}
265
266impl<'a> SpMmOpPlan<'a> {
267 pub fn create(
268 ctx: &'a Context,
269 op_a: Operation,
270 op_b: Operation,
271 matrix_a: &'a SparseMatrixDescriptor<'a>,
272 matrix_b: &'a DenseMatrixDescriptor<'a>,
273 matrix_c: &'a mut DenseMatrixDescriptor<'a>,
274 compute_type: DataType,
275 algorithm: SpMmOpAlgorithm,
276 add_operation_ltoir: Option<&[u8]>,
277 mul_operation_ltoir: Option<&[u8]>,
278 epilogue_ltoir: Option<&[u8]>,
279 ) -> Result<(Self, usize)> {
280 matrix_a.ensure_context(ctx)?;
281 matrix_b.ensure_context(ctx)?;
282 matrix_c.ensure_context(ctx)?;
283 ctx.bind()?;
284
285 let mut handle = ptr::null_mut();
286 let mut workspace_size = 0;
287 let (add_ptr, add_len) = ltoir_parts(add_operation_ltoir);
288 let (mul_ptr, mul_len) = ltoir_parts(mul_operation_ltoir);
289 let (epilogue_ptr, epilogue_len) = ltoir_parts(epilogue_ltoir);
290 unsafe {
291 try_ffi!(sys::cusparseSpMMOp_createPlan(
292 ctx.as_raw(),
293 &raw mut handle,
294 op_a.into(),
295 op_b.into(),
296 matrix_a.as_raw_const(),
297 matrix_b.as_raw_const(),
298 matrix_c.as_raw(),
299 compute_type.into(),
300 algorithm.into(),
301 add_ptr.cast(),
302 add_len as _,
303 mul_ptr.cast(),
304 mul_len as _,
305 epilogue_ptr.cast(),
306 epilogue_len as _,
307 &raw mut workspace_size,
308 ))?;
309 }
310
311 if handle.is_null() {
312 return Err(Error::NullHandle);
313 }
314
315 Ok((
316 Self {
317 context: ctx,
318 handle,
319 _matrix_a: PhantomData,
320 _matrix_b: PhantomData,
321 _matrix_c: PhantomData,
322 },
323 usize::try_from(workspace_size).map_err(|_| Error::OutOfRange {
324 name: "spmm op workspace size".into(),
325 })?,
326 ))
327 }
328
329 pub fn context(&self) -> &Context {
330 self.context
331 }
332
333 #[allow(deprecated)]
414 pub fn execute(&self, external_buffer: Option<DevicePtr>) -> Result<()> {
415 self.context.bind()?;
416
417 unsafe {
418 try_ffi!(sys::cusparseSpMMOp(
419 self.as_raw(),
420 external_buffer.unwrap_or(DevicePtr::null()).as_ptr() as _,
421 ))?;
422 }
423 Ok(())
424 }
425
426 pub fn as_raw(&self) -> sys::cusparseSpMMOpPlan_t {
427 self.handle
428 }
429
430 pub unsafe fn from_raw(ctx: &'a Context, handle: sys::cusparseSpMMOpPlan_t) -> Result<Self> {
439 if handle.is_null() {
440 return Err(Error::NullHandle);
441 }
442
443 Ok(Self {
444 context: ctx,
445 handle,
446 _matrix_a: PhantomData,
447 _matrix_b: PhantomData,
448 _matrix_c: PhantomData,
449 })
450 }
451
452 pub fn into_raw(self) -> sys::cusparseSpMMOpPlan_t {
458 let plan = ManuallyDrop::new(self);
459 plan.handle
460 }
461}
462
463impl Drop for SpMmOpPlan<'_> {
464 fn drop(&mut self) {
465 if let Err(err) = self.context.bind() {
466 #[cfg(debug_assertions)]
467 eprintln!("failed to bind cusparse spmm op plan context during drop: {err}");
468 return;
469 }
470
471 unsafe {
472 if let Err(err) = try_ffi!(sys::cusparseSpMMOp_destroyPlan(self.handle)) {
473 #[cfg(debug_assertions)]
474 eprintln!("failed to destroy cusparse spmm op plan: {err}");
475 }
476 }
477 }
478}
479
480fn ltoir_parts(buffer: Option<&[u8]>) -> (*const u8, usize) {
481 match buffer {
482 Some(buffer) => (buffer.as_ptr(), buffer.len()),
483 None => (ptr::null(), 0),
484 }
485}
486
487impl Drop for SpSmDescriptor {
488 fn drop(&mut self) {
489 unsafe {
490 if let Err(err) = try_ffi!(sys::cusparseSpSM_destroyDescr(self.handle)) {
491 #[cfg(debug_assertions)]
492 eprintln!("failed to destroy cusparse spsm descriptor: {err}");
493 }
494 }
495 }
496}