Skip to content
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

Add parallel execution example #835

Merged
merged 1 commit into from
Sep 27, 2019
Merged
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
24 changes: 24 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ members = [
"lib/emscripten-tests",
"lib/middleware-common-tests",
"examples/plugin-for-example",
"examples/parallel",
"examples/parallel-guest",
]

[build-dependencies]
Expand Down
Binary file added examples/parallel-guest.wasm
Binary file not shown.
11 changes: 11 additions & 0 deletions examples/parallel-guest/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end of line please?

82 changes: 82 additions & 0 deletions examples/parallel-guest/src/main.rs
Original file line number Diff line number Diff line change
@@ -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() {}
14 changes: 14 additions & 0 deletions examples/parallel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
5 changes: 5 additions & 0 deletions examples/parallel/README.md
Original file line number Diff line number Diff line change
@@ -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.
137 changes: 137 additions & 0 deletions examples/parallel/src/main.rs
Original file line number Diff line number Diff line change
@@ -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!");
}
}