#![allow(clippy::expect_used)]
use core::cell::Cell;
use core::ffi::c_void;
use core::hint::black_box;
use criterion::BenchmarkId;
use criterion::Criterion;
use criterion::Throughput;
use criterion::criterion_group;
use criterion::criterion_main;
use polyplug_abi::AbiError;
use polyplug_abi::AbiErrorCode;
use polyplug_abi::Array;
use polyplug_abi::BundleInitContext;
use polyplug_abi::DependencyInfo;
use polyplug_abi::DispatchType;
use polyplug_abi::GuestContractHandle;
use polyplug_abi::GuestContractInstance;
use polyplug_abi::GuestContractInterface;
use polyplug_abi::HostApi;
use polyplug_abi::HostContractInstance;
use polyplug_abi::HostContractInterface;
use polyplug_abi::PluginDescriptor;
use polyplug_abi::StringView;
use polyplug_utils::BundleId;
const TEST_PLUGIN_SO: &str = env!("TEST_PLUGIN_SO");
const TEST_PLUGIN_CPP_SO: &str = env!("TEST_PLUGIN_CPP_SO");
const COUNT: u64 = 1_000_000;
#[repr(C)]
struct AddArgs {
a: u32,
b: u32,
}
#[inline(never)]
fn inc_native(x: u32) -> u32 {
x.wrapping_add(1)
}
#[inline(never)]
extern "C" fn inc_abi(
_instance: GuestContractInstance,
args: *const (),
out: *mut (),
out_err: *mut AbiError,
) {
let x: u32 = unsafe { *(args as *const u32) };
unsafe { core::ptr::write(out as *mut u32, x.wrapping_add(1)) };
if !out_err.is_null() {
unsafe { out_err.write(AbiError::ok()) };
}
}
thread_local! {
static CAPTURED_INTERFACE: Cell<*const GuestContractInterface> =
const { Cell::new(core::ptr::null()) };
}
unsafe extern "C" fn capture_register_callback(
_this: *const HostApi,
descriptor: *const PluginDescriptor,
interface: *const GuestContractInterface,
out_err: *mut AbiError,
) {
let err: AbiError = if descriptor.is_null() || interface.is_null() {
AbiError {
code: AbiErrorCode::Generic as u32,
message: StringView::null(),
}
} else {
CAPTURED_INTERFACE.with(|cell: &Cell<*const GuestContractInterface>| cell.set(interface));
AbiError::ok()
};
if !out_err.is_null() {
unsafe { out_err.write(err) };
}
}
unsafe extern "C" fn stub_find(
_this: *const HostApi,
_contract_id: u64,
_min_version: u32,
) -> GuestContractHandle {
GuestContractHandle::null()
}
unsafe extern "C" fn stub_find_all(
_this: *const HostApi,
_contract_id: u64,
_min_version: u32,
) -> Array<GuestContractHandle> {
Array::empty()
}
unsafe extern "C" fn stub_resolve(
_this: *const HostApi,
_handle: GuestContractHandle,
) -> *const GuestContractInterface {
core::ptr::null()
}
unsafe extern "C" fn stub_get_host_contract(
_this: *const HostApi,
_contract_id: u64,
_min_version: u32,
) -> HostContractInstance {
HostContractInstance::null()
}
unsafe extern "C" fn stub_resolve_host_iface(
_this: *const HostApi,
_contract_id: u64,
_min_version: u32,
) -> *const HostContractInterface {
core::ptr::null()
}
unsafe extern "C" fn stub_list_bundles(_this: *const HostApi) -> Array<BundleId> {
Array::empty()
}
unsafe extern "C" fn stub_get_dependencies(_this: *const HostApi) -> Array<DependencyInfo> {
Array::empty()
}
unsafe extern "C" fn stub_load_bundle(
_this: *const HostApi,
_path: *const u8,
_path_len: usize,
out_err: *mut AbiError,
) {
if !out_err.is_null() {
unsafe {
out_err.write(AbiError {
code: AbiErrorCode::Generic as u32,
message: StringView::null(),
})
};
}
}
unsafe extern "C" fn stub_reload_bundle(
_this: *const HostApi,
_path: *const u8,
_path_len: usize,
out_err: *mut AbiError,
) {
if !out_err.is_null() {
unsafe {
out_err.write(AbiError {
code: AbiErrorCode::Generic as u32,
message: StringView::null(),
})
};
}
}
unsafe extern "C" fn stub_register_host_contract(
_this: *const HostApi,
_interface: *const HostContractInterface,
out_err: *mut AbiError,
) {
if !out_err.is_null() {
unsafe {
out_err.write(AbiError {
code: AbiErrorCode::Generic as u32,
message: StringView::null(),
})
};
}
}
unsafe extern "C" fn stub_register_loader(
_this: *const HostApi,
_loader_ptr: *mut c_void,
out_err: *mut AbiError,
) {
if !out_err.is_null() {
unsafe {
out_err.write(AbiError {
code: AbiErrorCode::Generic as u32,
message: StringView::null(),
})
};
}
}
unsafe extern "C" fn stub_get_last_error(
_this: *const HostApi,
_buf: *mut u8,
_buf_len: usize,
) -> usize {
0
}
unsafe extern "C" fn stub_get_error_len(_this: *const HostApi) -> usize {
0
}
unsafe extern "C" fn stub_unload_bundle(
_this: *const HostApi,
_bundle_id: BundleId,
out_err: *mut AbiError,
) {
if !out_err.is_null() {
unsafe { out_err.write(AbiError::ok()) };
}
}
unsafe extern "C" fn stub_alloc(_this: *const HostApi, _size: usize, _align: usize) -> *mut u8 {
core::ptr::null_mut()
}
unsafe extern "C" fn stub_free(_this: *const HostApi, _ptr: *mut u8, _size: usize, _align: usize) {}
fn capture_host() -> HostApi {
HostApi {
runtime: core::ptr::null_mut(),
register_guest_contract: capture_register_callback,
alloc: stub_alloc,
free: stub_free,
find_guest_contract: stub_find,
find_all_guest_contracts: stub_find_all,
resolve_guest_contract: stub_resolve,
get_host_contract: stub_get_host_contract,
resolve_host_contract_interface: stub_resolve_host_iface,
list_bundles: stub_list_bundles,
get_dependencies: stub_get_dependencies,
load_bundle: stub_load_bundle,
reload_bundle: stub_reload_bundle,
register_host_contract: stub_register_host_contract,
register_loader: stub_register_loader,
get_last_error: stub_get_last_error,
get_error_len: stub_get_error_len,
unload_bundle: stub_unload_bundle,
log: stub_host_log,
create_guest_instance: stub_create_guest_instance,
destroy_guest_instance: stub_destroy_guest_instance,
revision_counter: stub_revision_counter,
reserved: core::ptr::null(),
}
}
fn load_dispatch_fn(
so_path: &str,
) -> (
libloading::Library,
unsafe extern "C" fn(GuestContractInstance, *const (), *mut (), *mut AbiError),
) {
let library: libloading::Library =
unsafe { libloading::Library::new(so_path).expect("load test plugin") };
let init_fn: libloading::Symbol<
'_,
unsafe extern "C" fn(*const HostApi, *const BundleInitContext) -> AbiError,
> = unsafe { library.get(b"polyplug_init\0").expect("polyplug_init") };
let host: HostApi = capture_host();
let ctx: BundleInitContext = BundleInitContext {
bundle_path: StringView::null(),
bundle_id: 0,
};
let result: AbiError =
unsafe { init_fn(&host as *const HostApi, &ctx as *const BundleInitContext) };
assert!(result.is_ok(), "polyplug_init failed");
let interface_ptr: *const GuestContractInterface =
CAPTURED_INTERFACE.with(|cell: &Cell<*const GuestContractInterface>| cell.get());
assert!(!interface_ptr.is_null(), "interface not captured");
let interface: &GuestContractInterface = unsafe { &*interface_ptr };
assert!(
interface.dispatch_type == DispatchType::Native,
"expected native dispatch"
);
let fn_ptr: *const () = unsafe { *interface.dispatch.native.functions.add(0) };
let dispatch_fn: unsafe extern "C" fn(
GuestContractInstance,
*const (),
*mut (),
*mut AbiError,
) = unsafe { core::mem::transmute(fn_ptr) };
(library, dispatch_fn)
}
fn bench_counter_inc(c: &mut Criterion) {
let mut group: criterion::BenchmarkGroup<'_, criterion::measurement::WallTime> =
c.benchmark_group("counter_inc_1m");
group.throughput(Throughput::Elements(COUNT));
group.bench_function(BenchmarkId::new("native", "inline_never"), |b| {
b.iter(|| {
let mut counter: u32 = 0;
for _ in 0..COUNT {
counter = inc_native(black_box(counter));
}
black_box(counter)
});
});
let abi_fn: unsafe extern "C" fn(GuestContractInstance, *const (), *mut (), *mut AbiError) =
inc_abi;
group.bench_function(BenchmarkId::new("native", "abi_marshalled"), |b| {
b.iter(|| {
let mut counter: u32 = 0;
for _ in 0..COUNT {
let mut out: u32 = 0;
let mut err: AbiError = AbiError::ok();
unsafe {
black_box(abi_fn)(
black_box(GuestContractInstance::null()),
black_box(&counter as *const u32 as *const ()),
black_box(&mut out as *mut u32 as *mut ()),
&mut err,
)
};
debug_assert!(err.is_ok());
counter = out;
}
black_box(counter)
});
});
{
let library: libloading::Library =
unsafe { libloading::Library::new(TEST_PLUGIN_SO).expect("load test plugin (ffi)") };
let inc_ffi: libloading::Symbol<'_, unsafe extern "C" fn(u32) -> u32> = unsafe {
library
.get(b"polyplug_bench_inc\0")
.expect("polyplug_bench_inc")
};
let inc_ffi: unsafe extern "C" fn(u32) -> u32 = *inc_ffi;
group.bench_function(BenchmarkId::new("ffi", "by_value"), |b| {
b.iter(|| {
let mut counter: u32 = 0;
for _ in 0..COUNT {
counter = unsafe { black_box(inc_ffi)(black_box(counter)) };
}
black_box(counter)
});
});
core::mem::forget(library);
}
{
let (library, dispatch_fn): (
libloading::Library,
unsafe extern "C" fn(GuestContractInstance, *const (), *mut (), *mut AbiError),
) = load_dispatch_fn(TEST_PLUGIN_SO);
group.bench_function(BenchmarkId::new("polyplug", "dispatch"), |b| {
b.iter(|| {
let mut counter: u32 = 0;
for _ in 0..COUNT {
let args: AddArgs = AddArgs { a: counter, b: 1 };
let mut out: u32 = 0;
let mut err: AbiError = AbiError::ok();
unsafe {
black_box(dispatch_fn)(
black_box(GuestContractInstance::null()),
black_box(&args as *const AddArgs as *const ()),
black_box(&mut out as *mut u32 as *mut ()),
&mut err,
)
};
debug_assert!(err.is_ok());
counter = out;
}
black_box(counter)
});
});
core::mem::forget(library);
}
{
let (library, dispatch_fn): (
libloading::Library,
unsafe extern "C" fn(GuestContractInstance, *const (), *mut (), *mut AbiError),
) = load_dispatch_fn(TEST_PLUGIN_CPP_SO);
group.bench_function(BenchmarkId::new("polyplug", "dispatch_cpp"), |b| {
b.iter(|| {
let mut counter: u32 = 0;
for _ in 0..COUNT {
let args: AddArgs = AddArgs { a: counter, b: 1 };
let mut out: u32 = 0;
let mut err: AbiError = AbiError::ok();
unsafe {
black_box(dispatch_fn)(
black_box(GuestContractInstance::null()),
black_box(&args as *const AddArgs as *const ()),
black_box(&mut out as *mut u32 as *mut ()),
&mut err,
)
};
debug_assert!(err.is_ok());
counter = out;
}
black_box(counter)
});
});
core::mem::forget(library);
}
group.finish();
}
criterion_group!(benches, bench_counter_inc);
criterion_main!(benches);
unsafe extern "C" fn stub_host_log(
_this: *const polyplug_abi::HostApi,
_level: u32,
_scope: polyplug_abi::StringView,
_message: polyplug_abi::StringView,
) {
}
unsafe extern "C" fn stub_create_guest_instance(
_this: *const polyplug_abi::HostApi,
_interface: *const polyplug_abi::GuestContractInterface,
_args: *const c_void,
out_instance: *mut polyplug_abi::GuestContractInstance,
) {
if !out_instance.is_null() {
unsafe { out_instance.write(polyplug_abi::GuestContractInstance::null()) };
}
}
unsafe extern "C" fn stub_destroy_guest_instance(
_this: *const polyplug_abi::HostApi,
_interface: *const polyplug_abi::GuestContractInterface,
_instance: polyplug_abi::GuestContractInstance,
) {
}
unsafe extern "C" fn stub_revision_counter(_this: *const polyplug_abi::HostApi) -> *const u64 {
core::ptr::null()
}