use crate::compute::{
events::{Fence, driver_error},
storage::{cpu::PinnedMemoryStorage, gpu::GpuStorage},
};
use cubecl_core::{
MemoryConfiguration,
ir::MemoryDeviceProperties,
server::{BufferBinding, DeviceCaptures, Handle, ServerError},
};
use cubecl_server::driver::DriverError;
use cubecl_server::storage::PINNED_MEMORY_ALIGNMENT;
use cubecl_server::{
config::{memory::CudaAllocator, streaming::StreamPriority},
logging::ServerLogger,
memory_management::{
ErrorGraph, FailureId, MemoryAllocationMode, MemoryManagement, MemoryManagementOptions,
drop_queue,
},
metadata_cache::{MetadataCachePolicy, MetadataInfoCache},
stream::{EventStreamBackend, StreamCapture, StreamMemory},
};
use std::{mem::MaybeUninit, sync::Arc};
#[derive(Debug)]
pub struct Stream {
pub sys: cudarc::driver::sys::CUstream,
pub memory_management_gpu: MemoryManagement<GpuStorage>,
pub memory_management_cpu: MemoryManagement<PinnedMemoryStorage>,
pub drop_queue: drop_queue::PendingDropQueue<Fence>,
pub capturing: StreamCapture,
pub info_cache: MetadataInfoCache<Handle>,
}
impl StreamMemory for Stream {
fn failure(&self, binding: &BufferBinding) -> Option<FailureId> {
self.memory_management_gpu
.failure(&binding.memory, binding.range())
}
fn taint(&mut self, binding: &BufferBinding, failure: FailureId, failures: &mut ErrorGraph) {
self.memory_management_gpu
.taint(&binding.memory, binding.range(), failure, failures)
}
fn written(&mut self, binding: &BufferBinding, failures: &mut ErrorGraph) {
self.memory_management_gpu
.written(&binding.memory, binding.range(), failures)
}
}
#[derive(new, Debug)]
pub struct CudaStreamBackend {
mem_props: MemoryDeviceProperties,
mem_config: MemoryConfiguration,
mem_alignment: usize,
logger: Arc<ServerLogger>,
priority: StreamPriority,
allocator: CudaAllocator,
captures: DeviceCaptures,
}
pub(crate) fn create_cuda_stream(
priority: StreamPriority,
) -> Result<cudarc::driver::sys::CUstream, DriverError> {
use cudarc::driver::sys::{self, CUstream_flags};
let use_greatest = match priority {
StreamPriority::Default => {
return cudarc::driver::result::stream::create(
cudarc::driver::result::stream::StreamKind::NonBlocking,
)
.map_err(|err| driver_error("cuStreamCreate", err));
}
StreamPriority::High => true,
StreamPriority::Low => false,
};
let value = unsafe {
let mut least: i32 = 0;
let mut greatest: i32 = 0;
sys::cuCtxGetStreamPriorityRange(&mut least, &mut greatest)
.result()
.map_err(|err| driver_error("cuCtxGetStreamPriorityRange", err))?;
if use_greatest { greatest } else { least }
};
unsafe {
let mut stream = MaybeUninit::uninit();
sys::cuStreamCreateWithPriority(
stream.as_mut_ptr(),
CUstream_flags::CU_STREAM_NON_BLOCKING as u32,
value,
)
.result()
.map_err(|err| driver_error("cuStreamCreateWithPriority", err))?;
Ok(stream.assume_init())
}
}
impl EventStreamBackend for CudaStreamBackend {
type Stream = Stream;
type Event = Fence;
fn create_stream(&self) -> Result<Self::Stream, ServerError> {
let stream = create_cuda_stream(self.priority)?;
let storage = GpuStorage::new(self.mem_alignment, stream, self.allocator);
let memory_management_gpu = MemoryManagement::from_configuration(
storage,
&self.mem_props,
self.mem_config.clone(),
self.logger.clone(),
MemoryManagementOptions::new("Main GPU Memory"),
);
let memory_management_cpu = MemoryManagement::from_configuration(
PinnedMemoryStorage::new(),
&MemoryDeviceProperties::new(
self.mem_props.max_page_size,
PINNED_MEMORY_ALIGNMENT as u64,
),
self.mem_config.clone(),
self.logger.clone(),
MemoryManagementOptions::new("Pinned CPU Memory").mode(MemoryAllocationMode::Auto),
);
Ok(Stream {
sys: stream,
memory_management_gpu,
memory_management_cpu,
drop_queue: Default::default(),
capturing: StreamCapture::new(&self.captures),
info_cache: MetadataInfoCache::new(MetadataCachePolicy::default()),
})
}
fn flush(stream: &mut Self::Stream, _failures: &mut ErrorGraph) -> Self::Event {
Fence::new(stream.sys)
}
fn wait_event(stream: &mut Self::Stream, event: Self::Event) -> Result<(), ServerError> {
event.wait_async(stream.sys)
}
fn wait_event_sync(event: Self::Event) -> Result<(), ServerError> {
event.wait_sync()
}
fn handle_cursor(stream: &Self::Stream, binding: &BufferBinding) -> u64 {
stream
.memory_management_gpu
.get_cursor(binding.memory.clone())
.unwrap_or(u64::MAX)
}
}