use std::ffi::c_void;
use std::sync::Arc;
use cudarc::driver::CudaDevice;
use super::InferenceError;
#[link(name = "cudart")]
extern "C" {
fn cudaStreamCreate(stream: *mut *mut c_void) -> i32;
fn cudaStreamCreateWithFlags(stream: *mut *mut c_void, flags: u32) -> i32;
fn cudaStreamCreateWithPriority(stream: *mut *mut c_void, flags: u32, priority: i32) -> i32;
fn cudaStreamDestroy(stream: *mut c_void) -> i32;
fn cudaStreamSynchronize(stream: *mut c_void) -> i32;
fn cudaStreamWaitEvent(stream: *mut c_void, event: *mut c_void, flags: u32) -> i32;
fn cudaEventCreate(event: *mut *mut c_void) -> i32;
fn cudaEventCreateWithFlags(event: *mut *mut c_void, flags: u32) -> i32;
fn cudaEventDestroy(event: *mut c_void) -> i32;
fn cudaEventRecord(event: *mut c_void, stream: *mut c_void) -> i32;
fn cudaEventSynchronize(event: *mut c_void) -> i32;
fn cudaDeviceSynchronize() -> i32;
}
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum StreamFlags {
Default = 0x00,
NonBlocking = 0x01,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum EventFlags {
Default = 0x00,
DisableTiming = 0x02,
Interprocess = 0x04,
}
pub struct CudaEvent {
event: *mut c_void,
}
unsafe impl Send for CudaEvent {}
unsafe impl Sync for CudaEvent {}
impl CudaEvent {
pub fn new() -> Result<Self, InferenceError> {
let mut event: *mut c_void = std::ptr::null_mut();
let status = unsafe { cudaEventCreate(&mut event) };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaEventCreate failed with status {}",
status
)));
}
Ok(Self { event })
}
pub fn with_flags(flags: EventFlags) -> Result<Self, InferenceError> {
let mut event: *mut c_void = std::ptr::null_mut();
let status = unsafe { cudaEventCreateWithFlags(&mut event, flags as u32) };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaEventCreateWithFlags failed with status {}",
status
)));
}
Ok(Self { event })
}
pub fn record(&self, stream: &CudaStream) -> Result<(), InferenceError> {
let status = unsafe { cudaEventRecord(self.event, stream.stream) };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaEventRecord failed with status {}",
status
)));
}
Ok(())
}
pub fn synchronize(&self) -> Result<(), InferenceError> {
let status = unsafe { cudaEventSynchronize(self.event) };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaEventSynchronize failed with status {}",
status
)));
}
Ok(())
}
pub unsafe fn raw(&self) -> *mut c_void {
self.event
}
}
impl Drop for CudaEvent {
fn drop(&mut self) {
if !self.event.is_null() {
unsafe { cudaEventDestroy(self.event) };
}
}
}
pub struct CudaStream {
stream: *mut c_void,
}
unsafe impl Send for CudaStream {}
unsafe impl Sync for CudaStream {}
impl CudaStream {
pub fn new() -> Result<Self, InferenceError> {
let mut stream: *mut c_void = std::ptr::null_mut();
let status = unsafe { cudaStreamCreate(&mut stream) };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaStreamCreate failed with status {}",
status
)));
}
Ok(Self { stream })
}
pub fn with_flags(flags: StreamFlags) -> Result<Self, InferenceError> {
let mut stream: *mut c_void = std::ptr::null_mut();
let status = unsafe { cudaStreamCreateWithFlags(&mut stream, flags as u32) };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaStreamCreateWithFlags failed with status {}",
status
)));
}
Ok(Self { stream })
}
pub fn with_priority(priority: i32) -> Result<Self, InferenceError> {
let mut stream: *mut c_void = std::ptr::null_mut();
let status = unsafe {
cudaStreamCreateWithPriority(&mut stream, StreamFlags::NonBlocking as u32, priority)
};
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaStreamCreateWithPriority failed with status {}",
status
)));
}
Ok(Self { stream })
}
pub fn synchronize(&self) -> Result<(), InferenceError> {
let status = unsafe { cudaStreamSynchronize(self.stream) };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaStreamSynchronize failed with status {}",
status
)));
}
Ok(())
}
pub fn wait_event(&self, event: &CudaEvent) -> Result<(), InferenceError> {
let status = unsafe { cudaStreamWaitEvent(self.stream, event.event, 0) };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaStreamWaitEvent failed with status {}",
status
)));
}
Ok(())
}
pub unsafe fn raw(&self) -> *mut c_void {
self.stream
}
}
impl Drop for CudaStream {
fn drop(&mut self) {
if !self.stream.is_null() {
unsafe { cudaStreamDestroy(self.stream) };
}
}
}
pub struct StreamManager {
compute_stream: CudaStream,
memory_stream: CudaStream,
prefetch_stream: CudaStream,
compute_done: CudaEvent,
memory_done: CudaEvent,
device: Arc<CudaDevice>,
}
impl StreamManager {
pub fn new(device: Arc<CudaDevice>) -> Result<Self, InferenceError> {
let compute_stream = CudaStream::with_priority(0)?; let memory_stream = CudaStream::with_priority(1)?;
let prefetch_stream = CudaStream::with_priority(2)?;
let compute_done = CudaEvent::with_flags(EventFlags::DisableTiming)?;
let memory_done = CudaEvent::with_flags(EventFlags::DisableTiming)?;
Ok(Self {
compute_stream,
memory_stream,
prefetch_stream,
compute_done,
memory_done,
device,
})
}
pub fn compute(&self) -> &CudaStream {
&self.compute_stream
}
pub fn memory(&self) -> &CudaStream {
&self.memory_stream
}
pub fn prefetch(&self) -> &CudaStream {
&self.prefetch_stream
}
pub fn record_compute_done(&self) -> Result<(), InferenceError> {
self.compute_done.record(&self.compute_stream)
}
pub fn record_memory_done(&self) -> Result<(), InferenceError> {
self.memory_done.record(&self.memory_stream)
}
pub fn memory_wait_compute(&self) -> Result<(), InferenceError> {
self.memory_stream.wait_event(&self.compute_done)
}
pub fn compute_wait_memory(&self) -> Result<(), InferenceError> {
self.compute_stream.wait_event(&self.memory_done)
}
pub fn synchronize_all(&self) -> Result<(), InferenceError> {
self.compute_stream.synchronize()?;
self.memory_stream.synchronize()?;
self.prefetch_stream.synchronize()?;
Ok(())
}
pub fn synchronize_compute(&self) -> Result<(), InferenceError> {
self.compute_stream.synchronize()
}
pub unsafe fn compute_raw(&self) -> *mut c_void {
self.compute_stream.raw()
}
pub unsafe fn memory_raw(&self) -> *mut c_void {
self.memory_stream.raw()
}
pub fn device(&self) -> &Arc<CudaDevice> {
&self.device
}
}
pub fn device_synchronize() -> Result<(), InferenceError> {
let status = unsafe { cudaDeviceSynchronize() };
if status != 0 {
return Err(InferenceError::Kernel(format!(
"cudaDeviceSynchronize failed with status {}",
status
)));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stream_manager_types() {
assert_eq!(StreamFlags::Default as u32, 0);
assert_eq!(StreamFlags::NonBlocking as u32, 1);
assert_eq!(EventFlags::DisableTiming as u32, 2);
}
}