Add parallel execution example

This commit is contained in:
Mark McCaskey 2019-09-25 17:10:40 -07:00
parent 287b42579c
commit 1083e7cd2a
9 changed files with 278 additions and 1 deletions

View File

@ -6,9 +6,11 @@ Blocks of changes will separated by version increments.
## **[Unreleased]**
- [#835](https://github.com/wasmerio/wasmer/pull/836) Update Cranelift fork version to `0.44.0`
- [#836](https://github.com/wasmerio/wasmer/pull/836) Update Cranelift fork version to `0.44.0`
- [#839](https://github.com/wasmerio/wasmer/pull/839) Change supported version to stable Rust 1.37+
- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when unwraping `wasmer` arguments
- [#835](https://github.com/wasmerio/wasmer/pull/835) Add parallel execution example (independent instances created from the same `ImportObject` and `Module` run with rayon)
- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when parsing numerical arguments for no-ABI targets run with the wasmer binary
- [#833](https://github.com/wasmerio/wasmer/pull/833) Add doc example of using ImportObject's new `maybe_with_namespace` method
- [#832](https://github.com/wasmerio/wasmer/pull/832) Delete unused runtime ABI
- [#809](https://github.com/wasmerio/wasmer/pull/809) Fix bugs leading to panics in `LocalBacking`.

24
Cargo.lock generated
View File

@ -734,6 +734,11 @@ dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "md5"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.2.1"
@ -828,6 +833,24 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parallel"
version = "0.1.0"
dependencies = [
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime 0.7.0",
"wasmer-runtime-core 0.7.0",
]
[[package]]
name = "parallel-guest"
version = "0.1.0"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.9.0"
@ -1884,6 +1907,7 @@ dependencies = [
"checksum llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2110cd4daf9cd8e39dd3b933b1a2a2ac7315e91f7c92b3a20beab526c63b5978"
"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"

View File

@ -57,6 +57,8 @@ members = [
"lib/emscripten-tests",
"lib/middleware-common-tests",
"examples/plugin-for-example",
"examples/parallel",
"examples/parallel-guest",
]
[build-dependencies]

BIN
examples/parallel-guest.wasm Executable file

Binary file not shown.

View File

@ -0,0 +1,11 @@
[package]
name = "parallel-guest"
version = "0.1.0"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
license = "MIT"
edition = "2018"
publish = false
[dependencies]
md5 = "0.6"
lazy_static = "1"

View File

@ -0,0 +1,82 @@
#[macro_use]
extern crate lazy_static;
extern "C" {
fn get_hashed_password(ptr: u32, len: u32) -> u32;
fn print_char(c: u32);
}
fn print_str(s: &str) {
for c in s.chars() {
unsafe { print_char(c as u32) };
}
unsafe { print_char(b'\n' as u32) };
}
fn load_hashed_password() -> Option<String> {
let mut buffer = String::with_capacity(32);
for _ in 0..32 {
buffer.push(0 as char);
}
let result =
unsafe { get_hashed_password(buffer.as_mut_ptr() as u32, buffer.capacity() as u32) };
if result == 0 {
Some(buffer)
} else {
None
}
}
lazy_static! {
static ref HASHED_PASSWORD: String = load_hashed_password().unwrap();
}
static PASSWORD_CHARS: &'static [u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
// for simplicty we define a scheme for mapping numbers onto passwords
fn num_to_password(mut num: u64) -> String {
let mut extra_zero = num == 0;
let mut out = String::new();
while num > 0 {
out.push(PASSWORD_CHARS[num as usize % PASSWORD_CHARS.len()] as char);
extra_zero = extra_zero || num == PASSWORD_CHARS.len() as u64;
num /= PASSWORD_CHARS.len() as u64;
}
if extra_zero {
out.push(PASSWORD_CHARS[0] as char);
}
out
}
#[repr(C)]
struct RetStr {
ptr: u32,
len: u32,
}
// returns a (pointer, len) to the password or null
#[no_mangle]
fn check_password(from: u64, to: u64) -> u64 {
for i in from..to {
let password = num_to_password(i);
let digest = md5::compute(&password);
let hash_as_str = format!("{:x}", digest);
if hash_as_str == *HASHED_PASSWORD {
let ret = RetStr {
ptr: password.as_ptr() as usize as u32,
len: password.len() as u32,
};
// leak the data so ending the function doesn't corrupt it, if we cared the host could free it after
std::mem::forget(password);
return unsafe { std::mem::transmute(ret) };
}
}
return 0;
}
fn main() {}

View File

@ -0,0 +1,14 @@
[package]
name = "parallel"
version = "0.1.0"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
publish = false
license = "MIT"
[dependencies]
rayon = "1.2"
time = "0.1"
wasmer-runtime = { path = "../../lib/runtime" }
wasmer-runtime-core = { path = "../../lib/runtime-core" }

View File

@ -0,0 +1,5 @@
# Parallel Wasmer example
This example shows executing independent code from multiple threads on an "embarassingly parallel" problem
This is a toy example of cracking md5 hashes. This is not a benchmark. This example is not optimized, it will compare poorly to an implementation that is.

View File

@ -0,0 +1,137 @@
use rayon::prelude::*;
use wasmer_runtime::{compile_with, compiler_for_backend, func, imports, instantiate, Backend};
use wasmer_runtime_core::{
memory::ptr::{Array, WasmPtr},
vm::Ctx,
};
static PLUGIN_LOCATION: &'static str = "../parallel-guest.wasm";
fn get_hashed_password(ctx: &mut Ctx, ptr: WasmPtr<u8, Array>, len: u32) -> u32 {
// "hard" password - 7 characters
//let password = b"2ab96390c7dbe3439de74d0c9b0b1767";
// "easy" password - 5 characters
let password = b"ab56b4d92b40713acc5af89985d4b786";
let memory = ctx.memory(0);
if let Some(writer) = ptr.deref(memory, 0, len) {
for (i, byte) in password.iter().enumerate() {
writer[i].set(*byte)
}
0
} else {
u32::max_value()
}
}
#[repr(C)]
struct RetStr {
ptr: u32,
len: u32,
}
fn print_char(_cxt: &mut Ctx, c: u32) {
print!("{}", c as u8 as char);
}
fn main() {
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
"Could not read in WASM plugin at {}",
PLUGIN_LOCATION
));
let imports = imports! {
"env" => {
"get_hashed_password" => func!(get_hashed_password),
"print_char" => func!(print_char),
},
};
let compiler = compiler_for_backend(Backend::default()).unwrap();
let module = compile_with(&wasm_bytes[..], compiler.as_ref()).unwrap();
println!("Parallel");
let start_ts = time::SteadyTime::now();
for outer in 0..1000u64 {
let start = outer * 1000;
let end = start + 1000;
let out = (start..=end)
.into_par_iter()
.filter_map(|i| {
let instance = module
.clone()
.instantiate(&imports)
.expect("failed to instantiate wasm module");
let check_password = instance.func::<(u64, u64), u64>("check_password").unwrap();
let j = i * 10000;
let result = check_password.call(j, j + 10000).unwrap();
print!(".");
use std::io::Write;
std::io::stdout().flush().unwrap();
if result != 0 {
let res: RetStr = unsafe { std::mem::transmute(result) };
let ctx = instance.context();
let memory = ctx.memory(0);
let wasm_ptr: WasmPtr<u8, Array> = WasmPtr::new(res.ptr);
let password_str = wasm_ptr
.get_utf8_string(memory, res.len)
.unwrap()
.to_string();
Some(password_str)
} else {
None
}
})
.find_first(|_: &String| true);
if out.is_some() {
let end_ts = time::SteadyTime::now();
let delta = end_ts - start_ts;
println!(
"Password cracked: \"{}\" in {}.{:03}",
out.unwrap(),
delta.num_seconds(),
(delta.num_milliseconds() % 1000),
);
break;
}
}
println!("Serial:");
let start_ts = time::SteadyTime::now();
let instance =
instantiate(&wasm_bytes[..], &imports).expect("failed to instantiate wasm module");
let check_password = instance.func::<(u64, u64), u64>("check_password").unwrap();
let mut out: Option<RetStr> = None;
for i in (0..=u64::max_value()).step_by(10000) {
let result = check_password.call(i, i + 10000).unwrap();
print!(".");
use std::io::Write;
std::io::stdout().flush().unwrap();
if result != 0 {
out = Some(unsafe { std::mem::transmute(result) });
break;
}
}
println!("");
if let Some(res) = out {
let ctx = instance.context();
let memory = ctx.memory(0);
let wasm_ptr: WasmPtr<u8, Array> = WasmPtr::new(res.ptr);
let password_str = wasm_ptr.get_utf8_string(memory, res.len).unwrap();
let end_ts = time::SteadyTime::now();
let delta = end_ts - start_ts;
println!(
"Password cracked: \"{}\" in {}.{:03}",
password_str,
delta.num_seconds(),
(delta.num_milliseconds() % 1000),
);
} else {
println!("Password not found!");
}
}