1use std::ffi::CString;
5
6use crate::error::check_status;
7use crate::raw;
8use crate::{MlxArray, MlxCError, MlxDtype, MlxStream};
9
10pub fn metallib_path() -> Result<String, MlxCError> {
15 let operation: &'static str = "read the MLX metallib path";
16 let mut output = crate::MlxString::empty();
17 let status = unsafe { raw::mlx_metal_get_metallib_path(output.raw_mut()) };
20 check_status(status, operation)?;
21 output.require_populated(operation)?;
22 Ok(output.to_string_lossy())
23}
24
25pub fn metal_is_available() -> Result<bool, MlxCError> {
30 let operation: &'static str = "query MLX Metal availability";
31 let mut available = false;
32 let status = unsafe { raw::mlx_metal_is_available(&mut available) };
34 check_status(status, operation)?;
35 Ok(available)
36}
37
38pub fn set_wired_limit(limit_bytes: usize) -> Result<usize, MlxCError> {
46 let operation: &'static str = "set the MLX wired memory limit";
47 let mut previous_limit: usize = 0;
48 let status = unsafe { raw::mlx_set_wired_limit(&mut previous_limit, limit_bytes) };
51 check_status(status, operation)?;
52 Ok(previous_limit)
53}
54
55pub fn cuda_is_available() -> Result<bool, MlxCError> {
60 let operation: &'static str = "query MLX CUDA availability";
61 let mut available = false;
62 let status = unsafe { raw::mlx_cuda_is_available(&mut available) };
64 check_status(status, operation)?;
65 Ok(available)
66}
67
68fn backend_argument(backend: &str, operation: &'static str) -> Result<CString, MlxCError> {
70 CString::new(backend).map_err(|_| MlxCError {
71 operation,
72 description: "backend name contains an interior null byte".to_owned(),
73 })
74}
75
76pub fn distributed_is_available(backend: &str) -> Result<bool, MlxCError> {
81 let operation: &'static str = "query MLX distributed availability";
82 let backend_argument = backend_argument(backend, operation)?;
83 let available = unsafe { raw::mlx_distributed_is_available(backend_argument.as_ptr()) };
85 Ok(available)
86}
87
88pub fn initialize_distributed(
93 strict: bool,
94 backend: &str,
95) -> Result<MlxDistributedGroup, MlxCError> {
96 let operation: &'static str = "initialize MLX distributed";
97 let backend_argument = backend_argument(backend, operation)?;
98 let mut output = MlxDistributedGroup::empty();
99 let status =
102 unsafe { raw::mlx_distributed_init(output.raw_mut(), strict, backend_argument.as_ptr()) };
103 check_status(status, operation)?;
104 output.require_populated(operation)?;
105 Ok(output)
106}
107
108#[derive(Debug)]
110pub struct MlxDistributedGroup(raw::mlx_distributed_group);
111
112impl MlxDistributedGroup {
113 pub(crate) fn empty() -> Self {
115 let raw_group = unsafe { raw::mlx_distributed_group_new() };
117 Self(raw_group)
118 }
119
120 #[must_use]
122 pub fn rank(&self) -> i32 {
123 unsafe { raw::mlx_distributed_group_rank(self.0) }
125 }
126
127 #[must_use]
129 pub fn size(&self) -> i32 {
130 unsafe { raw::mlx_distributed_group_size(self.0) }
132 }
133
134 pub fn split(&self, color: i32, key: i32) -> Result<Self, MlxCError> {
139 let operation: &'static str = "split an MLX distributed group";
140 let mut output = Self::empty();
141 let status =
144 unsafe { raw::mlx_distributed_group_split(output.raw_mut(), self.0, color, key) };
145 check_status(status, operation)?;
146 output.require_populated(operation)?;
147 Ok(output)
148 }
149
150 pub(crate) fn raw_mut(&mut self) -> *mut raw::mlx_distributed_group {
151 &mut self.0
152 }
153
154 pub(crate) const fn raw(&self) -> raw::mlx_distributed_group {
155 self.0
156 }
157
158 fn require_populated(&self, operation: &'static str) -> Result<(), MlxCError> {
159 if self.0.ctx.is_null() {
160 return Err(MlxCError {
161 operation,
162 description: "MLX returned an empty distributed group handle".to_owned(),
163 });
164 }
165 Ok(())
166 }
167}
168
169impl Drop for MlxDistributedGroup {
170 fn drop(&mut self) {
171 unsafe {
174 raw::mlx_distributed_group_free(self.0);
175 }
176 }
177}
178
179impl MlxArray {
180 pub fn all_gather(
185 &self,
186 group: &MlxDistributedGroup,
187 stream: &MlxStream,
188 ) -> Result<Self, MlxCError> {
189 let operation: &'static str = "run the MLX distributed all-gather";
190 let mut output = Self::empty();
191 let status = unsafe {
194 raw::mlx_distributed_all_gather(output.raw_mut(), self.raw(), group.raw(), stream.raw())
195 };
196 check_status(status, operation)?;
197 output.require_populated(operation)?;
198 Ok(output)
199 }
200
201 pub fn all_max(
206 &self,
207 group: &MlxDistributedGroup,
208 stream: &MlxStream,
209 ) -> Result<Self, MlxCError> {
210 let operation: &'static str = "run the MLX distributed all-max";
211 let mut output = Self::empty();
212 let status = unsafe {
215 raw::mlx_distributed_all_max(output.raw_mut(), self.raw(), group.raw(), stream.raw())
216 };
217 check_status(status, operation)?;
218 output.require_populated(operation)?;
219 Ok(output)
220 }
221
222 pub fn all_min(
227 &self,
228 group: &MlxDistributedGroup,
229 stream: &MlxStream,
230 ) -> Result<Self, MlxCError> {
231 let operation: &'static str = "run the MLX distributed all-min";
232 let mut output = Self::empty();
233 let status = unsafe {
236 raw::mlx_distributed_all_min(output.raw_mut(), self.raw(), group.raw(), stream.raw())
237 };
238 check_status(status, operation)?;
239 output.require_populated(operation)?;
240 Ok(output)
241 }
242
243 pub fn all_sum(
248 &self,
249 group: &MlxDistributedGroup,
250 stream: &MlxStream,
251 ) -> Result<Self, MlxCError> {
252 let operation: &'static str = "run the MLX distributed all-sum";
253 let mut output = Self::empty();
254 let status = unsafe {
257 raw::mlx_distributed_all_sum(output.raw_mut(), self.raw(), group.raw(), stream.raw())
258 };
259 check_status(status, operation)?;
260 output.require_populated(operation)?;
261 Ok(output)
262 }
263
264 pub fn sum_scatter(
269 &self,
270 group: &MlxDistributedGroup,
271 stream: &MlxStream,
272 ) -> Result<Self, MlxCError> {
273 let operation: &'static str = "run the MLX distributed sum-scatter";
274 let mut output = Self::empty();
275 let status = unsafe {
278 raw::mlx_distributed_sum_scatter(
279 output.raw_mut(),
280 self.raw(),
281 group.raw(),
282 stream.raw(),
283 )
284 };
285 check_status(status, operation)?;
286 output.require_populated(operation)?;
287 Ok(output)
288 }
289
290 pub fn send_to_rank(
295 &self,
296 destination: i32,
297 group: &MlxDistributedGroup,
298 stream: &MlxStream,
299 ) -> Result<Self, MlxCError> {
300 let operation: &'static str = "run the MLX distributed send";
301 let mut output = Self::empty();
302 let status = unsafe {
305 raw::mlx_distributed_send(
306 output.raw_mut(),
307 self.raw(),
308 destination,
309 group.raw(),
310 stream.raw(),
311 )
312 };
313 check_status(status, operation)?;
314 output.require_populated(operation)?;
315 Ok(output)
316 }
317
318 pub fn receive_from_rank(
323 shape: &[i32],
324 dtype: MlxDtype,
325 source: i32,
326 group: &MlxDistributedGroup,
327 stream: &MlxStream,
328 ) -> Result<Self, MlxCError> {
329 let operation: &'static str = "run the MLX distributed receive";
330 let mut output = Self::empty();
331 let status = unsafe {
334 raw::mlx_distributed_recv(
335 output.raw_mut(),
336 shape.as_ptr(),
337 shape.len(),
338 dtype.to_raw(),
339 source,
340 group.raw(),
341 stream.raw(),
342 )
343 };
344 check_status(status, operation)?;
345 output.require_populated(operation)?;
346 Ok(output)
347 }
348
349 pub fn receive_like(
354 &self,
355 source: i32,
356 group: &MlxDistributedGroup,
357 stream: &MlxStream,
358 ) -> Result<Self, MlxCError> {
359 let operation: &'static str = "run the MLX distributed receive-like";
360 let mut output = Self::empty();
361 let status = unsafe {
364 raw::mlx_distributed_recv_like(
365 output.raw_mut(),
366 self.raw(),
367 source,
368 group.raw(),
369 stream.raw(),
370 )
371 };
372 check_status(status, operation)?;
373 output.require_populated(operation)?;
374 Ok(output)
375 }
376}