Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Improve Runtime execution by caching runtime lookup
Browse files Browse the repository at this point in the history
Cache whether the native or wasm runtime should be used for a given
code and if the latter, keep around the parsed wasmi::Module for faster
execution.

Refs #257, #275
  • Loading branch information
gnunicorn committed Jul 3, 2018
1 parent 7cf7812 commit 0ddd57a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 19 deletions.
5 changes: 4 additions & 1 deletion Cargo.lock

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

3 changes: 3 additions & 0 deletions substrate/executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ byteorder = "1.1"
rustc-hex = "1.0.0"
triehash = "0.1.0"
hex-literal = "0.1.0"
twox-hash = "1.1.0"
lazy_static = "1.0"
parking_lot = "*"
log = "0.3"

[dev-dependencies]
Expand Down
5 changes: 5 additions & 0 deletions substrate/executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ extern crate wasmi;
extern crate byteorder;
extern crate rustc_hex;
extern crate triehash;
extern crate parking_lot;
extern crate twox_hash;
#[macro_use] extern crate log;

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate error_chain;

Expand Down
58 changes: 47 additions & 11 deletions substrate/executor/src/native_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,33 @@
use error::{Error, ErrorKind, Result};
use state_machine::{CodeExecutor, Externalities};
use wasm_executor::WasmExecutor;
use wasmi::Module as WasmModule;
use runtime_version::RuntimeVersion;
use std::collections::HashMap;
use codec::Slicable;
use twox_hash::XxHash;
use std::hash::Hasher;
use parking_lot::Mutex;
use RuntimeInfo;

// For the internal Runtime Cache
enum RunWith {
NativeRuntime,
WasmRuntime(WasmModule)
}

lazy_static! {
static ref RUNTIMES_CACHE: Mutex<HashMap<u64, RunWith>> = Mutex::new(HashMap::new());
}

// helper function to generate low-over-head caching_keys
fn gen_cache_key(code: &[u8]) -> u64 {
let mut h = XxHash::with_seed(0);
h.write(code);
h.finish()
}


fn safe_call<F, U>(f: F) -> Result<U>
where F: ::std::panic::UnwindSafe + FnOnce() -> U
{
Expand Down Expand Up @@ -60,6 +83,11 @@ pub struct NativeExecutor<D: NativeExecutionDispatch + Sync + Send> {
impl<D: NativeExecutionDispatch + Sync + Send> NativeExecutor<D> {
/// Create new instance.
pub fn new() -> Self {
// FIXME: set this entry at compile time
RUNTIMES_CACHE.lock().insert(
gen_cache_key(D::native_equivalent()),
RunWith::NativeRuntime);

NativeExecutor {
_dummy: Default::default(),
}
Expand Down Expand Up @@ -88,17 +116,25 @@ impl<D: NativeExecutionDispatch + Sync + Send> CodeExecutor for NativeExecutor<D
method: &str,
data: &[u8],
) -> Result<Vec<u8>> {
if code == D::native_equivalent() {
// call native
D::dispatch(ext, method, data)
} else {
let version = WasmExecutor.call(ext, code, "version", &[])?;
let version = RuntimeVersion::decode(&mut version.as_slice());
if version.map_or(false, |v| D::VERSION.can_call_with(&v)) {
return D::dispatch(ext, method, data)
}
// call into wasm.
WasmExecutor.call(ext, code, method, data)
match RUNTIMES_CACHE.lock().entry(gen_cache_key(code)).or_insert_with(|| {
let wasm_module = WasmModule::from_buffer(code)
.expect("all modules compiled with rustc are valid wasm code; qed");

match WasmExecutor.call_in_wasm_module(ext, &wasm_module, "version", &[]) {
Ok(version) => {
let version = RuntimeVersion::decode(&mut version.as_slice());
if version.map_or(false, |v| D::VERSION.can_call_with(&v)) {
RunWith::NativeRuntime
} else {
RunWith::WasmRuntime(wasm_module)
}
},
// broken or old runtime, run with native
_ => RunWith::NativeRuntime
}
}) {
RunWith::NativeRuntime => D::dispatch(ext, method, data),
RunWith::WasmRuntime(module) => WasmExecutor.call_in_wasm_module(ext, &module, method, data)
}
}
}
Expand Down
28 changes: 21 additions & 7 deletions substrate/executor/src/wasm_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,27 +457,26 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
=> <'e, E: Externalities + 'e>
);


/// Wasm rust executor for contracts.
///
/// Executes the provided code in a sandboxed wasm runtime.
#[derive(Debug, Default, Clone)]
pub struct WasmExecutor;

impl CodeExecutor for WasmExecutor {
type Error = Error;
impl WasmExecutor {

fn call<E: Externalities>(
/// Call a given method in the given wasm-module runtime.
pub fn call_in_wasm_module<E: Externalities>(
&self,
ext: &mut E,
code: &[u8],
module: &Module,
method: &str,
data: &[u8],
) -> Result<Vec<u8>> {
let module = Module::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed");

// start module instantiation. Don't run 'start' function yet.
let intermediate_instance = ModuleInstance::new(
&module,
module,
&ImportsBuilder::new()
.with_resolver("env", FunctionExecutor::<E>::resolver())
)?;
Expand Down Expand Up @@ -524,6 +523,21 @@ impl CodeExecutor for WasmExecutor {
}
}

impl CodeExecutor for WasmExecutor {
type Error = Error;

fn call<E: Externalities>(
&self,
ext: &mut E,
code: &[u8],
method: &str,
data: &[u8],
) -> Result<Vec<u8>> {
let module = Module::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed");
self.call_in_wasm_module(ext, &module, method, data)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down

0 comments on commit 0ddd57a

Please sign in to comment.