use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use dlpark::ffi::DLManagedTensor;
use dlpark::metadata::{BorrowedArray, BorrowedSlice, CopiedArray, CopiedSlice};
use dlpark::tensor::compact_strides_array;
use dlpark::{Builder, OpaqueContext};
use std::ffi::c_void;
struct NoopContext;
unsafe impl OpaqueContext for NoopContext {
fn into_raw(self) -> *mut c_void {
std::ptr::null_mut()
}
unsafe fn drop_raw(_raw: *mut c_void) {}
}
fn context() -> NoopContext {
NoopContext
}
fn bench_ndim<const N: usize>(c: &mut Criterion) {
let shape: [i64; N] = [1; N];
let strides = compact_strides_array(shape).unwrap();
let mut group = c.benchmark_group(format!("builder/ndim={N}"));
group.bench_function(BenchmarkId::new("copied_array", N), |b| {
b.iter(|| {
let dlpack = Builder::new(
context(),
CopiedArray::new(std::hint::black_box(&shape), std::hint::black_box(&strides)),
)
.build::<DLManagedTensor>();
std::hint::black_box(dlpack);
});
});
group.bench_function(BenchmarkId::new("borrowed_array", N), |b| {
b.iter(|| {
let dlpack = unsafe {
Builder::new(
context(),
BorrowedArray::new(
std::hint::black_box(&shape),
std::hint::black_box(&strides),
),
)
.build::<DLManagedTensor>()
};
std::hint::black_box(dlpack);
});
});
group.bench_function(BenchmarkId::new("copied_slice", N), |b| {
b.iter(|| {
let dlpack = Builder::new(
context(),
CopiedSlice::new(
std::hint::black_box(shape.as_slice()),
std::hint::black_box(strides.as_slice()),
),
)
.try_build::<DLManagedTensor>()
.unwrap();
std::hint::black_box(dlpack);
});
});
group.bench_function(BenchmarkId::new("borrowed_slice", N), |b| {
b.iter(|| {
let dlpack = unsafe {
Builder::new(
context(),
BorrowedSlice::new(
std::hint::black_box(shape.as_slice()),
std::hint::black_box(strides.as_slice()),
),
)
.try_build::<DLManagedTensor>()
.unwrap()
};
std::hint::black_box(dlpack);
});
});
group.finish();
}
fn bench_alloc_dealloc_baseline(c: &mut Criterion) {
#[repr(C)]
struct Storage64 {
_managed_tensor: DLManagedTensor,
_shape: [i64; 64],
_strides: [i64; 64],
}
let mut group = c.benchmark_group("alloc_dealloc_baseline/1088_bytes");
group.bench_function("via_layout_new::<T>", |b| {
b.iter(|| unsafe {
let layout = std::alloc::Layout::new::<Storage64>();
let ptr = std::alloc::alloc(std::hint::black_box(layout));
std::hint::black_box(ptr);
std::alloc::dealloc(ptr, layout);
});
});
group.bench_function("via_from_size_align", |b| {
b.iter(|| unsafe {
let layout = std::alloc::Layout::from_size_align(1088, 8).unwrap();
let ptr = std::alloc::alloc(std::hint::black_box(layout));
std::hint::black_box(ptr);
std::alloc::dealloc(ptr, layout);
});
});
group.finish();
}
fn bench_generic_metadata_copy(c: &mut Criterion) {
const N: usize = 64;
let source = [1u32; N];
let mut destination = [0i64; N];
let mut group = c.benchmark_group("generic_metadata_copy/len=64");
group.bench_function("allocate_i64_then_copy_nonoverlapping", |b| {
b.iter(|| {
let converted: Vec<i64> = std::hint::black_box(&source)
.iter()
.copied()
.map(Into::into)
.collect();
unsafe {
std::ptr::copy_nonoverlapping(
converted.as_ptr(),
std::hint::black_box(destination.as_mut_ptr()),
N,
);
}
std::hint::black_box(&destination);
});
});
group.bench_function("convert_directly_into_destination", |b| {
b.iter(|| {
for (destination, &source) in std::hint::black_box(&mut destination)
.iter_mut()
.zip(std::hint::black_box(&source))
{
*destination = source.into();
}
std::hint::black_box(&destination);
});
});
group.finish();
}
fn bench_all(c: &mut Criterion) {
bench_ndim::<1>(c);
bench_ndim::<2>(c);
bench_ndim::<3>(c);
bench_ndim::<4>(c);
bench_ndim::<5>(c);
bench_ndim::<16>(c);
bench_ndim::<64>(c);
bench_alloc_dealloc_baseline(c);
bench_generic_metadata_copy(c);
}
criterion_group!(benches, bench_all);
criterion_main!(benches);