Skip to content

Component benchmarks #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ build-wasm-runtime target=default-target:
cd ./src/wasm_runtime && cargo build --verbose --profile={{ if target == "debug" {"dev"} else { target } }} && rm -R target

build-wasm-examples target=default-target:
wasm-tools component wit ./src/wasmsamples/components/runcomponent.wit -w -o ./src/wasmsamples/components/runcomponent-world.wasm
{{ build-wasm-examples-command }} {{target}}

build-rust-wasm-examples target=default-target: (mkdir-redist target)
Expand Down Expand Up @@ -100,10 +101,11 @@ examples-components target=default-target features="": (build-rust-component-exa
{{ wit-world }} cargo run {{ if features =="" {''} else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example component_example

# warning, compares to and then OVERWRITES the given baseline
bench-ci baseline target=default-target features="":
bench-ci baseline target="release" features="":
cd src/hyperlight_wasm && cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} -- --verbose --save-baseline {{baseline}}
bench target=default-target features="":
cd src/hyperlight_wasm && cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} -- --verbose
bench target="release" features="":
#cd src/hyperlight_wasm && cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} --bench benchmarks -- --verbose
cd src/hyperlight_wasm && {{wit-world}} cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} --bench benchmarks_components -- --verbose
bench-download os hypervisor tag="":
gh release download {{ tag }} -D ./src/hyperlight_wasm/target/ -p benchmarks_{{ os }}_{{ hypervisor }}.tar.gz
mkdir {{ mkdir-arg }} ./src/hyperlight_wasm/target/criterion
Expand Down
5 changes: 5 additions & 0 deletions src/hyperlight_wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,8 @@ mshv3 = ["hyperlight-host/mshv3"]
[[bench]]
name = "benchmarks"
harness = false

[[bench]]
name = "benchmarks_components"
harness = false

104 changes: 104 additions & 0 deletions src/hyperlight_wasm/benches/benchmarks_components.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::sync::{Arc, Mutex};

use criterion::{Bencher, Criterion, criterion_group, criterion_main};
use hyperlight_host::HyperlightError;
use hyperlight_wasm::{LoadedWasmSandbox, Result, SandboxBuilder};
use crate::bindings::example::runcomponent::Guest;

extern crate alloc;
mod bindings {
hyperlight_component_macro::host_bindgen!("../../src/wasmsamples/components/runcomponent-world.wasm");
}

pub struct State {}
impl State {
pub fn new() -> Self {
State {}
}
}

impl Default for State {
fn default() -> Self {
Self::new()
}
}

impl bindings::example::runcomponent::Host for State {
fn r#get_time_since_boot_microsecond(&mut self,) -> i64 {
let res = std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap()
.as_micros();
i64::try_from(res).unwrap()
}
}

impl bindings::example::runcomponent::RuncomponentImports for State {
type Host = State;

fn r#host(&mut self) -> impl ::core::borrow::BorrowMut<Self::Host> {
self
}
}


fn wasm_component_guest_call_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("wasm_component_guest_functions");

let bench_guest_function = |b: &mut Bencher<'_>, ext| {
let (sb, rt) = get_loaded_wasm_sandbox(ext);
let mut wrapped = bindings::RuncomponentSandbox { sb, rt };
let instance = bindings::example::runcomponent::RuncomponentExports::guest(&mut wrapped);

b.iter(|| {
instance
.echo("Hello World!".to_string());
});
};

group.bench_function("wasm_guest_call", |b: &mut Bencher<'_>| {
bench_guest_function(b, "wasm");
});

group.bench_function("wasm_guest_call_aot", |b: &mut Bencher<'_>| {
bench_guest_function(b, "aot");
});

group.finish();
}

fn wasm_component_sandbox_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("wasm_component_sandboxes");
let create_wasm_sandbox = || {
get_loaded_wasm_sandbox("wasm");
};

group.bench_function("create_sandbox", |b| {
b.iter_with_large_drop(create_wasm_sandbox);
});

group.bench_function("create_sandbox_and_drop", |b| {
b.iter(create_wasm_sandbox);
});

group.finish();
}

