#[cfg(feature = "libraries")]
use cudarc::cublas::sys::{cublasMath_t, cublasOperation_t};
#[cfg(feature = "libraries")]
use cudarc::cublas::{Gemm, GemmConfig, StridedBatchedConfig};
#[cfg(feature = "libraries")]
use cudarc::cudnn::sys::cudnnMathType_t;
use cudarc::driver::{DevicePtr, DevicePtrMut};
#[cfg(feature = "libraries")]
use crate::error::{check_len, to_c_int};
use crate::{Error, Gpu, Library};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Math {
Fp32,
Tf32,
}
#[cfg(feature = "libraries")]
impl Math {
pub fn cublas(self) -> cublasMath_t {
match self {
Self::Fp32 => cublasMath_t::CUBLAS_DEFAULT_MATH,
Self::Tf32 => cublasMath_t::CUBLAS_TF32_TENSOR_OP_MATH,
}
}
pub fn cudnn(self) -> cudnnMathType_t {
match self {
Self::Fp32 => cudnnMathType_t::CUDNN_FMA_MATH,
Self::Tf32 => cudnnMathType_t::CUDNN_TENSOR_OP_MATH,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Sgemm {
pub m: usize,
pub n: usize,
pub k: usize,
pub a_transposed: bool,
pub b_transposed: bool,
pub alpha: f32,
pub beta: f32,
pub math: Math,
}
impl Sgemm {
pub fn new(m: usize, n: usize, k: usize) -> Self {
Self {
m,
n,
k,
a_transposed: false,
b_transposed: false,
alpha: 1.0,
beta: 0.0,
math: Math::Fp32,
}
}
#[cfg(feature = "libraries")]
fn column_major(&self) -> Result<GemmConfig<f32>, Error> {
let context = "sgemm";
let op = |transposed: bool| {
if transposed {
cublasOperation_t::CUBLAS_OP_T
} else {
cublasOperation_t::CUBLAS_OP_N
}
};
Ok(GemmConfig {
transa: op(self.b_transposed),
transb: op(self.a_transposed),
m: to_c_int(context, self.n)?,
n: to_c_int(context, self.m)?,
k: to_c_int(context, self.k)?,
alpha: self.alpha,
lda: to_c_int(context, if self.b_transposed { self.k } else { self.n })?,
ldb: to_c_int(context, if self.a_transposed { self.m } else { self.k })?,
beta: self.beta,
ldc: to_c_int(context, self.n)?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SgemmBatched {
pub gemm: Sgemm,
pub batch: usize,
pub stride_a: usize,
pub stride_b: usize,
pub stride_c: usize,
}
impl SgemmBatched {
pub fn packed(gemm: Sgemm, batch: usize) -> Self {
Self {
gemm,
batch,
stride_a: gemm.m * gemm.k,
stride_b: gemm.k * gemm.n,
stride_c: gemm.m * gemm.n,
}
}
#[cfg(feature = "libraries")]
fn span(&self, stride: usize, size: usize) -> usize {
match self.batch {
0 => 0,
batch => stride.saturating_mul(batch - 1).saturating_add(size),
}
}
}
impl Gpu {
pub fn sgemm<A, B, C>(&self, spec: Sgemm, a: &A, b: &B, c: &mut C) -> Result<(), Error>
where
A: DevicePtr<f32>,
B: DevicePtr<f32>,
C: DevicePtrMut<f32>,
{
self.profile().allow(Library::Cublas)?;
#[cfg(feature = "libraries")]
{
check_len("sgemm a", spec.m.saturating_mul(spec.k), a.len())?;
check_len("sgemm b", spec.k.saturating_mul(spec.n), b.len())?;
check_len("sgemm c", spec.m.saturating_mul(spec.n), c.len())?;
let config = spec.column_major()?;
let blas = self.blas_in(spec.math)?;
unsafe { blas.gemm(config, b, a, c) }?;
Ok(())
}
#[cfg(not(feature = "libraries"))]
{
let _ = (spec, a, b, c);
Err(Error::LibraryForbidden {
library: Library::Cublas,
})
}
}
pub fn sgemm_batched<A, B, C>(
&self,
spec: SgemmBatched,
a: &A,
b: &B,
c: &mut C,
) -> Result<(), Error>
where
A: DevicePtr<f32>,
B: DevicePtr<f32>,
C: DevicePtrMut<f32>,
{
self.profile().allow(Library::Cublas)?;
#[cfg(feature = "libraries")]
{
let gemm = spec.gemm;
let (a_size, b_size, c_size) = (gemm.m * gemm.k, gemm.k * gemm.n, gemm.m * gemm.n);
check_len("sgemm batched a", spec.span(spec.stride_a, a_size), a.len())?;
check_len("sgemm batched b", spec.span(spec.stride_b, b_size), b.len())?;
check_len("sgemm batched c", spec.span(spec.stride_c, c_size), c.len())?;
if spec.batch > 1 && spec.stride_c < c_size {
return Err(Error::Unsupported {
context: "sgemm batched",
reason: format!(
"output stride {} overlaps a {c_size}-element product",
spec.stride_c
),
});
}
let stride = |value: usize| {
i64::try_from(value).map_err(|_| Error::DimensionOverflow {
context: "sgemm batched stride",
value,
})
};
let config = StridedBatchedConfig {
gemm: gemm.column_major()?,
batch_size: to_c_int("sgemm batched", spec.batch)?,
stride_a: stride(spec.stride_b)?,
stride_b: stride(spec.stride_a)?,
stride_c: stride(spec.stride_c)?,
};
let blas = self.blas_in(gemm.math)?;
unsafe { blas.gemm_strided_batched(config, b, a, c) }?;
Ok(())
}
#[cfg(not(feature = "libraries"))]
{
let _ = (spec, a, b, c);
Err(Error::LibraryForbidden {
library: Library::Cublas,
})
}
}
}