use crate::simt::device_future::DeviceFuture;
use crate::simt::device_operation::{DeviceOperation, ExecutionContext};
use crate::simt::error::{device_error, DeviceError};
use cuda_core::{CudaContext, CudaStream};
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
pub enum GlobalSchedulingPolicy {
RoundRobin(StreamPoolRoundRobin),
}
impl GlobalSchedulingPolicy {
pub fn as_scheduling_policy(&self) -> Result<&impl SchedulingPolicy, DeviceError> {
match self {
GlobalSchedulingPolicy::RoundRobin(rr) => Ok(rr),
}
}
}
impl SchedulingPolicy for GlobalSchedulingPolicy {
fn init(&mut self, ctx: &Arc<CudaContext>) -> Result<(), DeviceError> {
match self {
GlobalSchedulingPolicy::RoundRobin(rr) => rr.init(ctx),
}
}
fn schedule<T: Send, O: DeviceOperation<Output = T>>(
&self,
op: O,
) -> Result<DeviceFuture<T, O>, DeviceError> {
match self {
GlobalSchedulingPolicy::RoundRobin(rr) => rr.schedule(op),
}
}
fn sync<T: Send, O: DeviceOperation<Output = T>>(&self, op: O) -> Result<T, DeviceError> {
match self {
GlobalSchedulingPolicy::RoundRobin(rr) => rr.sync(op),
}
}
}
impl SchedulingPolicy for Arc<GlobalSchedulingPolicy> {
fn init(&mut self, _ctx: &Arc<CudaContext>) -> Result<(), DeviceError> {
Err(DeviceError::Scheduling(
"Cannot initialize scheduling policy inside an Arc.".to_string(),
))
}
fn schedule<T: Send, O: DeviceOperation<Output = T>>(
&self,
op: O,
) -> Result<DeviceFuture<T, O>, DeviceError> {
match self.as_ref() {
GlobalSchedulingPolicy::RoundRobin(rr) => rr.schedule(op),
}
}
fn sync<T: Send, O: DeviceOperation<Output = T>>(&self, op: O) -> Result<T, DeviceError> {
match self.as_ref() {
GlobalSchedulingPolicy::RoundRobin(rr) => rr.sync(op),
}
}
}
pub trait SchedulingPolicy: Sync {
fn init(&mut self, ctx: &Arc<CudaContext>) -> Result<(), DeviceError>;
fn schedule<T: Send, O: DeviceOperation<Output = T>>(
&self,
op: O,
) -> Result<DeviceFuture<T, O>, DeviceError>;
fn sync<T: Send, O: DeviceOperation<Output = T>>(&self, op: O) -> Result<T, DeviceError>;
}
#[derive(Debug)]
pub struct StreamPoolRoundRobin {
device_id: usize,
next_stream_idx: AtomicUsize,
pub(crate) num_streams: usize,
pub(crate) stream_pool: Option<Vec<Arc<CudaStream>>>,
}
impl StreamPoolRoundRobin {
pub unsafe fn new(device_id: usize, num_streams: usize) -> Self {
Self {
device_id,
num_streams,
stream_pool: None,
next_stream_idx: AtomicUsize::new(0),
}
}
}
impl SchedulingPolicy for StreamPoolRoundRobin {
fn init(&mut self, ctx: &Arc<CudaContext>) -> Result<(), DeviceError> {
let mut pool = vec![];
for _ in 0..self.num_streams {
pool.push(ctx.new_stream()?);
}
self.stream_pool = Some(pool);
Ok(())
}
fn sync<T: Send, O: DeviceOperation<Output = T>>(&self, op: O) -> Result<T, DeviceError> {
let idx = self
.next_stream_idx
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
% self.num_streams;
let pool = self
.stream_pool
.as_ref()
.ok_or_else(|| device_error(self.device_id, "Stream pool not initialized."))?;
op.sync_on(&pool[idx])
}
fn schedule<T: Send, O: DeviceOperation<Output = T>>(
&self,
op: O,
) -> Result<DeviceFuture<T, O>, DeviceError> {
let idx = self
.next_stream_idx
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
% self.num_streams;
let pool = self
.stream_pool
.as_ref()
.ok_or_else(|| device_error(self.device_id, "Stream pool not initialized."))?;
Ok(DeviceFuture {
device_operation: Some(op),
execution_context: Some(ExecutionContext::new(Arc::clone(&pool[idx]))),
result: None,
error: None,
state: Default::default(),
callback_state: None,
})
}
}
#[derive(Debug)]
pub struct SingleStream {
pub stream: Option<Arc<CudaStream>>,
}
impl SingleStream {
pub unsafe fn new() -> Self {
Self { stream: None }
}
}
impl SchedulingPolicy for SingleStream {
fn init(&mut self, ctx: &Arc<CudaContext>) -> Result<(), DeviceError> {
self.stream = Some(ctx.new_stream()?);
Ok(())
}
fn schedule<T: Send, O: DeviceOperation<Output = T>>(
&self,
op: O,
) -> Result<DeviceFuture<T, O>, DeviceError> {
let stream = self.stream.as_ref().unwrap();
Ok(DeviceFuture {
device_operation: Some(op),
execution_context: Some(ExecutionContext::new(Arc::clone(stream))),
result: None,
error: None,
state: Default::default(),
callback_state: None,
})
}
fn sync<T: Send, O: DeviceOperation<Output = T>>(&self, op: O) -> Result<T, DeviceError> {
let stream = self.stream.as_ref().unwrap();
op.sync_on(stream)
}
}