fn get_loaded_wasm_sandbox(ext: &str) -> (LoadedWasmSandbox, Arc<Mutex<bindings::RuncomponentResources<State>>>) {
let state = State::new();
let mut sandbox = SandboxBuilder::new().build().unwrap();
let rt = bindings::register_host_functions(&mut sandbox, state);


let sb = sandbox.load_runtime().unwrap();

let sb = sb.load_module(format!("../../x64/release/runcomponent.aot",))
.unwrap();
(sb, rt)
}

criterion_group! {
name = benches_components;
config = Criterion::default();//.warm_up_time(Duration::from_millis(50)); // If warm_up_time is default 3s warmup, the benchmark will fail due memory error
targets = wasm_component_guest_call_benchmark, wasm_component_sandbox_benchmark
}
criterion_main!(benches_components);
17 changes: 15 additions & 2 deletions src/hyperlight_wasm/scripts/build-wasm-examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,30 @@ else

docker build --build-arg GCC_VERSION=12 --build-arg WASI_SDK_VERSION_FULL=20.0 --cache-from ghcr.io/hyperlight-dev/wasm-clang-builder:latest -t wasm-clang-builder:latest . 2> ${OUTPUT_DIR}/dockerbuild.log

for FILENAME in $(find . -name '*.c')
for FILENAME in $(find . -name '*.c' -not -path './components/*')
do
echo Building ${FILENAME}
# Build the wasm file with wasi-libc for wasmtime
docker run --rm -i -v "${PWD}:/tmp/host" -v "${OUTPUT_DIR}:/tmp/output" wasm-clang-builder:latest /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o /tmp/output/${FILENAME%.*}-wasi-libc.wasm /tmp/host/${FILENAME}
docker run --rm -i -v "${PWD}:/tmp/host" -v "${OUTPUT_DIR}:/tmp/output/" wasm-clang-builder:latest /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o /tmp/output/${FILENAME%.*}-wasi-libc.wasm /tmp/host/${FILENAME}

# Build AOT for Wasmtime; note that Wasmtime does not support
# interpreting, so its wasm binary is secretly an AOT binary.
cargo run -p hyperlight-wasm-aot compile ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${OUTPUT_DIR}/${FILENAME%.*}.aot
cp ${OUTPUT_DIR}/${FILENAME%.*}.aot ${OUTPUT_DIR}/${FILENAME%.*}.wasm
done

echo Building component
# Build the wasm file with wasi-libc for wasmtime
wit-bindgen c ${PWD}/components/runcomponent.wit --out-dir ${PWD}/components
docker run --rm -i -v "${PWD}:/tmp/host" -v "${OUTPUT_DIR}:/tmp/output/" wasm-clang-builder:latest /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free -o /tmp/output/runcomponent-p1.wasm /tmp/host/components/component.c /tmp/host/components/runcomponent.c /tmp/host/components/runcomponent_component_type.o

# tooling currently builds a p1 wasm component so convert it
wasm-tools component new ${OUTPUT_DIR}/runcomponent-p1.wasm -o ${OUTPUT_DIR}/runcomponent-p2.wasm

# Build AOT for Wasmtime; note that Wasmtime does not support
# interpreting, so its wasm binary is secretly an AOT binary.
cargo run -p hyperlight-wasm-aot compile --component ${OUTPUT_DIR}/runcomponent-p2.wasm ${OUTPUT_DIR}/runcomponent.aot
cp ${OUTPUT_DIR}/runcomponent.aot ${OUTPUT_DIR}/runcomponent.wasm
fi

popd
11 changes: 11 additions & 0 deletions src/wasmsamples/components/component.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "runcomponent.h"
#include <stdlib.h>
#include <string.h>

void exports_example_runcomponent_guest_echo(runcomponent_string_t *msg, runcomponent_string_t *ret)
{
ret->len = msg->len;
ret->ptr = (uint8_t *) malloc(ret->len);
memcpy(ret->ptr, msg->ptr, ret->len);
runcomponent_string_free(msg);
}
78 changes: 78 additions & 0 deletions src/wasmsamples/components/runcomponent.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Generated by `wit-bindgen` 0.38.0. DO NOT EDIT!
#include "runcomponent.h"
#include <stdlib.h>
#include <string.h>

// Imported Functions from `example:runcomponent/host`

