feat: add benchmarking, optimize bounds checking in the kernel (#505)

- Adds benchmarking, run with `cargo bench` or `cargo criterion`
- Adds `MemoryRoot::pointer_in_bounds_fast` to do less precise bounds
checking in loads/stores

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: zshipko <zshipko@users.noreply.github.com>
This commit is contained in:
zach
2023-10-12 13:24:34 -07:00
committed by GitHub
parent a9835da614
commit 6f4b43bedc
8 changed files with 204 additions and 11 deletions

View File

@@ -1,6 +1,21 @@
#!/usr/bin/env bash
cargo build --release --target wasm32-unknown-unknown --package extism-runtime-kernel --bin extism-runtime
export CARGO_FLAGS=""
while getopts d flag
do
case "${flag}" in
d)
echo "Disabled bounds-checking";
export CARGO_FLAGS="--no-default-features";;
*)
echo "usage $0 [-d]"
echo "\t-d: build with bounds checking disabled"
exit 1
esac
done
cargo build --package extism-runtime-kernel --bin extism-runtime --release --target wasm32-unknown-unknown $CARGO_FLAGS
cp target/wasm32-unknown-unknown/release/extism-runtime.wasm .
wasm-strip extism-runtime.wasm
mv extism-runtime.wasm ../runtime/src/extism-runtime.wasm

View File

@@ -184,11 +184,21 @@ impl MemoryRoot {
}
#[inline(always)]
#[allow(unused)]
fn pointer_in_bounds(&self, p: Pointer) -> bool {
let start_ptr = self.blocks.as_ptr() as Pointer;
p >= start_ptr && p < start_ptr + self.length.load(Ordering::Acquire) as Pointer
}
#[inline(always)]
#[allow(unused)]
fn pointer_in_bounds_fast(p: Pointer) -> bool {
// Similar to `pointer_in_bounds` but less accurate on the upper bound. This uses the total memory size,
// instead of checking `MemoryRoot::length`
let end = core::arch::wasm32::memory_size(0) << 16;
p >= core::mem::size_of::<Self>() as Pointer && p <= end as Pointer
}
// Find a block that is free to use, this can be a new block or an existing freed block. The `self_position` argument
// is used to avoid loading the allocators position more than once when performing an allocation.
unsafe fn find_free_block(
@@ -290,10 +300,7 @@ impl MemoryRoot {
/// Finds the block at an offset in memory
pub unsafe fn find_block(&mut self, offs: Pointer) -> Option<&mut MemoryBlock> {
let blocks_start = self.blocks.as_ptr() as Pointer;
if offs < blocks_start
|| offs >= blocks_start + self.length.load(Ordering::Acquire) as Pointer
{
if !Self::pointer_in_bounds_fast(offs) {
return None;
}
let ptr = offs - core::mem::size_of::<MemoryBlock>() as u64;
@@ -372,7 +379,7 @@ pub unsafe fn extism_length(p: Pointer) -> Length {
#[no_mangle]
pub unsafe fn extism_load_u8(p: Pointer) -> u8 {
#[cfg(feature = "bounds-checking")]
if !MemoryRoot::new().pointer_in_bounds(p) {
if !MemoryRoot::pointer_in_bounds_fast(p) {
return 0;
}
*(p as *mut u8)
@@ -382,7 +389,7 @@ pub unsafe fn extism_load_u8(p: Pointer) -> u8 {
#[no_mangle]
pub unsafe fn extism_load_u64(p: Pointer) -> u64 {
#[cfg(feature = "bounds-checking")]
if !MemoryRoot::new().pointer_in_bounds(p + core::mem::size_of::<u64>() as Pointer - 1) {
if !MemoryRoot::pointer_in_bounds_fast(p + core::mem::size_of::<u64>() as u64 - 1) {
return 0;
}
*(p as *mut u64)
@@ -412,7 +419,7 @@ pub unsafe fn extism_input_load_u64(p: Pointer) -> u64 {
#[no_mangle]
pub unsafe fn extism_store_u8(p: Pointer, x: u8) {
#[cfg(feature = "bounds-checking")]
if !MemoryRoot::new().pointer_in_bounds(p) {
if !MemoryRoot::pointer_in_bounds_fast(p) {
return;
}
*(p as *mut u8) = x;
@@ -422,7 +429,7 @@ pub unsafe fn extism_store_u8(p: Pointer, x: u8) {
#[no_mangle]
pub unsafe fn extism_store_u64(p: Pointer, x: u64) {
#[cfg(feature = "bounds-checking")]
if !MemoryRoot::new().pointer_in_bounds(p) {
if !MemoryRoot::pointer_in_bounds_fast(p + core::mem::size_of::<u64>() as u64 - 1) {
return;
}
*(p as *mut u64) = x;