diff --git a/crates/herkos-tests/benches/herkos_runtime_benchmark.rs b/crates/herkos-tests/benches/herkos_runtime_benchmark.rs index a2cfa0f..6d315f0 100644 --- a/crates/herkos-tests/benches/herkos_runtime_benchmark.rs +++ b/crates/herkos-tests/benches/herkos_runtime_benchmark.rs @@ -24,11 +24,28 @@ fn fibo_20_orig_bench(c: &mut Criterion) { c.bench_function("fib 20 plain rust", |b| b.iter(|| fibo_orig(black_box(20)))); } +// ─── Memory-intensive benchmarks ───────────────────────────────────────────── + +fn memsort_500_wasm_bench(c: &mut Criterion) { + let mut m = rust_e2e_memory_bench::new().unwrap(); + c.bench_function("memsort 500 wasm transpiled to rust", |b| { + b.iter(|| m.mem_fill_sort_sum(black_box(500), black_box(42))) + }); +} + +fn memsort_500_orig_bench(c: &mut Criterion) { + c.bench_function("memsort 500 plain rust", |b| { + b.iter(|| mem_fill_sort_sum_orig(black_box(500), black_box(42))) + }); +} + criterion_group!( benches, fibo_5_wasm_bench, fibo_5_orig_bench, fibo_20_wasm_bench, - fibo_20_orig_bench + fibo_20_orig_bench, + memsort_500_wasm_bench, + memsort_500_orig_bench ); criterion_main!(benches); diff --git a/crates/herkos-tests/build.rs b/crates/herkos-tests/build.rs index b619166..22af1ea 100644 --- a/crates/herkos-tests/build.rs +++ b/crates/herkos-tests/build.rs @@ -118,6 +118,24 @@ fn process_rust_e2e_files( return Ok(Vec::new()); } + // Copy common include files to OUT_DIR so include!() paths resolve + // when rustc compiles the copied sources. + let common_dir = rust_dir.join("common"); + if common_dir.exists() { + let out_common = out_dir.join("common"); + fs::create_dir_all(&out_common).context("failed to create common/ in OUT_DIR")?; + for entry in fs::read_dir(&common_dir).context("failed to read data/rust/common/")? { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + let dest = out_common.join(path.file_name().unwrap()); + fs::copy(&path, &dest).with_context(|| { + format!("failed to copy {} to OUT_DIR/common/", path.display()) + })?; + } + } + } + let mut entries = collect_files_with_ext(rust_dir, "rs")?; entries.sort(); eprintln!( diff --git a/crates/herkos-tests/data/rust/common/fibo.rs b/crates/herkos-tests/data/rust/common/fibo.rs new file mode 100644 index 0000000..43d3706 --- /dev/null +++ b/crates/herkos-tests/data/rust/common/fibo.rs @@ -0,0 +1,14 @@ +fn fibo_impl(n: i32) -> i32 { + if n <= 1 { + n + } else { + let mut a: i32 = 0; + let mut b: i32 = 1; + for _ in 2..=n { + let tmp = a.wrapping_add(b); + a = b; + b = tmp; + } + b + } +} diff --git a/crates/herkos-tests/data/rust/common/fill_sort_sum.rs b/crates/herkos-tests/data/rust/common/fill_sort_sum.rs new file mode 100644 index 0000000..d6bc5cb --- /dev/null +++ b/crates/herkos-tests/data/rust/common/fill_sort_sum.rs @@ -0,0 +1,29 @@ +fn fill_sort_sum_impl(buf: &mut [i32; 1024], n: i32, seed: i32) -> i32 { + if n <= 0 { + return 0; + } + let n = (n as usize).min(buf.len()); + + // Fill with LCG pseudo-random values + let mut rng = seed; + for item in buf.iter_mut().take(n) { + rng = rng.wrapping_mul(1103515245).wrapping_add(12345); + *item = rng; + } + + // Bubble sort — indexing required for random-access swaps + for i in 0..n { + for j in 0..(n - 1 - i) { + if buf[j] > buf[j + 1] { + buf.swap(j, j + 1); + } + } + } + + // Wrapping checksum + let mut sum: i32 = 0; + for item in buf.iter().take(n) { + sum = sum.wrapping_add(*item); + } + sum +} diff --git a/crates/herkos-tests/data/rust/rust_e2e_arith.rs b/crates/herkos-tests/data/rust/rust_e2e_arith.rs index ffc34dd..a06cc1c 100644 --- a/crates/herkos-tests/data/rust/rust_e2e_arith.rs +++ b/crates/herkos-tests/data/rust/rust_e2e_arith.rs @@ -19,20 +19,11 @@ pub extern "C" fn sum_recursive(n: i32) -> i32 { } } +include!("common/fibo.rs"); + #[no_mangle] pub extern "C" fn fibo(n: i32) -> i32 { - if n <= 1 { - n - } else { - let mut a: i32 = 0; - let mut b: i32 = 1; - for _ in 2..=n { - let tmp = a.wrapping_add(b); - a = b; - b = tmp; - } - b - } + fibo_impl(n) } #[no_mangle] diff --git a/crates/herkos-tests/data/rust/rust_e2e_memory_bench.rs b/crates/herkos-tests/data/rust/rust_e2e_memory_bench.rs new file mode 100644 index 0000000..847ba5c --- /dev/null +++ b/crates/herkos-tests/data/rust/rust_e2e_memory_bench.rs @@ -0,0 +1,27 @@ +#![no_std] + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +include!("common/fill_sort_sum.rs"); + +// Work buffer in the Wasm data segment (zero-initialized, goes to BSS). +// 1024 i32s = 4 KiB — fits comfortably in 2-page memory (128 KiB). +static mut BUF: [i32; 1024] = [0i32; 1024]; + +/// Fill the work buffer with pseudo-random values, bubble sort them in-place, +/// and return a wrapping checksum (sum of all sorted values). +/// +/// Memory access profile (for `n` elements): +/// - Fill: n stores +/// - Sort: O(n²) loads + conditional stores (bubble sort) +/// - Sum: n loads +/// +/// `n`: number of i32 elements to process (capped at 1024) +/// `seed`: initial value for the LCG pseudo-random generator +#[no_mangle] +pub extern "C" fn mem_fill_sort_sum(n: i32, seed: i32) -> i32 { + unsafe { fill_sort_sum_impl(&mut BUF, n, seed) } +} diff --git a/crates/herkos-tests/src/lib.rs b/crates/herkos-tests/src/lib.rs index 76aa1ce..2e726e4 100644 --- a/crates/herkos-tests/src/lib.rs +++ b/crates/herkos-tests/src/lib.rs @@ -1,17 +1,21 @@ // Include generated modules from build.rs (in OUT_DIR) include!(concat!(env!("OUT_DIR"), "/mod.rs")); +// Shared algorithm implementations used by both the transpiled Wasm modules +// and the native Rust baselines below. +include!("../data/rust/common/fibo.rs"); +include!("../data/rust/common/fill_sort_sum.rs"); + pub fn fibo_orig(n: i32) -> i32 { - if n <= 1 { - n - } else { - let mut a: i32 = 0; - let mut b: i32 = 1; - for _ in 2..=n { - let tmp = a.wrapping_add(b); - a = b; - b = tmp; - } - b - } + fibo_impl(n) +} + +/// Native Rust baseline for the memory-intensive fill+sort+sum benchmark. +/// +/// Uses a stack-allocated array with direct indexing (no bounds checks in +/// release mode). This is the "best case" that the transpiled Wasm version +/// is compared against. +pub fn mem_fill_sort_sum_orig(n: i32, seed: i32) -> i32 { + let mut buf = [0i32; 1024]; + fill_sort_sum_impl(&mut buf, n, seed) }