__attribute__((__import_module__("example:runcomponent/host"), __import_name__("get-time-since-boot-microsecond")))
extern int64_t __wasm_import_example_runcomponent_host_get_time_since_boot_microsecond(void);

// Exported Functions from `example:runcomponent/guest`

__attribute__((__weak__, __export_name__("cabi_post_example:runcomponent/guest#echo")))
void __wasm_export_exports_example_runcomponent_guest_echo_post_return(uint8_t * arg0) {
if ((*((size_t*) (arg0 + 4))) > 0) {
free(*((uint8_t **) (arg0 + 0)));
}
}

// Canonical ABI intrinsics

__attribute__((__weak__, __export_name__("cabi_realloc")))
void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
(void) old_size;
if (new_size == 0) return (void*) align;
void *ret = realloc(ptr, new_size);
if (!ret) abort();
return ret;
}

// Helper Functions

void runcomponent_string_set(runcomponent_string_t *ret, const char*s) {
ret->ptr = (uint8_t*) s;
ret->len = strlen(s);
}

void runcomponent_string_dup(runcomponent_string_t *ret, const char*s) {
ret->len = strlen(s);
ret->ptr = (uint8_t*) cabi_realloc(NULL, 0, 1, ret->len * 1);
memcpy(ret->ptr, s, ret->len * 1);
}

void runcomponent_string_free(runcomponent_string_t *ret) {
if (ret->len > 0) {
free(ret->ptr);
}
ret->ptr = NULL;
ret->len = 0;
}

// Component Adapters

__attribute__((__aligned__(4)))
static uint8_t RET_AREA[8];

int64_t example_runcomponent_host_get_time_since_boot_microsecond(void) {
int64_t ret = __wasm_import_example_runcomponent_host_get_time_since_boot_microsecond();
return ret;
}

__attribute__((__export_name__("example:runcomponent/guest#echo")))
uint8_t * __wasm_export_exports_example_runcomponent_guest_echo(uint8_t * arg, size_t arg0) {
runcomponent_string_t arg1 = (runcomponent_string_t) { (uint8_t*)(arg), (arg0) };
runcomponent_string_t ret;
exports_example_runcomponent_guest_echo(&arg1, &ret);
uint8_t *ptr = (uint8_t *) &RET_AREA;
*((size_t*)(ptr + 4)) = (ret).len;
*((uint8_t **)(ptr + 0)) = (uint8_t *) (ret).ptr;
return ptr;
}

// Ensure that the *_component_type.o object is linked in

extern void __component_type_object_force_link_runcomponent(void);
void __component_type_object_force_link_runcomponent_public_use_in_this_compilation_unit(void) {
__component_type_object_force_link_runcomponent();
}
39 changes: 39 additions & 0 deletions src/wasmsamples/components/runcomponent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Generated by `wit-bindgen` 0.38.0. DO NOT EDIT!
#ifndef __BINDINGS_RUNCOMPONENT_H
#define __BINDINGS_RUNCOMPONENT_H
#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

typedef struct runcomponent_string_t {
uint8_t*ptr;
size_t len;
} runcomponent_string_t;

// Imported Functions from `example:runcomponent/host`
extern int64_t example_runcomponent_host_get_time_since_boot_microsecond(void);

// Exported Functions from `example:runcomponent/guest`
void exports_example_runcomponent_guest_echo(runcomponent_string_t *msg, runcomponent_string_t *ret);

// Helper Functions

// Transfers ownership of `s` into the string `ret`
void runcomponent_string_set(runcomponent_string_t *ret, const char*s);

// Creates a copy of the input nul-terminate string `s` and
// stores it into the component model string `ret`.
void runcomponent_string_dup(runcomponent_string_t *ret, const char*s);

// Deallocates the string pointed to by `ret`, deallocating
// the memory behind the string.
void runcomponent_string_free(runcomponent_string_t *ret);

#ifdef __cplusplus
}
#endif
#endif
14 changes: 14 additions & 0 deletions src/wasmsamples/components/runcomponent.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package example:runcomponent;

interface guest {
echo: func(msg: string) -> string;
}

interface host {
get-time-since-boot-microsecond: func() -> s64;
}

world runcomponent {
export guest;
import host;
}
Binary file not shown.
Loading