use cubecl_common::profile::Duration;
use cubecl_server::device_events::EventApi;
use cubecl_server::driver::DriverError;
use cudarc::driver::result::{event, stream};
use cudarc::driver::sys::{CUevent, CUevent_flags, CUevent_wait_flags, CUresult, CUstream};
pub type Fence = cubecl_server::device_events::EventFence<Cuda>;
pub type EventProfiler = cubecl_server::device_events::EventProfiler<Cuda>;
pub struct Cuda;
pub struct CudaEvent(CUevent);
unsafe impl Send for CudaEvent {}
unsafe impl Sync for CudaEvent {}
impl EventApi for Cuda {
type Event = CudaEvent;
type Stream = CUstream;
const BACKEND: &'static str = "CUDA";
fn event_create() -> Result<Self::Event, DriverError> {
let sys = named(
"cuEventCreate",
event::create(CUevent_flags::CU_EVENT_DEFAULT),
)?;
Ok(CudaEvent(sys))
}
fn event_destroy(event: &mut Self::Event) -> Result<(), DriverError> {
named("cuEventDestroy", unsafe { event::destroy(event.0) })
}
fn event_record(event: &Self::Event, stream: Self::Stream) -> Result<(), DriverError> {
named("cuEventRecord", unsafe { event::record(event.0, stream) })
}
fn event_wait(event: &Self::Event) -> Result<(), DriverError> {
named("cuEventSynchronize", unsafe { event::synchronize(event.0) })
}
fn event_elapsed(start: &Self::Event, end: &Self::Event) -> Result<Duration, DriverError> {
let ms = named("cuEventElapsedTime", unsafe {
event::elapsed(start.0, end.0)
})?;
Ok(Duration::from_secs_f64((ms as f64 / 1000.0).max(0.0)))
}
fn stream_wait_event(stream: Self::Stream, event: &Self::Event) -> Result<(), DriverError> {
named("cuStreamWaitEvent", unsafe {
stream::wait_event(stream, event.0, CUevent_wait_flags::CU_EVENT_WAIT_DEFAULT)
})
}
fn stream_synchronize(stream: Self::Stream) -> Result<(), DriverError> {
named("cuStreamSynchronize", unsafe {
stream::synchronize(stream)
})
}
fn stream_create_non_blocking() -> Result<Self::Stream, DriverError> {
named(
"cuStreamCreate",
stream::create(stream::StreamKind::NonBlocking),
)
}
fn stream_destroy(stream: Self::Stream) -> Result<(), DriverError> {
named("cuStreamDestroy", unsafe { stream::destroy(stream) })
}
}
fn named<T>(
op: &'static str,
result: Result<T, cudarc::driver::DriverError>,
) -> Result<T, DriverError> {
result.map_err(|err| driver_error(op, err))
}
pub(crate) fn driver_error(op: &'static str, err: cudarc::driver::DriverError) -> DriverError {
match poisons_device(err.0) {
true => DriverError::poisoned(op, err.0 as u32),
false => DriverError::new(op, err.0 as u32),
}
}
pub(crate) fn poisons_device(status: CUresult) -> bool {
matches!(
status,
CUresult::CUDA_ERROR_ECC_UNCORRECTABLE
| CUresult::CUDA_ERROR_ILLEGAL_ADDRESS
| CUresult::CUDA_ERROR_LAUNCH_TIMEOUT
| CUresult::CUDA_ERROR_CONTEXT_IS_DESTROYED
| CUresult::CUDA_ERROR_ASSERT
| CUresult::CUDA_ERROR_HARDWARE_STACK_ERROR
| CUresult::CUDA_ERROR_ILLEGAL_INSTRUCTION
| CUresult::CUDA_ERROR_MISALIGNED_ADDRESS
| CUresult::CUDA_ERROR_INVALID_ADDRESS_SPACE
| CUresult::CUDA_ERROR_INVALID_PC
| CUresult::CUDA_ERROR_LAUNCH_FAILED
)
}