- Naive GEMM kernel: one thread per output element (F32 + BF16) - Tiled GEMM kernel: 32x32 shared memory tiles (F32 + BF16) - cuBLAS wrapper: cublasGemmEx with row-major trick - GemmBackend enum for runtime backend selection - CublasContext RAII handle - Made error::check public for cross-crate use - 17 GEMM tests: small/medium/rect sizes, all backends, F32+BF16 - Cross-backend consistency verified (naive vs tiled vs cuBLAS) - All 44 tests pass across all crates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
44 lines
1.1 KiB
Rust
44 lines
1.1 KiB
Rust
use crate::ffi;
|
|
use std::ffi::CStr;
|
|
use std::fmt;
|
|
|
|
#[derive(Debug)]
|
|
pub enum CudaError {
|
|
OutOfMemory,
|
|
InvalidDevice,
|
|
Raw { code: i32, message: String },
|
|
}
|
|
|
|
impl fmt::Display for CudaError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
CudaError::OutOfMemory => write!(f, "CUDA out of memory"),
|
|
CudaError::InvalidDevice => write!(f, "CUDA invalid device"),
|
|
CudaError::Raw { code, message } => write!(f, "CUDA error {code}: {message}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for CudaError {}
|
|
|
|
pub type Result<T> = std::result::Result<T, CudaError>;
|
|
|
|
pub fn check(code: i32) -> Result<()> {
|
|
if code == ffi::CUDA_SUCCESS {
|
|
return Ok(());
|
|
}
|
|
let message = unsafe {
|
|
let ptr = ffi::cudaGetErrorString(code);
|
|
if ptr.is_null() {
|
|
"unknown error".to_string()
|
|
} else {
|
|
CStr::from_ptr(ptr).to_string_lossy().into_owned()
|
|
}
|
|
};
|
|
Err(match code {
|
|
ffi::CUDA_ERROR_OUT_OF_MEMORY => CudaError::OutOfMemory,
|
|
101 => CudaError::InvalidDevice,
|
|
_ => CudaError::Raw { code, message },
|
|
})
|
|
}
|