use crate::error::CudaResult;
use crate::ffi::{CU_EVENT_DEFAULT, CUevent};
use crate::loader::try_driver;
use crate::stream::Stream;
pub struct Event {
raw: CUevent,
owner: crate::context::CtxOwner,
}
impl Event {
pub fn new() -> CudaResult<Self> {
Self::with_flags(CU_EVENT_DEFAULT)
}
pub fn with_flags(flags: u32) -> CudaResult<Self> {
let api = try_driver()?;
let mut raw = CUevent::default();
crate::cuda_call!((api.cu_event_create)(&mut raw, flags))?;
Ok(Self {
raw,
owner: crate::context::current_ctx_owner(),
})
}
pub fn record(&self, stream: &Stream) -> CudaResult<()> {
let api = try_driver()?;
crate::cuda_call!((api.cu_event_record)(self.raw, stream.raw()))
}
pub fn query(&self) -> CudaResult<bool> {
let api = try_driver()?;
let rc = unsafe { (api.cu_event_query)(self.raw) };
if rc == 0 {
Ok(true)
} else if rc == crate::ffi::CUDA_ERROR_NOT_READY {
Ok(false)
} else {
Err(crate::error::CudaError::from_raw(rc))
}
}
pub fn synchronize(&self) -> CudaResult<()> {
let api = try_driver()?;
crate::cuda_call!((api.cu_event_synchronize)(self.raw))
}
pub fn elapsed_time(start: &Event, end: &Event) -> CudaResult<f32> {
let api = try_driver()?;
let mut ms: f32 = 0.0;
crate::cuda_call!((api.cu_event_elapsed_time)(&mut ms, start.raw, end.raw))?;
Ok(ms)
}
#[inline]
pub fn raw(&self) -> CUevent {
self.raw
}
}
impl Drop for Event {
fn drop(&mut self) {
let map = crate::context::lock_live_ctxs();
if !crate::context::owner_is_live(&map, self.owner) {
return;
}
if let Ok(api) = try_driver() {
let rc = unsafe { (api.cu_event_destroy_v2)(self.raw) };
if rc != 0 {
tracing::warn!(
cuda_error = rc,
event = ?self.raw,
"cuEventDestroy_v2 failed during drop"
);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::Context;
use crate::device::Device;
use crate::ffi::CUdeviceptr;
#[test]
fn event_elapsed_time_on_real_device() {
let Ok(dev) = Device::get(0) else {
return;
};
let ctx = match Context::new(&dev) {
Ok(c) => std::sync::Arc::new(c),
Err(_) => return,
};
let stream = match Stream::new(&ctx) {
Ok(s) => s,
Err(_) => return,
};
let api = try_driver().expect("driver present");
let start = Event::new().expect("start event");
let end = Event::new().expect("end event");
const N: usize = 1 << 16;
let bytes = N * std::mem::size_of::<u32>();
let mut dptr: CUdeviceptr = 0;
crate::error::check(unsafe { (api.cu_mem_alloc_v2)(&mut dptr, bytes) }).expect("alloc");
let timed = || -> CudaResult<f32> {
start.record(&stream)?;
if let Some(memset_async) = api.cu_memset_d32_async {
crate::error::check(unsafe { memset_async(dptr, 0x7, N, stream.raw()) })?;
} else {
crate::error::check(unsafe { (api.cu_memset_d32_v2)(dptr, 0x7, N) })?;
}
end.record(&stream)?;
end.synchronize()?;
Event::elapsed_time(&start, &end)
};
let result = timed();
let _ = unsafe { (api.cu_mem_free_v2)(dptr) };
let ms = result.expect("elapsed time");
assert!(ms.is_finite(), "elapsed time must be finite, got {ms}");
assert!(ms >= 0.0, "elapsed time must be non-negative, got {ms}");
}
}