Skip to main content

cubecl_server/memory_management/relocation/
streams.rs

1//! A relocation as a server runs it: across every stream, since any of them
2//! may use the memory that moves.
3
4use super::{RelocationNeed, RelocationReason};
5use crate::memory_management::ErrorGraph;
6use crate::server::ServerError;
7
8/// One stream, as relocating its memory needs it: what a runtime whose
9/// streams share one way to finish their work (a scheduler, say) supplies per
10/// stream, leaving the rest of [`RelocatingStreams`] to that shared way.
11pub trait RelocatableStream {
12    /// Whether any stream of the device records a graph.
13    fn recording(&self) -> bool;
14
15    /// Whether a growth left pages behind in the stream's memory.
16    fn has_outdated(&self) -> bool;
17
18    /// Whether the stream's memory wants a relocation, see
19    /// [`MemoryManagement::relocation_need`](crate::memory_management::MemoryManagement::relocation_need).
20    fn relocation_need(&self) -> RelocationNeed;
21
22    /// The bytes the stream's memory holds from the device.
23    fn bytes_allocated(&self) -> u64;
24
25    /// Move what the stream's memory holds on outdated pages, once every
26    /// stream's work is done.
27    fn relocate(&mut self, reason: RelocationReason, failures: &mut ErrorGraph);
28
29    /// Give back every page the stream's memory holds and nothing needs.
30    fn cleanup_memory(&mut self, failures: &mut ErrorGraph);
31}
32
33/// A runtime's streams, as relocating one stream's memory needs them.
34///
35/// A relocation moves memory any stream may still read or write, so every
36/// stream's work is done before a byte moves; and nothing moves while a graph
37/// records, since the pages it touches keep their addresses until it seals.
38/// The runtime supplies each step. The order they run in lives here, once.
39pub trait RelocatingStreams {
40    /// Whether any stream is recording a graph.
41    fn recording(&mut self) -> bool;
42
43    /// Whether a growth left pages behind in the relocating stream's memory:
44    /// without any, a relocation has nothing to move and waits on nothing.
45    fn has_outdated(&mut self) -> bool;
46
47    /// Whether the relocating stream's memory wants a relocation, before the
48    /// bytes the device holds are known.
49    fn relocation_need(&mut self) -> RelocationNeed;
50
51    /// The bytes every stream's memory holds from the device.
52    fn bytes_allocated(&mut self) -> u64;
53
54    /// Get every stream's queued and submitted work done, or leave it to the
55    /// [`CopyQueue`](super::CopyQueue) to wait for before its first copy.
56    fn finish(&mut self);
57
58    /// Move what the relocating stream's memory holds on outdated pages.
59    fn relocate_memory(&mut self, reason: RelocationReason);
60
61    /// Whether a growth left pages behind in any stream's memory.
62    fn device_has_outdated(&mut self) -> bool;
63
64    /// Move what every stream's memory holds on outdated pages.
65    fn relocate_device_memory(&mut self, reason: RelocationReason);
66
67    /// Give back every page the relocating stream's memory holds and nothing
68    /// needs.
69    fn cleanup_memory(&mut self);
70
71    /// Refuse while any stream records a graph: the pages a recording
72    /// touches keep their addresses until it seals, and nothing may wait on
73    /// the device under it.
74    ///
75    /// What [`reclaim`](Self::reclaim) checks first, for a caller with work of
76    /// its own to refuse before it.
77    ///
78    /// # Errors
79    ///
80    /// [`ServerError`] naming the recording when a stream records.
81    fn refuse_while_recording(&mut self) -> Result<(), ServerError> {
82        if self.recording() {
83            return Err(ServerError::graph_state(
84                "memory_cleanup: a stream is recording a graph, whose pages keep their addresses \
85                 until it seals",
86            ));
87        }
88        Ok(())
89    }
90
91    /// Give back everything the relocating stream's memory holds and nothing
92    /// needs: what the outdated pages still hold moves first, while the
93    /// current pages keep the room it moves into, then every page left empty
94    /// goes back.
95    ///
96    /// # Errors
97    ///
98    /// Refused while any stream records a graph, see
99    /// [`refuse_while_recording`](Self::refuse_while_recording).
100    fn reclaim(&mut self) -> Result<(), ServerError> {
101        self.refuse_while_recording()?;
102        self.relocate(RelocationReason::Explicit);
103        self.cleanup_memory();
104        Ok(())
105    }
106
107    /// Relocate when the memory asks for it: while the device still has a
108    /// page to spare, or before the pools run out of page sizes.
109    fn relocate_when_wanted(&mut self) {
110        let need = self.relocation_need();
111        if need == RelocationNeed::Nothing || self.recording() {
112            return;
113        }
114        if let Some(reason) = need.reason(|| self.bytes_allocated()) {
115            self.finish();
116            self.relocate_memory(reason);
117        }
118    }
119
120    /// Relocate for `reason`, unless a graph records or nothing is outdated.
121    fn relocate(&mut self, reason: RelocationReason) {
122        if self.recording() || !self.has_outdated() {
123            return;
124        }
125        self.finish();
126        self.relocate_memory(reason);
127    }
128
129    /// Relocate every stream's memory for `reason`, unless a graph records or
130    /// nothing is outdated anywhere.
131    ///
132    /// What a capture does before its warmup: a recording may touch memory
133    /// any stream owns, and every page it touches is guarded for the graph's
134    /// life, where no relocation can empty it.
135    fn relocate_device(&mut self, reason: RelocationReason) {
136        if self.recording() || !self.device_has_outdated() {
137            return;
138        }
139        self.finish();
140        self.relocate_device_memory(reason);
141    }
142}