From 0ddd57a691fff421714cb790a0e3c38cb62e2c9b Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 3 Jul 2018 17:30:38 +0200 Subject: [PATCH] Improve Runtime execution by caching runtime lookup 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 --- Cargo.lock | 5 +- substrate/executor/Cargo.toml | 3 ++ substrate/executor/src/lib.rs | 5 ++ substrate/executor/src/native_executor.rs | 58 ++++++++++++++++++----- substrate/executor/src/wasm_executor.rs | 28 ++++++++--- 5 files changed, 80 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b21f2c3a5cb5..1db6095a8c54f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1718,7 +1718,7 @@ dependencies = [ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2161,7 +2161,9 @@ dependencies = [ "ed25519 0.1.0", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2172,6 +2174,7 @@ dependencies = [ "substrate-serializer 0.1.0", "substrate-state-machine 0.1.0", "triehash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/substrate/executor/Cargo.toml b/substrate/executor/Cargo.toml index e2dbb56eed0a7..c974baf97c61f 100644 --- a/substrate/executor/Cargo.toml +++ b/substrate/executor/Cargo.toml @@ -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] diff --git a/substrate/executor/src/lib.rs b/substrate/executor/src/lib.rs index 2983cf9f54e33..352106a74d2eb 100644 --- a/substrate/executor/src/lib.rs +++ b/substrate/executor/src/lib.rs @@ -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; diff --git a/substrate/executor/src/native_executor.rs b/substrate/executor/src/native_executor.rs index 5625082604c12..41108a49327e6 100644 --- a/substrate/executor/src/native_executor.rs +++ b/substrate/executor/src/native_executor.rs @@ -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> = 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: F) -> Result where F: ::std::panic::UnwindSafe + FnOnce() -> U { @@ -60,6 +83,11 @@ pub struct NativeExecutor { impl NativeExecutor { /// 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(), } @@ -88,17 +116,25 @@ impl CodeExecutor for NativeExecutor Result> { - 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) } } } diff --git a/substrate/executor/src/wasm_executor.rs b/substrate/executor/src/wasm_executor.rs index d230cc19865d1..9140992f21dc0 100644 --- a/substrate/executor/src/wasm_executor.rs +++ b/substrate/executor/src/wasm_executor.rs @@ -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( + /// Call a given method in the given wasm-module runtime. + pub fn call_in_wasm_module( &self, ext: &mut E, - code: &[u8], + module: &Module, method: &str, data: &[u8], ) -> Result> { - 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::::resolver()) )?; @@ -524,6 +523,21 @@ impl CodeExecutor for WasmExecutor { } } +impl CodeExecutor for WasmExecutor { + type Error = Error; + + fn call( + &self, + ext: &mut E, + code: &[u8], + method: &str, + data: &[u8], + ) -> Result> { + 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::*;