use std::{
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
ptr,
sync::Arc,
};
use singe_cuda::{
context::Context as CudaContext, data_type::DataTypeLike, memory::DeviceMemory,
types::DevicePtr,
};
use crate::{
context::Context,
error::{Error, Result},
sys, try_ffi,
types::{IndexBase, IndexType, IndexTypeLike},
utility::{to_i64, to_usize},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SparseVectorInfo {
pub size: usize,
pub nonzero_count: usize,
pub indices: DevicePtr,
pub values: DevicePtr,
pub index_type: IndexType,
pub index_base: IndexBase,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DenseVectorInfo {
pub size: usize,
pub values: DevicePtr,
}
#[derive(Debug)]
pub struct SparseVectorDescriptor<'a> {
handle: sys::cusparseSpVecDescr_t,
cuda_ctx: Arc<CudaContext>,
_buffers: PhantomData<&'a mut ()>,
}
#[derive(Debug)]
pub struct DenseVectorDescriptor<'a> {
handle: sys::cusparseDnVecDescr_t,
cuda_ctx: Arc<CudaContext>,
_buffers: PhantomData<&'a mut ()>,
}
unsafe impl Send for SparseVectorDescriptor<'_> {}
unsafe impl Send for DenseVectorDescriptor<'_> {}
impl<'a> SparseVectorDescriptor<'a> {
pub fn create<I: IndexTypeLike, T: DataTypeLike>(
ctx: &Context,
size: usize,
nonzero_count: usize,
indices: &'a mut DeviceMemory<I>,
values: &'a mut DeviceMemory<T>,
index_base: IndexBase,
) -> Result<Self> {
ctx.bind()?;
let size = to_i64(size, "size")?;
let nonzero_count = to_i64(nonzero_count, "nonzero_count")?;
let mut handle = ptr::null_mut();
unsafe {
try_ffi!(sys::cusparseCreateSpVec(
&raw mut handle,
size,
nonzero_count,
indices.as_mut_ptr().cast(),
values.as_mut_ptr().cast(),
I::index_type().into(),
index_base.into(),
T::data_type().into(),
))?;
}
if handle.is_null() {
return Err(Error::NullHandle);
}
Ok(Self {
handle,
cuda_ctx: Arc::clone(ctx.cuda_context()),
_buffers: PhantomData,
})
}
pub fn index_base(&self) -> Result<IndexBase> {
let mut value = sys::cusparseIndexBase_t::CUSPARSE_INDEX_BASE_ZERO;
unsafe {
try_ffi!(sys::cusparseSpVecGetIndexBase(
self.as_raw_const(),
&raw mut value
))?;
}
Ok(value.into())
}
pub fn values(&self) -> Result<DevicePtr> {
let mut values = ptr::null_mut();
unsafe {
try_ffi!(sys::cusparseSpVecGetValues(self.as_raw(), &raw mut values))?;
}
Ok(unsafe { DevicePtr::from_raw(values.cast()) })
}
pub fn set_values<T: DataTypeLike>(&mut self, values: &'a mut DeviceMemory<T>) -> Result<()> {
unsafe {
try_ffi!(sys::cusparseSpVecSetValues(
self.as_raw(),
values.as_mut_ptr().cast()
))?;
}
Ok(())
}
pub fn info(&self) -> Result<SparseVectorInfo> {
let mut size = 0_i64;
let mut nnz = 0_i64;
let mut indices = ptr::null_mut();
let mut values = ptr::null_mut();
let mut index_type = sys::cusparseIndexType_t::CUSPARSE_INDEX_32I;
let mut index_base = sys::cusparseIndexBase_t::CUSPARSE_INDEX_BASE_ZERO;
let mut value_type = MaybeUninit::uninit();
unsafe {
try_ffi!(sys::cusparseSpVecGet(
self.as_raw(),
&raw mut size,
&raw mut nnz,
&raw mut indices,
&raw mut values,
&raw mut index_type,
&raw mut index_base,
value_type.as_mut_ptr(),
))?;
}
Ok(SparseVectorInfo {
size: to_usize(size, "size")?,
nonzero_count: to_usize(nnz, "nonzero_count")?,
indices: unsafe { DevicePtr::from_raw(indices.cast()) },
values: unsafe { DevicePtr::from_raw(values.cast()) },
index_type: index_type.into(),
index_base: index_base.into(),
})
}
pub fn as_raw(&self) -> sys::cusparseSpVecDescr_t {
self.handle
}
pub fn as_raw_const(&self) -> sys::cusparseConstSpVecDescr_t {
self.handle.cast_const()
}
pub fn cuda_context(&self) -> &Arc<CudaContext> {
&self.cuda_ctx
}
pub(crate) fn ensure_context(&self, ctx: &Context) -> Result<()> {
if self.cuda_ctx.as_ref() != ctx.cuda_context().as_ref() {
return Err(Error::PlanContextMismatch);
}
Ok(())
}
pub unsafe fn from_raw(handle: sys::cusparseSpVecDescr_t, ctx: &Context) -> Result<Self> {
if handle.is_null() {
return Err(Error::NullHandle);
}
Ok(Self {
handle,
cuda_ctx: Arc::clone(ctx.cuda_context()),
_buffers: PhantomData,
})
}
pub fn into_raw(self) -> sys::cusparseSpVecDescr_t {
let descriptor = ManuallyDrop::new(self);
descriptor.handle
}
}
impl Drop for SparseVectorDescriptor<'_> {
fn drop(&mut self) {
unsafe {
if let Err(err) = try_ffi!(sys::cusparseDestroySpVec(self.as_raw_const())) {
#[cfg(debug_assertions)]
eprintln!("failed to destroy cusparse sparse vector descriptor: {err}");
}
}
}
}
impl<'a> DenseVectorDescriptor<'a> {
pub fn create<T: DataTypeLike>(
ctx: &Context,
size: usize,
values: &'a mut DeviceMemory<T>,
) -> Result<Self> {
ctx.bind()?;
let size = to_i64(size, "size")?;
let mut handle = ptr::null_mut();
unsafe {
try_ffi!(sys::cusparseCreateDnVec(
&raw mut handle,
size,
values.as_mut_ptr().cast(),
T::data_type().into(),
))?;
}
if handle.is_null() {
return Err(Error::NullHandle);
}
Ok(Self {
handle,
cuda_ctx: Arc::clone(ctx.cuda_context()),
_buffers: PhantomData,
})
}
pub fn values(&self) -> Result<DevicePtr> {
let mut values = ptr::null_mut();
unsafe {
try_ffi!(sys::cusparseDnVecGetValues(self.as_raw(), &raw mut values))?;
}
Ok(unsafe { DevicePtr::from_raw(values.cast()) })
}
pub fn set_values<T: DataTypeLike>(&mut self, values: &'a mut DeviceMemory<T>) -> Result<()> {
unsafe {
try_ffi!(sys::cusparseDnVecSetValues(
self.as_raw(),
values.as_mut_ptr().cast()
))?;
}
Ok(())
}
pub fn info(&self) -> Result<DenseVectorInfo> {
let mut size = 0_i64;
let mut values = ptr::null_mut();
let mut value_type = MaybeUninit::uninit();
unsafe {
try_ffi!(sys::cusparseDnVecGet(
self.as_raw(),
&raw mut size,
&raw mut values,
value_type.as_mut_ptr(),
))?;
}
Ok(DenseVectorInfo {
size: to_usize(size, "size")?,
values: unsafe { DevicePtr::from_raw(values.cast()) },
})
}
pub fn as_raw(&self) -> sys::cusparseDnVecDescr_t {
self.handle
}
pub fn as_raw_const(&self) -> sys::cusparseConstDnVecDescr_t {
self.handle.cast_const()
}
pub fn cuda_context(&self) -> &Arc<CudaContext> {
&self.cuda_ctx
}
pub(crate) fn ensure_context(&self, ctx: &Context) -> Result<()> {
if self.cuda_ctx.as_ref() != ctx.cuda_context().as_ref() {
return Err(Error::PlanContextMismatch);
}
Ok(())
}
pub unsafe fn from_raw(handle: sys::cusparseDnVecDescr_t, ctx: &Context) -> Result<Self> {
if handle.is_null() {
return Err(Error::NullHandle);
}
Ok(Self {
handle,
cuda_ctx: Arc::clone(ctx.cuda_context()),
_buffers: PhantomData,
})
}
pub fn into_raw(self) -> sys::cusparseDnVecDescr_t {
let descriptor = ManuallyDrop::new(self);
descriptor.handle
}
}
impl Drop for DenseVectorDescriptor<'_> {
fn drop(&mut self) {
unsafe {
if let Err(err) = try_ffi!(sys::cusparseDestroyDnVec(self.as_raw_const())) {
#[cfg(debug_assertions)]
eprintln!("failed to destroy cusparse dense vector descriptor: {err}");
}
}
}
}
pub fn gather(
ctx: &Context,
sparse_vector: &mut SparseVectorDescriptor,
dense_vector: &DenseVectorDescriptor,
) -> Result<()> {
sparse_vector.ensure_context(ctx)?;
dense_vector.ensure_context(ctx)?;
ctx.bind()?;
unsafe {
try_ffi!(sys::cusparseGather(
ctx.as_raw(),
dense_vector.as_raw_const(),
sparse_vector.as_raw(),
))?;
}
Ok(())
}
pub fn scatter(
ctx: &Context,
sparse_vector: &SparseVectorDescriptor,
dense_vector: &mut DenseVectorDescriptor,
) -> Result<()> {
sparse_vector.ensure_context(ctx)?;
dense_vector.ensure_context(ctx)?;
ctx.bind()?;
unsafe {
try_ffi!(sys::cusparseScatter(
ctx.as_raw(),
sparse_vector.as_raw_const(),
dense_vector.as_raw(),
))?;
}
Ok(())
}