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#[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}