1use std::ffi::CString;
9
10use crate::error::check_status;
11use crate::raw;
12use crate::{MlxArrayVector, MlxBindingsContext, MlxCError, MlxDtype, MlxStream};
13
14#[derive(Debug)]
16pub struct MlxCudaKernelConfig(raw::mlx_fast_cuda_kernel_config);
17
18impl MlxCudaKernelConfig {
19 pub fn new() -> Result<Self, MlxCError> {
21 const OPERATION: &str = "configure an MLX custom CUDA kernel";
22 let raw_config = unsafe { raw::mlx_fast_cuda_kernel_config_new() };
25 if raw_config.ctx.is_null() {
26 return Err(MlxCError {
27 operation: OPERATION,
28 description: "MLX returned an empty custom CUDA kernel config".to_owned(),
29 });
30 }
31 Ok(Self(raw_config))
32 }
33
34 pub fn add_output(&mut self, shape: &[i32], dtype: MlxDtype) -> Result<(), MlxCError> {
36 const OPERATION: &str = "configure an MLX custom CUDA kernel output";
37 let status = unsafe {
39 raw::mlx_fast_cuda_kernel_config_add_output_arg(
40 self.raw(),
41 shape.as_ptr(),
42 shape.len(),
43 dtype.to_raw(),
44 )
45 };
46 check_status(status, OPERATION)
47 }
48
49 pub fn set_grid(&mut self, grid: [i32; 3]) -> Result<(), MlxCError> {
51 const OPERATION: &str = "configure an MLX custom CUDA kernel grid";
52 let status = unsafe {
54 raw::mlx_fast_cuda_kernel_config_set_grid(self.raw(), grid[0], grid[1], grid[2])
55 };
56 check_status(status, OPERATION)
57 }
58
59 pub fn set_thread_group(&mut self, thread_group: [i32; 3]) -> Result<(), MlxCError> {
61 const OPERATION: &str = "configure an MLX custom CUDA kernel thread group";
62 let status = unsafe {
64 raw::mlx_fast_cuda_kernel_config_set_thread_group(
65 self.raw(),
66 thread_group[0],
67 thread_group[1],
68 thread_group[2],
69 )
70 };
71 check_status(status, OPERATION)
72 }
73
74 pub fn set_init_value(&mut self, initial_value: f32) -> Result<(), MlxCError> {
76 const OPERATION: &str = "initialize an MLX custom CUDA kernel output";
77 let status =
79 unsafe { raw::mlx_fast_cuda_kernel_config_set_init_value(self.raw(), initial_value) };
80 check_status(status, OPERATION)
81 }
82
83 pub fn set_verbose(&mut self, verbose: bool) -> Result<(), MlxCError> {
85 const OPERATION: &str = "configure MLX custom CUDA kernel logging";
86 let status = unsafe { raw::mlx_fast_cuda_kernel_config_set_verbose(self.raw(), verbose) };
88 check_status(status, OPERATION)
89 }
90
91 pub fn add_dtype_template_argument(
93 &mut self,
94 name: &str,
95 dtype: MlxDtype,
96 ) -> Result<(), MlxCError> {
97 const OPERATION: &str = "add an MLX custom CUDA kernel dtype template argument";
98 let name_argument = CString::new(name).map_err(|_| MlxCError {
99 operation: OPERATION,
100 description: "template argument name contains an interior null byte".to_owned(),
101 })?;
102 let status = unsafe {
104 raw::mlx_fast_cuda_kernel_config_add_template_arg_dtype(
105 self.raw(),
106 name_argument.as_ptr(),
107 dtype.to_raw(),
108 )
109 };
110 check_status(status, OPERATION)
111 }
112
113 pub fn add_int_template_argument(
115 &mut self,
116 name: &str,
117 argument: i32,
118 ) -> Result<(), MlxCError> {
119 const OPERATION: &str = "add an MLX custom CUDA kernel integer template argument";
120 let name_argument = CString::new(name).map_err(|_| MlxCError {
121 operation: OPERATION,
122 description: "template argument name contains an interior null byte".to_owned(),
123 })?;
124 let status = unsafe {
126 raw::mlx_fast_cuda_kernel_config_add_template_arg_int(
127 self.raw(),
128 name_argument.as_ptr(),
129 argument,
130 )
131 };
132 check_status(status, OPERATION)
133 }
134
135 pub fn add_bool_template_argument(
137 &mut self,
138 name: &str,
139 argument: bool,
140 ) -> Result<(), MlxCError> {
141 const OPERATION: &str = "add an MLX custom CUDA kernel boolean template argument";
142 let name_argument = CString::new(name).map_err(|_| MlxCError {
143 operation: OPERATION,
144 description: "template argument name contains an interior null byte".to_owned(),
145 })?;
146 let status = unsafe {
148 raw::mlx_fast_cuda_kernel_config_add_template_arg_bool(
149 self.raw(),
150 name_argument.as_ptr(),
151 argument,
152 )
153 };
154 check_status(status, OPERATION)
155 }
156
157 pub(crate) const fn raw(&self) -> raw::mlx_fast_cuda_kernel_config {
158 self.0
159 }
160}
161
162impl Drop for MlxCudaKernelConfig {
163 fn drop(&mut self) {
164 unsafe {
167 raw::mlx_fast_cuda_kernel_config_free(self.0);
168 }
169 }
170}
171
172#[derive(Debug)]
174pub struct MlxCudaKernel(raw::mlx_fast_cuda_kernel);
175
176impl MlxCudaKernel {
177 pub fn new(
182 kernel_name: &str,
183 input_names: &[&str],
184 output_names: &[&str],
185 kernel_header: &str,
186 kernel_source: &str,
187 ensure_row_contiguous: bool,
188 shared_memory_bytes: i32,
189 ) -> Result<Self, MlxCError> {
190 const OPERATION: &str = "compile an MLX custom CUDA kernel";
191 let kernel_name_argument = CString::new(kernel_name).map_err(|_| MlxCError {
192 operation: OPERATION,
193 description: "kernel name contains an interior null byte".to_owned(),
194 })?;
195 let input_names_vector = crate::types::vectors::MlxVectorString::from_strings(input_names)?;
196 let output_names_vector =
197 crate::types::vectors::MlxVectorString::from_strings(output_names)?;
198 let header_argument = CString::new(kernel_header).map_err(|_| MlxCError {
199 operation: OPERATION,
200 description: "kernel header contains an interior null byte".to_owned(),
201 })?;
202 let source_argument = CString::new(kernel_source).map_err(|_| MlxCError {
203 operation: OPERATION,
204 description: "kernel source contains an interior null byte".to_owned(),
205 })?;
206 let raw_kernel = unsafe {
210 raw::mlx_fast_cuda_kernel_new(
211 kernel_name_argument.as_ptr(),
212 input_names_vector.raw(),
213 output_names_vector.raw(),
214 source_argument.as_ptr(),
215 header_argument.as_ptr(),
216 ensure_row_contiguous,
217 shared_memory_bytes,
218 )
219 };
220 if raw_kernel.ctx.is_null() {
221 return Err(MlxCError {
222 operation: OPERATION,
223 description: "MLX returned an empty custom CUDA kernel handle".to_owned(),
224 });
225 }
226 Ok(Self(raw_kernel))
227 }
228
229 pub fn apply(
234 &self,
235 context: &MlxBindingsContext,
236 inputs: &MlxArrayVector,
237 config: &MlxCudaKernelConfig,
238 ) -> Result<MlxArrayVector, MlxCError> {
239 const OPERATION: &str = "apply an MLX custom CUDA kernel";
240 let mut outputs = MlxArrayVector::empty(OPERATION)?;
241 let status = unsafe {
244 raw::mlx_fast_cuda_kernel_apply(
245 outputs.raw_mut(),
246 self.0,
247 inputs.raw(),
248 config.raw(),
249 context.gpu_stream().raw(),
250 )
251 };
252 check_status(status, OPERATION)?;
253 Ok(outputs)
254 }
255}
256
257impl Drop for MlxCudaKernel {
258 fn drop(&mut self) {
259 unsafe {
262 raw::mlx_fast_cuda_kernel_free(self.0);
263 }
264 }
265}
266
267impl MlxStream {
268 pub fn apply_cuda_kernel(
273 &self,
274 kernel: &MlxCudaKernel,
275 inputs: &MlxArrayVector,
276 config: &MlxCudaKernelConfig,
277 ) -> Result<MlxArrayVector, MlxCError> {
278 const OPERATION: &str = "apply an MLX custom CUDA kernel on a stream";
279 let mut outputs = MlxArrayVector::empty(OPERATION)?;
280 let status = unsafe {
283 raw::mlx_fast_cuda_kernel_apply(
284 outputs.raw_mut(),
285 kernel.0,
286 inputs.raw(),
287 config.raw(),
288 self.raw(),
289 )
290 };
291 check_status(status, OPERATION)?;
292 Ok(outputs)
293 }
294}