1use crate::memory_management::relocation::{
2 RelocatableStream, RelocatingStreams, RelocationNeed, RelocationReason,
3};
4use crate::{
5 config::streaming::StreamingLogLevel,
6 logging::ServerLogger,
7 memory_management::{ErrorGraph, FailureId},
8 server::{BufferBinding, ServerError},
9 stream::{FailureStore, Failures, StreamFactory, StreamMemory, StreamPool},
10};
11use alloc::{format, sync::Arc, vec, vec::Vec};
12use cubecl_environment::stream::StreamId;
13
14pub trait SchedulerStreamBackend {
16 type Task: core::fmt::Debug;
18 type Stream: core::fmt::Debug + StreamMemory;
21 type Factory: StreamFactory<Stream = Self::Stream>;
23
24 fn enqueue(task: Self::Task, stream: &mut Self::Stream, failures: &mut ErrorGraph);
30 fn flush(stream: &mut Self::Stream, failures: &mut ErrorGraph);
32 fn factory(&mut self) -> &mut Self::Factory;
34 fn requires_isolation(_stream: &Self::Stream) -> bool {
41 false
42 }
43}
44
45#[derive(Debug)]
47pub struct SchedulerMultiStream<B: SchedulerStreamBackend> {
48 pool: StreamPool<SchedulerPoolMarker<B>>,
50 failures: Failures,
53 strategy: SchedulerStrategy,
55 max_tasks: usize,
57 pub logger: Arc<ServerLogger>,
59}
60
61#[derive(Debug)]
63pub enum SchedulerStrategy {
64 Interleave,
66 Sequential,
68}
69
70#[derive(Debug)]
72pub struct Stream<B: SchedulerStreamBackend> {
73 tasks: Vec<B::Task>,
75 stream: B::Stream,
77}
78
79impl<B: SchedulerStreamBackend> Stream<B> {
80 fn flush(&mut self) -> Vec<B::Task> {
82 let mut returned = Vec::with_capacity(self.tasks.capacity());
83 core::mem::swap(&mut returned, &mut self.tasks);
84 returned
85 }
86}
87
88#[derive(Debug)]
89pub struct SchedulerPoolMarker<B: SchedulerStreamBackend> {
93 backend: B,
94}
95
96impl<B: SchedulerStreamBackend> StreamMemory for Stream<B> {
97 fn failure(&self, binding: &BufferBinding) -> Option<FailureId> {
98 self.stream.failure(binding)
99 }
100
101 fn taint(&mut self, binding: &BufferBinding, failure: FailureId, failures: &mut ErrorGraph) {
102 self.stream.taint(binding, failure, failures)
103 }
104
105 fn written(&mut self, binding: &BufferBinding, failures: &mut ErrorGraph) {
106 self.stream.written(binding, failures)
107 }
108}
109
110impl<B: SchedulerStreamBackend> FailureStore for SchedulerMultiStream<B> {
111 type Factory = SchedulerPoolMarker<B>;
112
113 fn split(&mut self) -> (&mut StreamPool<Self::Factory>, &mut Failures) {
114 (&mut self.pool, &mut self.failures)
115 }
116
117 fn parts(&self) -> (&StreamPool<Self::Factory>, &Failures) {
118 (&self.pool, &self.failures)
119 }
120}
121
122impl<B: SchedulerStreamBackend> StreamFactory for SchedulerPoolMarker<B> {
123 type Stream = Stream<B>;
125
126 fn create(&mut self) -> Result<Self::Stream, ServerError> {
128 Ok(Stream {
129 tasks: Vec::new(),
130 stream: self.backend.factory().create()?,
132 })
133 }
134}
135
136#[derive(Debug)]
138pub struct SchedulerMultiStreamOptions {
139 pub max_streams: u8,
141 pub max_tasks: usize,
143 pub strategy: SchedulerStrategy,
145}
146
147pub struct SchedulerRelocation<'a, B: SchedulerStreamBackend> {
151 scheduler: &'a mut SchedulerMultiStream<B>,
152 stream_id: StreamId,
153}
154
155impl<B> RelocatingStreams for SchedulerRelocation<'_, B>
156where
157 B: SchedulerStreamBackend,
158 B::Stream: RelocatableStream,
159{
160 fn recording(&mut self) -> bool {
161 self.scheduler.stream(&self.stream_id).recording()
162 }
163
164 fn has_outdated(&mut self) -> bool {
165 self.scheduler.stream(&self.stream_id).has_outdated()
166 }
167
168 fn relocation_need(&mut self) -> RelocationNeed {
169 self.scheduler.stream(&self.stream_id).relocation_need()
170 }
171
172 fn bytes_allocated(&mut self) -> u64 {
173 self.scheduler
174 .streams()
175 .map(RelocatableStream::bytes_allocated)
176 .sum()
177 }
178
179 fn finish(&mut self) {
180 self.scheduler.flush_all();
181 }
182
183 fn relocate_memory(&mut self, reason: RelocationReason) {
184 let (stream, failures) = self.scheduler.stream_and_failures(&self.stream_id);
185 stream.relocate(reason, failures);
186 }
187
188 fn cleanup_memory(&mut self) {
189 let (stream, failures) = self.scheduler.stream_and_failures(&self.stream_id);
190 stream.cleanup_memory(failures);
191 }
192
193 fn device_has_outdated(&mut self) -> bool {
194 self.scheduler
195 .streams()
196 .any(RelocatableStream::has_outdated)
197 }
198
199 fn relocate_device_memory(&mut self, reason: RelocationReason) {
200 let stream_ids: Vec<_> = self.scheduler.stream_ids().collect();
201 for stream_id in stream_ids {
202 let (stream, failures) = self.scheduler.stream_and_failures(&stream_id);
203 if stream.has_outdated() {
204 stream.relocate(reason, failures);
205 }
206 }
207 }
208}
209
210impl<B: SchedulerStreamBackend> SchedulerMultiStream<B> {
211 pub fn new(
213 logger: Arc<ServerLogger>,
214 backend: B,
215 options: SchedulerMultiStreamOptions,
216 ) -> Self {
217 Self {
218 pool: StreamPool::new(SchedulerPoolMarker { backend }, options.max_streams, 0),
219 failures: Failures::new(logger.clone()),
220 max_tasks: options.max_tasks,
221 strategy: options.strategy,
222 logger,
223 }
224 }
225
226 pub fn stream(&mut self, stream_id: &StreamId) -> &mut B::Stream {
228 let stream = self.pool.get_mut(stream_id);
229 &mut stream.stream
230 }
231
232 pub fn stream_and_failures(
237 &mut self,
238 stream_id: &StreamId,
239 ) -> (&mut B::Stream, &mut ErrorGraph) {
240 let stream = self.pool.get_mut(stream_id);
241 (&mut stream.stream, self.failures.graph_mut())
242 }
243
244 pub fn backend_mut(&mut self) -> &mut B {
248 &mut self.pool.factory_mut().backend
249 }
250
251 pub fn streams(&self) -> impl Iterator<Item = &B::Stream> {
253 self.pool.streams().map(|s| &s.stream)
254 }
255
256 pub fn stream_ids(&self) -> impl Iterator<Item = StreamId> + '_ {
258 self.pool.stream_ids()
259 }
260
261 pub fn relocating(&mut self, stream_id: StreamId) -> SchedulerRelocation<'_, B> {
263 SchedulerRelocation {
264 scheduler: self,
265 stream_id,
266 }
267 }
268
269 pub fn flush_all(&mut self) {
273 let stream_ids: Vec<_> = self.stream_ids().collect();
274 self.execute_streams(stream_ids);
275 let graph = self.failures.graph_mut();
276 for stream in self.pool.streams_mut() {
277 B::flush(&mut stream.stream, graph);
278 }
279 }
280
281 pub fn register(&mut self, stream_id: StreamId, task: B::Task, args_streams: &[StreamId]) {
283 self.align_streams(stream_id, args_streams);
285
286 let current = self.pool.get_mut(&stream_id);
288 current.tasks.push(task);
289
290 if current.tasks.len() >= self.max_tasks {
292 self.execute_streams(vec![stream_id]);
293 }
294 }
295
296 pub(crate) fn align_streams(&mut self, stream_id: StreamId, args_streams: &[StreamId]) {
298 let mut to_flush = Vec::new();
299 let index = self.pool.stream_index(&stream_id);
301
302 for arg_stream in args_streams {
304 let index_stream = self.pool.stream_index(arg_stream);
305 if index != index_stream {
306 to_flush.push(*arg_stream);
307
308 self.logger.log_streaming(
309 |level| matches!(level, StreamingLogLevel::Full),
310 || format!("Binding on {} is shared on {}", arg_stream, stream_id),
311 );
312 }
313 }
314
315 if to_flush.is_empty() {
317 return;
318 }
319
320 self.logger.log_streaming(
321 |level| !matches!(level, StreamingLogLevel::Disabled),
322 || {
323 format!(
324 "Flushing streams {to_flush:?} before registering more tasks on {stream_id}"
325 )
326 },
327 );
328 self.execute_streams(to_flush);
330 }
331
332 pub fn execute_streams(&mut self, stream_ids: Vec<StreamId>) {
334 let mut indices = Vec::with_capacity(stream_ids.len());
335
336 for id in stream_ids {
338 let index = self.pool.stream_index(&id);
339 if !indices.contains(&index) {
340 indices.push(index);
341 }
342 }
343
344 let mut schedules = Vec::new();
348 let mut isolation = false;
349 for index in indices {
350 let stream = unsafe { self.pool.get_mut_index(index) }; isolation |= B::requires_isolation(&stream.stream);
352 let tasks = stream.flush();
353 let num_tasks = tasks.len();
354
355 schedules.push(Schedule {
356 tasks: tasks.into_iter(),
357 num_tasks,
358 stream_index: index,
359 });
360 }
361
362 if schedules.is_empty() {
364 return;
365 }
366
367 match self.strategy {
371 SchedulerStrategy::Interleave if !isolation => {
372 self.execute_schedules_interleave(schedules)
373 }
374 _ => self.execute_schedules_sequence(schedules),
375 }
376 }
377
378 fn execute_schedules_sequence(&mut self, schedules: Vec<Schedule<B>>) {
380 for schedule in schedules {
381 let stream = unsafe { self.pool.get_mut_index(schedule.stream_index) }; for task in schedule.tasks {
383 B::enqueue(task, &mut stream.stream, self.failures.graph_mut());
385 }
386
387 B::flush(&mut stream.stream, self.failures.graph_mut());
389 }
390 }
391
392 fn execute_schedules_interleave(&mut self, mut schedules: Vec<Schedule<B>>) {
399 for schedule in schedules.iter_mut().skip(1) {
401 let stream = unsafe { self.pool.get_mut_index(schedule.stream_index) };
402 B::flush(&mut stream.stream, self.failures.graph_mut());
403 }
404
405 let execution_index = schedules.first().expect("At least one stream").stream_index;
406 let stream = unsafe { self.pool.get_mut_index(execution_index) };
407
408 let num_tasks_max = schedules
410 .iter()
411 .map(|s| s.num_tasks)
412 .max()
413 .expect("At least one schedule");
414
415 for _ in 0..num_tasks_max {
417 for schedule in schedules.iter_mut() {
418 if let Some(task) = schedule.tasks.next() {
420 B::enqueue(task, &mut stream.stream, self.failures.graph_mut());
421 }
422 }
423 }
424
425 B::flush(&mut stream.stream, self.failures.graph_mut());
427 }
428}
429
430struct Schedule<B: SchedulerStreamBackend> {
432 tasks: alloc::vec::IntoIter<B::Task>,
434 num_tasks: usize,
436 stream_index: usize,
438}