Skip to content

Commit 086d43b

Browse files
committed
Add c Component example
Signed-off-by: James Sturtevant <[email protected]>
1 parent d53f11f commit 086d43b

File tree

7 files changed

+175
-5
lines changed

7 files changed

+175
-5
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,3 +479,4 @@ target/
479479
# MSVC Windows builds of rustc generate these, which store debugging information
480480
*.pdb
481481
src/component_sample/**/*.wasm
482+
src/wasmsamples/components/bindings/

Justfile

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ build-wasm-examples-command := if os() == "windows" { "./src/hyperlight_wasm/scr
44
mkdir-arg := if os() == "windows" { "-Force" } else { "-p" }
55
latest-release:= if os() == "windows" {"$(git tag -l --sort=v:refname | select -last 2 | select -first 1)"} else {`git tag -l --sort=v:refname | tail -n 2 | head -n 1`}
66
wit-world := if os() == "windows" { "$env:WIT_WORLD=\"" + justfile_directory() + "\\src\\component_sample\\wit\\component-world.wasm" + "\";" } else { "WIT_WORLD=" + justfile_directory() + "/src/component_sample/wit/component-world.wasm" }
7+
wit-world-c := if os() == "windows" { "$env:WIT_WORLD=\"" + justfile_directory() + "\\src\\wasmsamples\\components\\runcomponent-world.wasm" + "\";" } else { "WIT_WORLD=" + justfile_directory() + "/src/wasmsamples/components/runcomponent-world.wasm" }
78

89
set windows-shell := ["pwsh.exe", "-NoLogo", "-Command"]
910

@@ -30,6 +31,7 @@ build-wasm-runtime target=default-target:
3031
cd ./src/wasm_runtime && cargo build --verbose --profile={{ if target == "debug" {"dev"} else { target } }} && rm -R target
3132

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

3537
build-rust-wasm-examples target=default-target: (mkdir-redist target)
@@ -101,10 +103,13 @@ examples-components target=default-target features="": (build-rust-component-exa
101103
{{ wit-world }} cargo run {{ if features =="" {''} else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example component_example
102104

103105
# warning, compares to and then OVERWRITES the given baseline
104-
bench-ci baseline target=default-target features="":
106+
bench-ci baseline target="release" features="":
105107
cd src/hyperlight_wasm && cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} -- --verbose --save-baseline {{baseline}}
106-
bench target=default-target features="":
107-
cd src/hyperlight_wasm && cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} -- --verbose
108+
bench target="release" features="": (bench-wasm target features) (bench-components target features)
109+
bench-wasm target="release" features="":
110+
cd src/hyperlight_wasm && cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} --bench benchmarks -- --verbose
111+
bench-components target="release" features="":
112+
cd src/hyperlight_wasm && {{wit-world-c}} cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} --bench benchmarks_components -- --verbose
108113
bench-download os hypervisor cpu tag="":
109114
gh release download {{ tag }} -D ./src/hyperlight_wasm/target/ -p benchmarks_{{ os }}_{{ hypervisor }}_{{ cpu }}.tar.gz
110115
mkdir {{ mkdir-arg }} ./src/hyperlight_wasm/target/criterion

src/hyperlight_wasm/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,8 @@ mshv3 = ["hyperlight-host/mshv3"]
8888
[[bench]]
8989
name = "benchmarks"
9090
harness = false
91+
92+
[[bench]]
93+
name = "benchmarks_components"
94+
harness = false
95+
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use std::sync::{Arc, Mutex};
2+
3+
use criterion::{Bencher, Criterion, criterion_group, criterion_main};
4+
use hyperlight_wasm::{LoadedWasmSandbox, SandboxBuilder};
5+
6+
use crate::bindings::example::runcomponent::Guest;
7+
8+
extern crate alloc;
9+
mod bindings {
10+
hyperlight_component_macro::host_bindgen!(
11+
"../../src/wasmsamples/components/runcomponent-world.wasm"
12+
);
13+
}
14+
15+
pub struct State {}
16+
impl State {
17+
pub fn new() -> Self {
18+
State {}
19+
}
20+
}
21+
22+
impl Default for State {
23+
fn default() -> Self {
24+
Self::new()
25+
}
26+
}
27+
28+
impl bindings::example::runcomponent::Host for State {
29+
fn r#get_time_since_boot_microsecond(&mut self) -> i64 {
30+
let res = std::time::SystemTime::now()
31+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
32+
.unwrap()
33+
.as_micros();
34+
i64::try_from(res).unwrap()
35+
}
36+
}
37+
38+
impl bindings::example::runcomponent::RuncomponentImports for State {
39+
type Host = State;
40+
41+
fn r#host(&mut self) -> impl ::core::borrow::BorrowMut<Self::Host> {
42+
self
43+
}
44+
}
45+
46+
fn wasm_component_guest_call_benchmark(c: &mut Criterion) {
47+
let mut group = c.benchmark_group("wasm_component_guest_functions");
48+
49+
let bench_guest_function = |b: &mut Bencher<'_>, ext| {
50+
let (sb, rt) = get_loaded_wasm_sandbox(ext);
51+
let mut wrapped = bindings::RuncomponentSandbox { sb, rt };
52+
let instance = bindings::example::runcomponent::RuncomponentExports::guest(&mut wrapped);
53+
54+
b.iter(|| {
55+
instance.echo("Hello World!".to_string());
56+
});
57+
};
58+
59+
group.bench_function("wasm_guest_call", |b: &mut Bencher<'_>| {
60+
bench_guest_function(b, "wasm");
61+
});
62+
63+
group.bench_function("wasm_guest_call_aot", |b: &mut Bencher<'_>| {
64+
bench_guest_function(b, "aot");
65+
});
66+
67+
group.finish();
68+
}
69+
70+
fn wasm_component_sandbox_benchmark(c: &mut Criterion) {
71+
let mut group = c.benchmark_group("wasm_component_sandboxes");
72+
let create_wasm_sandbox = || {
73+
get_loaded_wasm_sandbox("wasm");
74+
};
75+
76+
group.bench_function("create_sandbox", |b| {
77+
b.iter_with_large_drop(create_wasm_sandbox);
78+
});
79+
80+
group.bench_function("create_sandbox_and_drop", |b| {
81+
b.iter(create_wasm_sandbox);
82+
});
83+
84+
group.finish();
85+
}
86+
87+
fn get_loaded_wasm_sandbox(
88+
ext: &str,
89+
) -> (
90+
LoadedWasmSandbox,
91+
Arc<Mutex<bindings::RuncomponentResources<State>>>,
92+
) {
93+
let state = State::new();
94+
let mut sandbox = SandboxBuilder::new().build().unwrap();
95+
let rt = bindings::register_host_functions(&mut sandbox, state);
96+
97+
let sb = sandbox.load_runtime().unwrap();
98+
99+
let sb = sb
100+
.load_module(format!("../../x64/release/runcomponent.{ext}",))
101+
.unwrap();
102+
(sb, rt)
103+
}
104+
105+
criterion_group! {
106+
name = benches_components;
107+
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
108+
targets = wasm_component_guest_call_benchmark, wasm_component_sandbox_benchmark
109+
}
110+
criterion_main!(benches_components);

