diol_cbind/
lib.rs

1use std::any::TypeId;
2use std::ffi::c_char;
3use std::ffi::c_void;
4use std::fmt;
5
6use diol::config::BenchConfig;
7use diol::config::PlotMetric;
8use diol::traits::Arg;
9use diol::traits::Monotonicity;
10use diol::traits::Register;
11use diol::Bench;
12use diol::Bencher;
13use diol::PlotArg;
14
15fn nounwind<R>(f: impl FnOnce() -> R) -> R {
16    struct PanicDrop;
17    impl Drop for PanicDrop {
18        fn drop(&mut self) {
19            panic!();
20        }
21    }
22    let guard = PanicDrop;
23    let r = f();
24    std::mem::forget(guard);
25    r
26}
27
28#[derive(Copy, Clone)]
29#[repr(C)]
30pub struct LibDiolPlotArg {
31    pub n: usize,
32}
33
34pub struct LibDiolResult {
35    __private: (),
36}
37
38pub struct LibDiolBench {
39    __private: (),
40}
41
42pub struct LibDiolBencher {
43    __private: (),
44}
45
46#[derive(Copy, Clone)]
47#[repr(C)]
48pub struct LibDiolStringUtf8Ref {
49    pub data: *const c_char,
50    pub len: usize,
51}
52
53#[derive(Copy, Clone)]
54#[repr(C)]
55pub struct LibDiolStringUtf8 {
56    pub data: *mut c_char,
57    pub len: usize,
58    pub dealloc: unsafe extern "C" fn(*mut c_char, usize),
59}
60
61struct StringUtf8 {
62    pub data: *mut c_char,
63    pub len: usize,
64    pub dealloc: unsafe extern "C" fn(*mut c_char, usize),
65}
66
67impl Drop for StringUtf8 {
68    fn drop(&mut self) {
69        unsafe { (self.dealloc)(self.data, self.len) }
70    }
71}
72
73impl LibDiolStringUtf8Ref {
74    unsafe fn to_str(self) -> Option<&'static str> {
75        if self.len == usize::MAX {
76            None
77        } else if self.data.is_null() {
78            equator::assert!(self.len == 0);
79            Some("")
80        } else {
81            Some(
82                std::str::from_utf8(std::slice::from_raw_parts(self.data as *const u8, self.len))
83                    .unwrap(),
84            )
85        }
86    }
87}
88
89#[derive(Copy, Clone)]
90#[repr(u8)]
91pub enum LibDiolMonotonicity {
92    None,
93    HigherIsBetter,
94    LowerIsBetter,
95}
96
97/// colors to use for the generated plot
98#[derive(Copy, Clone)]
99pub enum LibDiolPlotColors {
100    CubehelixDefault,
101    Turbo,
102    Spectral,
103    Viridis,
104    Magma,
105    Inferno,
106    Plasma,
107    Cividis,
108    Warm,
109    Cool,
110}
111
112#[repr(C)]
113pub struct LibDiolConfig {
114    __private: (),
115}
116
117#[no_mangle]
118pub unsafe extern "C" fn libdiol_config_drop(config: *mut LibDiolConfig) {
119    nounwind(|| {
120        if !config.is_null() {
121            drop(Box::from_raw(config as *mut BenchConfig));
122        }
123    })
124}
125
126#[no_mangle]
127pub unsafe extern "C" fn libdiol_bench_drop(bench: *mut LibDiolBench) {
128    nounwind(|| {
129        if !bench.is_null() {
130            drop(Box::from_raw(bench as *mut Bench));
131        }
132    })
133}
134
135#[no_mangle]
136pub unsafe extern "C" fn libdiol_config_from_args() -> *mut LibDiolConfig {
137    nounwind(|| Box::into_raw(Box::new(BenchConfig::from_args())) as *mut _)
138}
139
140#[no_mangle]
141pub unsafe extern "C" fn libdiol_bench_from_config(
142    config: *const LibDiolConfig,
143) -> *mut LibDiolBench {
144    nounwind(|| Box::into_raw(Box::new(Bench::new(&*(config as *const BenchConfig)))) as *mut _)
145}
146
147#[no_mangle]
148pub unsafe extern "C" fn libdiol_bencher_bench(
149    bencher: *mut LibDiolBencher,
150    f: unsafe extern "C" fn(*mut c_void),
151    data: *mut c_void,
152) {
153    nounwind(|| {
154        let bencher = std::ptr::read(bencher as *mut Bencher<'_>);
155        bencher.bench(|| f(data));
156    })
157}
158
159#[no_mangle]
160pub unsafe extern "C" fn libdiol_config_set_metric(
161    config: *mut LibDiolConfig,
162    metric_name: LibDiolStringUtf8Ref,
163    metric_monotonicity: LibDiolMonotonicity,
164    metric: extern "C" fn(usize, f64) -> f64,
165) {
166    let config = &mut *(config as *mut BenchConfig);
167
168    #[derive(Clone)]
169    struct FfiMetric {
170        name: String,
171        monotonicity: Monotonicity,
172        metric: extern "C" fn(usize, f64) -> f64,
173    }
174
175    impl diol::traits::PlotMetric for FfiMetric {
176        fn compute(&self, arg: PlotArg, time: diol::Picoseconds) -> f64 {
177            (self.metric)(arg.0, time.to_secs())
178        }
179
180        fn monotonicity(&self) -> Monotonicity {
181            self.monotonicity
182        }
183
184        fn name(&self) -> &str {
185            &self.name
186        }
187    }
188
189    config.plot_metric = PlotMetric::new(FfiMetric {
190        name: metric_name.to_str().unwrap().to_string(),
191        monotonicity: match metric_monotonicity {
192            LibDiolMonotonicity::None => Monotonicity::None,
193            LibDiolMonotonicity::HigherIsBetter => Monotonicity::HigherIsBetter,
194            LibDiolMonotonicity::LowerIsBetter => Monotonicity::LowerIsBetter,
195        },
196        metric,
197    });
198}
199
200#[no_mangle]
201pub unsafe extern "C" fn libdiol_bench_register(
202    bench: *mut LibDiolBench,
203    func_names: *const LibDiolStringUtf8Ref,
204    func_data: *const *const c_void,
205    funcs: *const unsafe extern "C" fn(
206        func_data: *const c_void,
207        bencher: *mut LibDiolBencher,
208        arg: *mut c_void,
209    ),
210    n_funcs: usize,
211    args: *const *const c_void,
212    n_args: usize,
213    arg_clone: unsafe extern "C" fn(*const c_void) -> *mut c_void,
214    arg_drop: unsafe extern "C" fn(*mut c_void),
215    arg_print: unsafe extern "C" fn(*const c_void) -> LibDiolStringUtf8,
216    is_plot_arg: bool,
217) {
218    nounwind(|| {
219        let bench = &mut *(bench as *mut Bench);
220        let func_names = std::slice::from_raw_parts(func_names, n_funcs);
221        let func_data = std::slice::from_raw_parts(func_data, n_funcs);
222        let funcs = std::slice::from_raw_parts(funcs, n_funcs);
223        if is_plot_arg {
224            let args = std::slice::from_raw_parts(args, n_args);
225            bench.register_many_dyn(
226                func_names
227                    .iter()
228                    .map(|s| unsafe { s.to_str().unwrap().to_string() })
229                    .collect(),
230                funcs
231                    .iter()
232                    .zip(func_data)
233                    .map(|(&f, &data)| {
234                        Box::new(move |bencher: diol::Bencher<'_>, arg: Box<dyn Arg>| {
235                            let arg = (&*arg) as *const dyn Arg as *const PlotArg;
236                            let arg = *arg;
237                            f(
238                                data,
239                                (&mut { bencher }) as *mut diol::Bencher<'_> as *mut LibDiolBencher,
240                                (&mut { arg }) as *mut PlotArg as *mut c_void,
241                            );
242                        }) as Box<dyn Register<Box<dyn Arg>>>
243                    })
244                    .collect(),
245                TypeId::of::<PlotArg>(),
246                args.iter()
247                    .copied()
248                    .map(|arg| Box::new(*(arg as *const PlotArg)) as Box<dyn Arg>)
249                    .collect(),
250            );
251        } else {
252            struct DynArg {
253                data: *mut c_void,
254                clone: unsafe extern "C" fn(*const c_void) -> *mut c_void,
255                drop: unsafe extern "C" fn(*mut c_void),
256                print: unsafe extern "C" fn(*const c_void) -> LibDiolStringUtf8,
257            }
258
259            impl Clone for DynArg {
260                fn clone(&self) -> Self {
261                    Self {
262                        data: unsafe { (self.clone)(self.data) },
263                        clone: self.clone,
264                        drop: self.drop,
265                        print: self.print,
266                    }
267                }
268            }
269            impl Drop for DynArg {
270                fn drop(&mut self) {
271                    unsafe { (self.drop)(self.data) }
272                }
273            }
274
275            impl fmt::Debug for DynArg {
276                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277                    let LibDiolStringUtf8 { data, len, dealloc } =
278                        unsafe { (self.print)(self.data) };
279                    let str = StringUtf8 { data, len, dealloc };
280                    let str = LibDiolStringUtf8Ref {
281                        data: str.data,
282                        len: str.len,
283                    };
284                    f.write_str(unsafe { str.to_str().unwrap() })
285                }
286            }
287
288            let args = std::slice::from_raw_parts(args, n_args);
289            bench.register_many_dyn(
290                func_names
291                    .iter()
292                    .map(|s| unsafe { s.to_str().unwrap().to_string() })
293                    .collect(),
294                funcs
295                    .iter()
296                    .zip(func_data)
297                    .map(|(&f, &data)| {
298                        Box::new(move |bencher: diol::Bencher<'_>, arg: Box<dyn Arg>| {
299                            let arg = (&*arg) as *const dyn Arg as *const DynArg;
300                            let arg = (*arg).data;
301                            f(
302                                data,
303                                (&mut { bencher }) as *mut diol::Bencher<'_> as *mut LibDiolBencher,
304                                arg,
305                            );
306                        }) as Box<dyn Register<Box<dyn Arg>>>
307                    })
308                    .collect(),
309                TypeId::of::<DynArg>(),
310                args.iter()
311                    .copied()
312                    .map(|arg| DynArg {
313                        data: arg_clone(arg),
314                        clone: arg_clone,
315                        drop: arg_drop,
316                        print: arg_print,
317                    })
318                    .map(|arg| Box::new(arg) as Box<dyn Arg>)
319                    .collect(),
320            );
321        }
322    })
323}
324
325#[no_mangle]
326pub unsafe extern "C" fn libdiol_bench_run(bench: *mut LibDiolBench) {
327    nounwind(|| {
328        let bench = &mut *(bench as *mut Bench);
329        bench.run().unwrap();
330    })
331}
332
333#[cfg(test)]
334mod tests {
335    use std::ptr::null;
336
337    use super::*;
338
339    #[test]
340    fn test_bindings() {
341        unsafe {
342            let config = libdiol_config_from_args();
343            let bench = libdiol_bench_from_config(config);
344            libdiol_config_drop(config);
345
346            unsafe extern "C" fn foo(
347                _: *const c_void,
348                bencher: *mut LibDiolBencher,
349                arg: *mut c_void,
350            ) {
351                unsafe extern "C" fn f(data: *mut c_void) {
352                    let data = &mut *(data as *mut Vec<f64>);
353                    for x in data {
354                        *x *= 2.0;
355                    }
356                }
357
358                let arg = *(arg as *const u32);
359                let mut data = vec![0.0; arg as usize];
360                let data = (&mut data) as *mut _ as *mut c_void;
361                libdiol_bencher_bench(bencher, f, data);
362            }
363            unsafe extern "C" fn bar(
364                _: *const c_void,
365                bencher: *mut LibDiolBencher,
366                arg: *mut c_void,
367            ) {
368                unsafe extern "C" fn f(data: *mut c_void) {
369                    let data = &mut *(data as *mut Vec<f64>);
370                    for x in data {
371                        *x *= 0.1;
372                    }
373                }
374
375                let arg = *(arg as *const u32);
376                let mut data = vec![0.0; arg as usize];
377                let data = (&mut data) as *mut _ as *mut c_void;
378                libdiol_bencher_bench(bencher, f, data);
379            }
380
381            let names = ["foo", "bar"];
382            libdiol_bench_register(
383                bench,
384                names
385                    .map(|str| LibDiolStringUtf8Ref {
386                        data: str.as_ptr() as *const i8,
387                        len: str.len(),
388                    })
389                    .as_ptr(),
390                [null(), null()].as_ptr(),
391                [foo, bar].as_ptr(),
392                2,
393                [
394                    (&1u32) as *const u32 as *const c_void,
395                    (&3u32) as *const u32 as *const c_void,
396                    (&9u32) as *const u32 as *const c_void,
397                ]
398                .as_ptr(),
399                3,
400                {
401                    unsafe extern "C" fn f(x: *const c_void) -> *mut c_void {
402                        Box::into_raw(Box::new(*(x as *const u32))) as *mut c_void
403                    }
404                    f
405                },
406                {
407                    unsafe extern "C" fn f(_: *mut c_void) {}
408                    f
409                },
410                {
411                    unsafe extern "C" fn f(x: *const c_void) -> LibDiolStringUtf8 {
412                        let x = *(x as *const u32);
413                        let str = Box::into_raw(format!("{x}").into_bytes().into_boxed_slice());
414                        LibDiolStringUtf8 {
415                            data: str as *mut i8,
416                            len: (*str).len(),
417                            dealloc: {
418                                unsafe extern "C" fn f(ptr: *mut c_char, len: usize) {
419                                    drop(Box::from_raw(std::slice::from_raw_parts_mut(
420                                        ptr as *mut u8,
421                                        len,
422                                    )));
423                                }
424                                f
425                            },
426                        }
427                    }
428                    f
429                },
430                false,
431            );
432
433            libdiol_bench_drop(bench);
434        }
435    }
436}