cubecl_runtime/channel/
mpsc.rs

1use std::{sync::Arc, thread};
2
3use cubecl_common::benchmark::TimestampsResult;
4
5use super::ComputeChannel;
6use crate::{
7    memory_management::MemoryUsage,
8    server::{Binding, ComputeServer, CubeCount, Handle},
9    storage::BindingResource,
10    ExecutionMode,
11};
12
13/// Create a channel using a [multi-producer, single-consumer channel to communicate with
14/// the compute server spawn on its own thread.
15#[derive(Debug)]
16pub struct MpscComputeChannel<Server>
17where
18    Server: ComputeServer,
19{
20    state: Arc<MpscComputeChannelState<Server>>,
21}
22
23#[derive(Debug)]
24struct MpscComputeChannelState<Server>
25where
26    Server: ComputeServer,
27{
28    _handle: thread::JoinHandle<()>,
29    sender: async_channel::Sender<Message<Server>>,
30}
31
32type Callback<Response> = async_channel::Sender<Response>;
33
34enum Message<Server>
35where
36    Server: ComputeServer,
37{
38    Read(Vec<Binding>, Callback<Vec<Vec<u8>>>),
39    GetResource(Binding, Callback<BindingResource<Server>>),
40    Create(Vec<u8>, Callback<Handle>),
41    Empty(usize, Callback<Handle>),
42    ExecuteKernel((Server::Kernel, CubeCount, ExecutionMode), Vec<Binding>),
43    Flush,
44    SyncElapsed(Callback<TimestampsResult>),
45    Sync(Callback<()>),
46    GetMemoryUsage(Callback<MemoryUsage>),
47    EnableTimestamps,
48    DisableTimestamps,
49}
50
51impl<Server> MpscComputeChannel<Server>
52where
53    Server: ComputeServer + 'static,
54{
55    /// Create a new mpsc compute channel.
56    pub fn new(mut server: Server) -> Self {
57        let (sender, receiver) = async_channel::unbounded();
58
59        let _handle = thread::spawn(move || {
60            // Run the whole procedure as one blocking future. This is much simpler than trying
61            // to use some multithreaded executor.
62            cubecl_common::future::block_on(async {
63                while let Ok(message) = receiver.recv().await {
64                    match message {
65                        Message::Read(bindings, callback) => {
66                            let data = server.read(bindings).await;
67                            callback.send(data).await.unwrap();
68                        }
69                        Message::GetResource(binding, callback) => {
70                            let data = server.get_resource(binding);
71                            callback.send(data).await.unwrap();
72                        }
73                        Message::Create(data, callback) => {
74                            let handle = server.create(&data);
75                            callback.send(handle).await.unwrap();
76                        }
77                        Message::Empty(size, callback) => {
78                            let handle = server.empty(size);
79                            callback.send(handle).await.unwrap();
80                        }
81                        Message::ExecuteKernel(kernel, bindings) => unsafe {
82                            server.execute(kernel.0, kernel.1, bindings, kernel.2);
83                        },
84                        Message::SyncElapsed(callback) => {
85                            let duration = server.sync_elapsed().await;
86                            callback.send(duration).await.unwrap();
87                        }
88                        Message::Sync(callback) => {
89                            server.sync().await;
90                            callback.send(()).await.unwrap();
91                        }
92                        Message::Flush => {
93                            server.flush();
94                        }
95                        Message::GetMemoryUsage(callback) => {
96                            callback.send(server.memory_usage()).await.unwrap();
97                        }
98                        Message::EnableTimestamps => {
99                            server.enable_timestamps();
100                        }
101                        Message::DisableTimestamps => {
102                            server.disable_timestamps();
103                        }
104                    };
105                }
106            });
107        });
108
109        let state = Arc::new(MpscComputeChannelState { sender, _handle });
110
111        Self { state }
112    }
113}
114
115impl<Server: ComputeServer> Clone for MpscComputeChannel<Server> {
116    fn clone(&self) -> Self {
117        Self {
118            state: self.state.clone(),
119        }
120    }
121}
122
123impl<Server> ComputeChannel<Server> for MpscComputeChannel<Server>
124where
125    Server: ComputeServer + 'static,
126{
127    async fn read(&self, bindings: Vec<Binding>) -> Vec<Vec<u8>> {
128        let sender = self.state.sender.clone();
129        let (callback, response) = async_channel::unbounded();
130        sender
131            .send(Message::Read(bindings, callback))
132            .await
133            .unwrap();
134        handle_response(response.recv().await)
135    }
136
137    fn get_resource(&self, binding: Binding) -> BindingResource<Server> {
138        let (callback, response) = async_channel::unbounded();
139
140        self.state
141            .sender
142            .send_blocking(Message::GetResource(binding, callback))
143            .unwrap();
144
145        handle_response(response.recv_blocking())
146    }
147
148    fn create(&self, data: &[u8]) -> Handle {
149        let (callback, response) = async_channel::unbounded();
150
151        self.state
152            .sender
153            .send_blocking(Message::Create(data.to_vec(), callback))
154            .unwrap();
155
156        handle_response(response.recv_blocking())
157    }
158
159    fn empty(&self, size: usize) -> Handle {
160        let (callback, response) = async_channel::unbounded();
161        self.state
162            .sender
163            .send_blocking(Message::Empty(size, callback))
164            .unwrap();
165
166        handle_response(response.recv_blocking())
167    }
168
169    unsafe fn execute(
170        &self,
171        kernel: Server::Kernel,
172        count: CubeCount,
173        bindings: Vec<Binding>,
174        kind: ExecutionMode,
175    ) {
176        self.state
177            .sender
178            .send_blocking(Message::ExecuteKernel((kernel, count, kind), bindings))
179            .unwrap()
180    }
181
182    fn flush(&self) {
183        self.state.sender.send_blocking(Message::Flush).unwrap()
184    }
185
186    async fn sync(&self) {
187        let (callback, response) = async_channel::unbounded();
188        self.state
189            .sender
190            .send(Message::Sync(callback))
191            .await
192            .unwrap();
193        handle_response(response.recv().await)
194    }
195
196    async fn sync_elapsed(&self) -> TimestampsResult {
197        let (callback, response) = async_channel::unbounded();
198        self.state
199            .sender
200            .send(Message::SyncElapsed(callback))
201            .await
202            .unwrap();
203        handle_response(response.recv().await)
204    }
205
206    fn memory_usage(&self) -> crate::memory_management::MemoryUsage {
207        let (callback, response) = async_channel::unbounded();
208        self.state
209            .sender
210            .send_blocking(Message::GetMemoryUsage(callback))
211            .unwrap();
212        handle_response(response.recv_blocking())
213    }
214
215    fn enable_timestamps(&self) {
216        self.state
217            .sender
218            .send_blocking(Message::EnableTimestamps)
219            .unwrap();
220    }
221
222    fn disable_timestamps(&self) {
223        self.state
224            .sender
225            .send_blocking(Message::DisableTimestamps)
226            .unwrap();
227    }
228}
229
230fn handle_response<Response, Err: core::fmt::Debug>(response: Result<Response, Err>) -> Response {
231    match response {
232        Ok(val) => val,
233        Err(err) => panic!("Can't connect to the server correctly {err:?}"),
234    }
235}