src/hyperlight_wasm/scripts/build-wasm-examples.sh

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,41 @@ else
3333

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

36-
for FILENAME in $(find . -name '*.c')
36+
for FILENAME in $(find . -name '*.c' -not -path './components/*')
3737
do
3838
echo Building ${FILENAME}
3939
# Build the wasm file with wasi-libc for wasmtime
40-
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}
40+
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}
4141

4242
# Build AOT for Wasmtime; note that Wasmtime does not support
4343
# interpreting, so its wasm binary is secretly an AOT binary.
4444
cargo run -p hyperlight-wasm-aot compile ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${OUTPUT_DIR}/${FILENAME%.*}.aot
4545
cp ${OUTPUT_DIR}/${FILENAME%.*}.aot ${OUTPUT_DIR}/${FILENAME%.*}.wasm
4646
done
47+
48+
echo Building components
49+
# Iterate over all .wit files in the components folder
50+
for WIT_FILE in ${PWD}/components/*.wit; do
51+
COMPONENT_NAME=$(basename ${WIT_FILE} .wit)
52+
echo Building component: ${COMPONENT_NAME}
53+
54+
# Generate bindings for the component
55+
wit-bindgen c ${WIT_FILE} --out-dir ${PWD}/components/bindings
56+
57+
# Build the wasm file with wasi-libc for wasmtime
58+
docker run --rm -i -v "${PWD}:/tmp/host" -v "${OUTPUT_DIR}:/tmp/output/" wasm-clang-builder:latest /opt/wasi-sdk/bin/wasm32-wasip2-clang \
59+
-ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 \
60+
-Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors \
61+
-Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections \
62+
-o /tmp/output/${COMPONENT_NAME}-p2.wasm \
63+
/tmp/host/components/${COMPONENT_NAME}.c \
64+
/tmp/host/components/bindings/${COMPONENT_NAME}.c \
65+
/tmp/host/components/bindings/${COMPONENT_NAME}_component_type.o
66+
67+
# Build AOT for Wasmtime
68+
cargo run -p hyperlight-wasm-aot compile --component ${OUTPUT_DIR}/${COMPONENT_NAME}-p2.wasm ${OUTPUT_DIR}/${COMPONENT_NAME}.aot
69+
cp ${OUTPUT_DIR}/${COMPONENT_NAME}.aot ${OUTPUT_DIR}/${COMPONENT_NAME}.wasm
70+
done
4771
fi
4872

4973
popd
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include "bindings/runcomponent.h"
2+
#include <stdlib.h>
3+
#include <string.h>
4+
5+
void exports_example_runcomponent_guest_echo(runcomponent_string_t *msg, runcomponent_string_t *ret)
6+
{
7+
ret->len = msg->len;
8+
ret->ptr = (uint8_t *) malloc(ret->len);
9+
memcpy(ret->ptr, msg->ptr, ret->len);
10+
runcomponent_string_free(msg);
11+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package example:runcomponent;
2+
3+
interface guest {
4+
echo: func(msg: string) -> string;
5+
}
6+
7+
interface host {
8+
get-time-since-boot-microsecond: func() -> s64;
9+
}
10+
11+
world runcomponent {
12+
export guest;
13+
import host;
14+
}

0 commit comments

Comments
 (0)