#![allow(clippy::expect_used)]
use core::cell::RefCell;
use core::hint::black_box;
use criterion::BenchmarkId;
use criterion::Criterion;
use criterion::Throughput;
use criterion::criterion_group;
use criterion::criterion_main;
use polyplug::runtime_store::RuntimeStore;
use polyplug_abi::AbiError;
use polyplug_abi::AbiErrorCode;
use polyplug_abi::Array;
use polyplug_abi::Buffer;
use polyplug_abi::DispatchType;
use polyplug_abi::GuestContractHandle;
use polyplug_abi::GuestContractInstance;
use polyplug_abi::GuestContractInterface;
use polyplug_abi::HostApi;
use polyplug_abi::PluginDescriptor;
use polyplug_abi::StringView;
use polyplug_abi::ffi::polyplug_host_alloc;
use polyplug_abi::ffi::polyplug_host_free;
use polyplug_utils::BundleId;
const TEST_PLUGIN_SO: &str = env!("TEST_PLUGIN_SO");
const MEMORY_PLUGIN_SO: &str = env!("MEMORY_PLUGIN_SO");
#[allow(dead_code)]
const ERROR_PLUGIN_SO: &str = env!("ERROR_PLUGIN_SO");
#[repr(C)]
struct AddArgs {
a: u32,
b: u32,
}
#[repr(C)]
struct FillArgs {
buf: Buffer,
fill_byte: u8,
}
thread_local! {
static BENCH_REGISTRY: RefCell<Option<RuntimeStore>> = RefCell::new(Some(RuntimeStore::new()));
static LAST_INTERFACE: core::cell::Cell<*const GuestContractInterface> = const { core::cell::Cell::new(core::ptr::null()) };
static LAST_CONTRACT_ID: core::cell::Cell<u64> = const { core::cell::Cell::new(0) };
}
unsafe extern "C" fn bench_register_callback(
_this: *const HostApi,
descriptor: *const PluginDescriptor,
interface: *const GuestContractInterface,
out_err: *mut AbiError,
) {
if descriptor.is_null() || interface.is_null() {
if !out_err.is_null() {
unsafe {
out_err.write(AbiError {
code: AbiErrorCode::Generic as u32,
message: StringView::null(),
})
};
}
return;
}
let desc: &PluginDescriptor = unsafe { &*descriptor };
let iface: &GuestContractInterface = unsafe { &*interface };
let contract_name: &str = unsafe {
let bytes: &[u8] =
core::slice::from_raw_parts(desc.contract_name.ptr, desc.contract_name.len);
core::str::from_utf8_unchecked(bytes) };
let result: Result<GuestContractHandle, _> =
BENCH_REGISTRY.with(|cell: &core::cell::RefCell<Option<RuntimeStore>>| {
let borrowed = cell.borrow();
let registry = borrowed.as_ref().expect("registry not initialized");
unsafe {
registry.register_guest_contract(
*desc,
interface,
contract_name.to_owned(),
BundleId::from_u64(iface.contract_id.id()),
)
}
});
let err: AbiError = match result {
Ok(_) => {
LAST_INTERFACE.with(|cell| cell.set(interface));
LAST_CONTRACT_ID.with(|cell| cell.set(iface.contract_id.id()));
AbiError::ok()
}
Err(_) => AbiError {
code: AbiErrorCode::Generic as u32,
message: StringView::null(),
},
};
if !out_err.is_null() {
unsafe { out_err.write(err) };
}
}
unsafe extern "C" fn bench_find_guest_contract(
_this: *const HostApi,
contract_id: u64,
min_version: u32,
) -> GuestContractHandle {
BENCH_REGISTRY.with(|cell: &core::cell::RefCell<Option<RuntimeStore>>| {
let registry = cell.borrow();
let reg = registry.as_ref().expect("registry not initialized");
reg.find(
polyplug_utils::GuestContractId::from_u64(contract_id),
min_version,
)
.unwrap_or_else(|_| GuestContractHandle::null())
})
}
unsafe extern "C" fn bench_find_all_guest_contracts(
_this: *const HostApi,
_contract_id: u64,
_min_version: u32,
) -> Array<GuestContractHandle> {
Array::empty()
}
unsafe extern "C" fn bench_resolve_guest_contract(
_this: *const HostApi,
handle: GuestContractHandle,
) -> *const GuestContractInterface {
BENCH_REGISTRY.with(|cell: &core::cell::RefCell<Option<RuntimeStore>>| {
cell.borrow()
.as_ref()
.expect("registry not initialized")
.resolve_guest_contract(handle)
.unwrap_or(core::ptr::null())
})
}
unsafe extern "C" fn bench_get_host_contract(
_this: *const HostApi,
_contract_id: u64,
_min_version: u32,
) -> polyplug_abi::HostContractInstance {
polyplug_abi::HostContractInstance::null()
}
unsafe extern "C" fn bench_resolve_host_contract_interface(
_this: *const HostApi,
_contract_id: u64,
_min_version: u32,
) -> *const polyplug_abi::HostContractInterface {
core::ptr::null()
}
unsafe extern "C" fn bench_list_bundles(_this: *const HostApi) -> Array<BundleId> {
Array::empty()
}
unsafe extern "C" fn bench_get_dependencies(
_this: *const HostApi,
) -> Array<polyplug_abi::DependencyInfo> {
Array::empty()
}
unsafe extern "C" fn bench_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 bench_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 bench_register_host_contract(
_this: *const HostApi,
_interface: *const polyplug_abi::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 bench_register_loader(
_this: *const HostApi,
_loader_ptr: *mut core::ffi::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 bench_get_last_error(
_this: *const HostApi,
_buf: *mut u8,
_buf_len: usize,
) -> usize {
0
}
unsafe extern "C" fn bench_get_error_len(_this: *const HostApi) -> usize {
0
}
unsafe extern "C" fn bench_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 bench_alloc(_this: *const HostApi, size: usize, align: usize) -> *mut u8 {
polyplug_host_alloc(size, align)
}
unsafe extern "C" fn bench_free(_this: *const HostApi, ptr: *mut u8, size: usize, align: usize) {
unsafe { polyplug_host_free(ptr, size, align) };
}
fn bench_host_api() -> HostApi {
HostApi {
runtime: core::ptr::null_mut(),
register_guest_contract: bench_register_callback,
alloc: bench_alloc,
free: bench_free,
find_guest_contract: bench_find_guest_contract,
find_all_guest_contracts: bench_find_all_guest_contracts,
resolve_guest_contract: bench_resolve_guest_contract,
get_host_contract: bench_get_host_contract,
resolve_host_contract_interface: bench_resolve_host_contract_interface,
list_bundles: bench_list_bundles,
get_dependencies: bench_get_dependencies,
load_bundle: bench_load_bundle,
reload_bundle: bench_reload_bundle,
register_host_contract: bench_register_host_contract,
register_loader: bench_register_loader,
get_last_error: bench_get_last_error,
get_error_len: bench_get_error_len,
unload_bundle: bench_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_and_init_plugin(path: &str) -> libloading::Library {
let library: libloading::Library =
unsafe { libloading::Library::new(path).expect("failed to load plugin") };
let init_fn: libloading::Symbol<
'_,
unsafe extern "C" fn(*const HostApi, *const polyplug_abi::BundleInitContext) -> AbiError,
> = unsafe {
library
.get(b"polyplug_init\0")
.expect("polyplug_init not found")
};
let host_interface: HostApi = bench_host_api();
let plugin_ctx: polyplug_abi::BundleInitContext = polyplug_abi::BundleInitContext {
bundle_path: StringView::null(),
bundle_id: 0,
};
let result: AbiError = unsafe {
init_fn(
&host_interface as *const HostApi,
&plugin_ctx as *const polyplug_abi::BundleInitContext,
)
};
assert!(result.is_ok(), "polyplug_init failed for {}", path);
library
}
fn get_interface_fn(
fn_id: usize,
) -> unsafe extern "C" fn(GuestContractInstance, *const (), *mut (), *mut AbiError) {
let interface_ptr: *const GuestContractInterface = LAST_INTERFACE.with(|cell| cell.get());
assert!(
!interface_ptr.is_null(),
"interface not captured — was load_and_init_plugin called?"
);
let interface: &GuestContractInterface = unsafe { &*interface_ptr };
assert!(
interface.dispatch_type == DispatchType::Native,
"expected Native dispatch type, got {:?}",
interface.dispatch_type
);
let fn_ptr: *const () = unsafe { *interface.dispatch.native.functions.add(fn_id) };
unsafe { core::mem::transmute(fn_ptr) }
}
fn bench_dispatch_noop(c: &mut Criterion) {
BENCH_REGISTRY.with(|cell: &core::cell::RefCell<Option<RuntimeStore>>| {
*cell.borrow_mut() = Some(RuntimeStore::new());
});
let _library: libloading::Library = load_and_init_plugin(TEST_PLUGIN_SO);
let dispatch_fn: unsafe extern "C" fn(
GuestContractInstance,
*const (),
*mut (),
*mut AbiError,
) = get_interface_fn(0);
let mut group: criterion::BenchmarkGroup<'_, criterion::measurement::WallTime> =
c.benchmark_group("dispatch");
group.throughput(Throughput::Elements(1));
let args: AddArgs = AddArgs { a: 0, b: 0 };
let mut out: u32 = 0_u32;
group.bench_function(BenchmarkId::new("noop", "add(0,0)"), |b| {
b.iter(|| {
let mut result: AbiError = AbiError::ok();
unsafe {
dispatch_fn(
black_box(GuestContractInstance::null()),
black_box(&args as *const AddArgs as *const ()),
black_box(&mut out as *mut u32 as *mut ()),
&mut result,
)
};
black_box(result);
});
});
group.finish();
core::mem::forget(_library);
}
fn bench_dispatch_buffer_arg(c: &mut Criterion) {
BENCH_REGISTRY.with(|cell: &core::cell::RefCell<Option<RuntimeStore>>| {
*cell.borrow_mut() = Some(RuntimeStore::new());
});
let _library: libloading::Library = load_and_init_plugin(MEMORY_PLUGIN_SO);
let dispatch_fn: unsafe extern "C" fn(
GuestContractInstance,
*const (),
*mut (),
*mut AbiError,
) = get_interface_fn(0);
let buf_ptr: *mut u8 = polyplug_host_alloc(4096, 1);
assert!(!buf_ptr.is_null(), "bench alloc failed");
let mut group: criterion::BenchmarkGroup<'_, criterion::measurement::WallTime> =
c.benchmark_group("dispatch");
group.throughput(Throughput::Elements(1));
let args: FillArgs = FillArgs {
buf: Buffer {
ptr: buf_ptr,
len: 0,
cap: 4096,
},
fill_byte: 0xBB_u8,
};
let mut out: u32 = 0_u32;
group.bench_function(BenchmarkId::new("buffer_arg", "fill_4096"), |b| {
b.iter(|| {
let mut result: AbiError = AbiError::ok();
unsafe {
dispatch_fn(
black_box(GuestContractInstance::null()),
black_box(&args as *const FillArgs as *const ()),
black_box(&mut out as *mut u32 as *mut ()),
&mut result,
)
};
black_box(result);
});
});
group.finish();
unsafe { polyplug_host_free(buf_ptr, 4096, 1) };
core::mem::forget(_library);
}
fn bench_dispatch_struct_arg_and_return(c: &mut Criterion) {
BENCH_REGISTRY.with(|cell: &core::cell::RefCell<Option<RuntimeStore>>| {
*cell.borrow_mut() = Some(RuntimeStore::new());
});
let _library: libloading::Library = load_and_init_plugin(TEST_PLUGIN_SO);
let dispatch_fn: unsafe extern "C" fn(
GuestContractInstance,
*const (),
*mut (),
*mut AbiError,
) = get_interface_fn(0);
let mut group: criterion::BenchmarkGroup<'_, criterion::measurement::WallTime> =
c.benchmark_group("dispatch");
group.throughput(Throughput::Elements(1));
let args: AddArgs = AddArgs {
a: 42_u32,
b: 57_u32,
};
let mut out: u32 = 0_u32;
group.bench_function(
BenchmarkId::new("struct_arg_and_return", "add(42,57)"),
|b| {
b.iter(|| {
let mut result: AbiError = AbiError::ok();
unsafe {
dispatch_fn(
black_box(GuestContractInstance::null()),
black_box(&args as *const AddArgs as *const ()),
black_box(&mut out as *mut u32 as *mut ()),
&mut result,
)
};
black_box(out);
black_box(result);
});
},
);
group.finish();
core::mem::forget(_library);
}
fn bench_dispatch_cross_plugin(c: &mut Criterion) {
BENCH_REGISTRY.with(|cell: &core::cell::RefCell<Option<RuntimeStore>>| {
*cell.borrow_mut() = Some(RuntimeStore::new());
});
let _memory_lib: libloading::Library = load_and_init_plugin(MEMORY_PLUGIN_SO);
let memory_contract_id: u64 = LAST_CONTRACT_ID.with(|cell| cell.get());
assert_ne!(
memory_contract_id, 0,
"memory_plugin contract_id was not captured"
);
let host_interface: HostApi = bench_host_api();
let sv: StringView = StringView {
ptr: b"hello".as_ptr(),
len: 5,
};
let mut sv_out: StringView = StringView::null();
let mut group: criterion::BenchmarkGroup<'_, criterion::measurement::WallTime> =
c.benchmark_group("dispatch");
group.throughput(Throughput::Elements(1));
group.bench_function(BenchmarkId::new("cross_plugin", "find+call"), |b| {
b.iter(|| {
let handle: GuestContractHandle = unsafe {
black_box((host_interface.find_guest_contract)(
&host_interface as *const HostApi,
memory_contract_id,
0,
))
};
let interface_ptr: *const GuestContractInterface = unsafe {
black_box((host_interface.resolve_guest_contract)(
&host_interface as *const HostApi,
handle,
))
};
let result: AbiError = if interface_ptr.is_null() {
AbiError {
code: AbiErrorCode::NotFound as u32,
message: StringView::null(),
}
} else {
let interface: &GuestContractInterface = unsafe { &*interface_ptr };
let fn_ptr: *const () = unsafe { *interface.dispatch.native.functions.add(2) };
let dispatch_fn: unsafe extern "C" fn(
GuestContractInstance,
*const (),
*mut (),
*mut AbiError,
) = unsafe { core::mem::transmute(fn_ptr) };
let mut err: AbiError = AbiError::ok();
unsafe {
dispatch_fn(
black_box(GuestContractInstance::null()),
black_box(&sv as *const StringView as *const ()),
black_box(&mut sv_out as *mut StringView as *mut ()),
&mut err,
)
};
err
};
black_box(result);
black_box(sv_out);
});
});
group.finish();
core::mem::forget(_memory_lib);
}
#[repr(C)]
struct CopyArgs {
host: *const HostApi,
sv: StringView,
}
fn bench_marshalling(c: &mut Criterion) {
BENCH_REGISTRY.with(|cell: &core::cell::RefCell<Option<RuntimeStore>>| {
*cell.borrow_mut() = Some(RuntimeStore::new());
});
let _library: libloading::Library = load_and_init_plugin(MEMORY_PLUGIN_SO);
let borrowed_fn: unsafe extern "C" fn(
GuestContractInstance,
*const (),
*mut (),
*mut AbiError,
) = get_interface_fn(4);
let owned_fn: unsafe extern "C" fn(GuestContractInstance, *const (), *mut (), *mut AbiError) =
get_interface_fn(5);
let host_interface: HostApi = bench_host_api();
let payload: Vec<u8> = vec![b'a'; 1_048_576];
let sizes: [usize; 9] = [16, 64, 256, 1024, 4096, 16384, 65536, 262_144, 1_048_576];
let mut group: criterion::BenchmarkGroup<'_, criterion::measurement::WallTime> =
c.benchmark_group("marshalling");
group.throughput(Throughput::Elements(1));
for &size in &sizes {
let sv: StringView = StringView {
ptr: payload.as_ptr(),
len: size,
};
group.bench_with_input(BenchmarkId::new("borrowed", size), &sv, |b, sv| {
let mut out: StringView = StringView::null();
b.iter(|| {
let mut result: AbiError = AbiError::ok();
unsafe {
borrowed_fn(
black_box(GuestContractInstance::null()),
black_box(sv as *const StringView as *const ()),
black_box(&mut out as *mut StringView as *mut ()),
&mut result,
)
};
black_box(result);
black_box(out);
});
});
let copy_args: CopyArgs = CopyArgs {
host: &host_interface as *const HostApi,
sv,
};
group.bench_with_input(BenchmarkId::new("owned", size), ©_args, |b, args| {
let mut out: Buffer = Buffer {
ptr: core::ptr::null_mut(),
len: 0,
cap: 0,
};
b.iter(|| {
let mut result: AbiError = AbiError::ok();
unsafe {
owned_fn(
black_box(GuestContractInstance::null()),
black_box(args as *const CopyArgs as *const ()),
black_box(&mut out as *mut Buffer as *mut ()),
&mut result,
)
};
black_box(result);
if !out.ptr.is_null() {
unsafe { polyplug_host_free(out.ptr, out.len, 1) };
}
});
});
}
group.finish();
core::mem::forget(_library);
}
criterion_group!(
benches,
bench_dispatch_noop,
bench_dispatch_buffer_arg,
bench_dispatch_struct_arg_and_return,
bench_dispatch_cross_plugin,
bench_marshalling,
);
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 core::ffi::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()
}