use std::{fmt::Debug, marker::PhantomData, sync::Arc};
use openvm_cuda_common::{
copy::MemCopyD2H, d_buffer::DeviceBuffer, error::MemCopyError, stream::GpuDeviceCtx,
};
use openvm_stark_backend::prover::MatrixDimensions;
pub struct DeviceMatrix<T> {
buffer: Arc<DeviceBuffer<T>>,
height: usize,
width: usize,
}
unsafe impl<T> Send for DeviceMatrix<T> {}
unsafe impl<T> Sync for DeviceMatrix<T> {}
impl<T> Clone for DeviceMatrix<T> {
fn clone(&self) -> Self {
Self {
buffer: Arc::clone(&self.buffer),
height: self.height,
width: self.width,
}
}
}
impl<T> Drop for DeviceMatrix<T> {
fn drop(&mut self) {
tracing::debug!(
"Dropping DeviceMatrix of size {} with Arc strong count={}",
self.buffer.len(),
self.strong_count()
);
}
}
impl<T> DeviceMatrix<T> {
pub fn new(buffer: Arc<DeviceBuffer<T>>, height: usize, width: usize) -> Self {
assert_ne!(
height * width,
0,
"Zero dimensions h {} w {} are wrong",
height,
width
);
assert_eq!(
buffer.len(),
height * width,
"Buffer size must match dimensions"
);
Self {
buffer,
height,
width,
}
}
pub fn with_capacity_on(height: usize, width: usize, device_ctx: &GpuDeviceCtx) -> Self {
let buffer = DeviceBuffer::with_capacity_on(height * width, device_ctx);
Self::new(Arc::new(buffer), height, width)
}
pub fn dummy() -> Self {
Self {
buffer: Arc::new(DeviceBuffer::new()),
height: 0,
width: 0,
}
}
pub fn buffer(&self) -> &DeviceBuffer<T> {
&self.buffer
}
pub fn strong_count(&self) -> usize {
Arc::strong_count(&self.buffer)
}
pub fn as_view(&self) -> DeviceMatrixView<'_, T> {
unsafe { DeviceMatrixView::from_raw_parts(self.buffer.as_ptr(), self.height, self.width) }
}
}
impl<T> MatrixDimensions for DeviceMatrix<T> {
#[inline]
fn height(&self) -> usize {
self.height
}
#[inline]
fn width(&self) -> usize {
self.width
}
}
impl<T> MemCopyD2H<T> for DeviceMatrix<T> {
fn to_host_on(&self, device_ctx: &GpuDeviceCtx) -> Result<Vec<T>, MemCopyError> {
self.buffer.to_host_on(device_ctx)
}
}
impl<T: Debug> Debug for DeviceMatrix<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"DeviceMatrix (height = {}, width = {}): {:?}",
self.height(),
self.width(),
self.buffer()
)
}
}
#[derive(Clone, Copy)]
pub struct DeviceMatrixView<'a, T> {
ptr: *const T,
height: usize,
width: usize,
_ptr_lifetime: PhantomData<&'a T>,
}
unsafe impl<T> Send for DeviceMatrixView<'_, T> {}
unsafe impl<T> Sync for DeviceMatrixView<'_, T> {}
impl<T> DeviceMatrixView<'_, T> {
pub unsafe fn from_raw_parts(ptr: *const T, height: usize, width: usize) -> Self {
Self {
ptr,
height,
width,
_ptr_lifetime: PhantomData,
}
}
pub fn as_ptr(&self) -> *const T {
self.ptr
}
}
impl<T> MatrixDimensions for DeviceMatrixView<'_, T> {
#[inline]
fn height(&self) -> usize {
self.height
}
#[inline]
fn width(&self) -> usize {
self.width
}
}
pub trait Basis: Copy + Debug + Send + Sync {}
#[derive(Clone, Copy, Debug)]
pub struct Coeff;
impl Basis for Coeff {}
#[derive(Clone, Copy, Debug)]
pub struct LagrangeCoeff;
impl Basis for LagrangeCoeff {}
#[derive(Clone, Copy, Debug)]
pub struct ExtendedLagrangeCoeff;
impl Basis for ExtendedLagrangeCoeff {}
pub struct DevicePoly<T, B> {
pub is_bit_reversed: bool,
pub coeff: DeviceBuffer<T>,
_marker: PhantomData<B>,
}
impl<T, B> DevicePoly<T, B> {
pub fn new(is_bit_reversed: bool, coeff: DeviceBuffer<T>) -> Self {
Self {
is_bit_reversed,
coeff,
_marker: PhantomData,
}
}
#[inline]
pub fn len(&self) -> usize {
self.coeff.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_device_matrix() {
let device_ctx = GpuDeviceCtx::for_current_device().unwrap();
let buffer = Arc::new(DeviceBuffer::<i32>::with_capacity_on(12, &device_ctx));
let matrix = DeviceMatrix::<i32>::new(buffer, 3, 4);
assert_eq!(matrix.height(), 3);
assert_eq!(matrix.width(), 4);
}
}