Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
WASM execution optimizations
  • Loading branch information
arkpar committed Jul 31, 2018
commit 95a1c8f4e5a1994fae46e395626078d7bb70edbc
12 changes: 6 additions & 6 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion polkadot/parachain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description = "Types and utilities for creating and working with parachains"

[dependencies]
substrate-codec = { path = "../../substrate/codec", default-features = false }
wasmi = { version = "0.3", optional = true }
wasmi = { version = "0.4", optional = true }
error-chain = { version = "0.12", optional = true }

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions substrate/executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ substrate-runtime-version = { path = "../runtime/version" }
ed25519 = { path = "../ed25519" }
serde = "1.0"
serde_derive = "1.0"
wasmi = "0.3"
wasmi = "0.4"
byteorder = "1.1"
rustc-hex = "1.0.0"
triehash = "0.1.0"
Expand All @@ -30,4 +30,4 @@ wabt = "0.4"

[features]
default = []
wasm-extern-trace = []
wasm-extern-trace = []
16 changes: 1 addition & 15 deletions substrate/executor/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,7 @@ error_chain! {
description("invalid memory reference"),
display("Invalid memory reference"),
}

/// Retry, please.
PleaseRetry {
description("retry needed"),
display("Retry needed"),
}
}
}

impl state_machine::Error for Error {
fn needs_retry(&self) -> bool {
if let ErrorKind::PleaseRetry = self.0 {
true
} else {
false
}
}
}
impl state_machine::Error for Error {}
37 changes: 17 additions & 20 deletions substrate/executor/src/native_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ use RuntimeInfo;

// For the internal Runtime Cache:
// Is it compatible enough to run this natively or do we need to fall back on the WasmModule

enum Compatibility {
InvalidVersion(WasmModule),
IsCompatible(RuntimeVersion),
IsCompatible(RuntimeVersion, WasmModule),
Copy link
Member Author

Choose a reason for hiding this comment

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

If natve+wasm execution is requested this should still cache the module. Wasm execution will be performed even if the version is compatible.

NotCompatible(RuntimeVersion, WasmModule)
}

unsafe impl Send for Compatibility {}

type CacheType = HashMap<u64, Compatibility>;

lazy_static! {
Expand All @@ -54,6 +57,7 @@ fn gen_cache_key(code: &[u8]) -> u64 {
/// the runtime version entry for `code`, determines whether `Compatibility::IsCompatible`
/// can be used by by comparing returned RuntimeVersion to `ref_version`
fn fetch_cached_runtime_version<'a, E: Externalities>(
wasm_executor: &WasmExecutor,
cache: &'a mut MutexGuard<CacheType>,
ext: &mut E,
code: &[u8],
Expand All @@ -62,12 +66,12 @@ fn fetch_cached_runtime_version<'a, E: Externalities>(
cache.entry(gen_cache_key(code))
.or_insert_with(|| {
let module = WasmModule::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed");
let version = WasmExecutor::new(8, 8).call_in_wasm_module(ext, &module, "version", &[]).ok()
let version = wasm_executor.call_in_wasm_module(ext, &module, "version", &[]).ok()
.and_then(|v| RuntimeVersion::decode(&mut v.as_slice()));

if let Some(v) = version {
if ref_version.can_call_with(&v) {
Compatibility::IsCompatible(v)
Compatibility::IsCompatible(v, module)
} else {
Compatibility::NotCompatible(v, module)
}
Expand Down Expand Up @@ -109,8 +113,8 @@ pub trait NativeExecutionDispatch: Send + Sync {
const VERSION: RuntimeVersion;

/// Construct corresponding `NativeExecutor` with given `heap_pages`.
fn with_heap_pages(min_heap_pages: usize, max_heap_pages: usize) -> NativeExecutor<Self> where Self: Sized {
NativeExecutor::with_heap_pages(min_heap_pages, max_heap_pages)
fn with_heap_pages(max_heap_pages: usize) -> NativeExecutor<Self> where Self: Sized {
NativeExecutor::with_heap_pages(max_heap_pages)
}
}

Expand All @@ -126,15 +130,10 @@ pub struct NativeExecutor<D: NativeExecutionDispatch> {

impl<D: NativeExecutionDispatch> NativeExecutor<D> {
/// Create new instance with specific number of pages for wasm fallback's heap.
pub fn with_heap_pages(min_heap_pages: usize, max_heap_pages: usize) -> Self {
// FIXME: set this entry at compile time
RUNTIMES_CACHE.lock().insert(
gen_cache_key(D::native_equivalent()),
Compatibility::IsCompatible(D::VERSION));

pub fn with_heap_pages(max_heap_pages: usize) -> Self {
NativeExecutor {
_dummy: Default::default(),
fallback: WasmExecutor::new(min_heap_pages, max_heap_pages),
fallback: WasmExecutor::new(max_heap_pages),
}
}
}
Expand All @@ -157,8 +156,8 @@ impl<D: NativeExecutionDispatch> RuntimeInfo for NativeExecutor<D> {
code: &[u8],
) -> Option<RuntimeVersion> {
let mut c = RUNTIMES_CACHE.lock();
match fetch_cached_runtime_version(&mut c, ext, code, D::VERSION) {
Compatibility::IsCompatible(v) | Compatibility::NotCompatible(v, _) => Some(v.clone()),
match fetch_cached_runtime_version(&self.fallback, &mut c, ext, code, D::VERSION) {
Compatibility::IsCompatible(v, _) | Compatibility::NotCompatible(v, _) => Some(v.clone()),
Compatibility::InvalidVersion(_m) => None
}
}
Expand All @@ -176,11 +175,9 @@ impl<D: NativeExecutionDispatch> CodeExecutor for NativeExecutor<D> {
use_native: bool,
) -> (Result<Vec<u8>>, bool) {
let mut c = RUNTIMES_CACHE.lock();
match (use_native, fetch_cached_runtime_version(&mut c, ext, code, D::VERSION)) {
(_, Compatibility::NotCompatible(_, m)) | (_, Compatibility::InvalidVersion(m)) =>
match (use_native, fetch_cached_runtime_version(&self.fallback, &mut c, ext, code, D::VERSION)) {
(_, Compatibility::NotCompatible(_, m)) | (_, Compatibility::InvalidVersion(m)) | (false, Compatibility::IsCompatible(_, m)) =>
(self.fallback.call_in_wasm_module(ext, m, method, data), false),
(false, _) =>
(self.fallback.call(ext, code, method, data, false).0, false),
_ => (D::dispatch(ext, method, data), true),
}
}
Expand Down Expand Up @@ -211,8 +208,8 @@ macro_rules! native_executor_instance {
.ok_or_else(|| $crate::error::ErrorKind::MethodNotFound(method.to_owned()).into())
}

fn with_heap_pages(min_heap_pages: usize, max_heap_pages: usize) -> $crate::NativeExecutor<$name> {
$crate::NativeExecutor::with_heap_pages(min_heap_pages, max_heap_pages)
fn with_heap_pages(max_heap_pages: usize) -> $crate::NativeExecutor<$name> {
$crate::NativeExecutor::with_heap_pages(max_heap_pages)
}
}
}
Expand Down
66 changes: 8 additions & 58 deletions substrate/executor/src/wasm_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@
//! Rust implementation of Substrate contracts.

use std::cmp::Ordering;
use parking_lot::Mutex;
use std::collections::HashMap;
use wasmi::{
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder
};
use wasmi::RuntimeValue::{I32, I64};
use wasmi::memory_units::{Pages, Bytes};
use state_machine::{Externalities, CodeExecutor};
use state_machine::Externalities;
use error::{Error, ErrorKind, Result};
use wasm_utils::UserError;
use primitives::{blake2_256, twox_128, twox_256};
Expand All @@ -46,9 +45,8 @@ impl Heap {
/// This could mean that wasm binary specifies memory
/// limit and we are trying to allocate beyond that limit.
fn new(memory: &MemoryRef, pages: usize) -> Result<Self> {
let prev_page_count = memory
.grow(Pages(pages))
.map_err(|_| Error::from(ErrorKind::Runtime))?;
let prev_page_count = memory.initial();
memory.grow(Pages(pages)).map_err(|_| Error::from(ErrorKind::Runtime))?;
Ok(Heap {
end: Bytes::from(prev_page_count).0 as u32,
})
Expand Down Expand Up @@ -486,35 +484,24 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
/// Executes the provided code in a sandboxed wasm runtime.
#[derive(Debug)]
pub struct WasmExecutor {
/// The min number of pages to allocate for the heap.
pub min_heap_pages: usize,
/// The max number of pages to allocate for the heap.
pub max_heap_pages: usize,

try_heap_pages: Mutex<(usize, usize)>,
}

impl Clone for WasmExecutor {
fn clone(&self) -> Self {
WasmExecutor {
min_heap_pages: self.min_heap_pages,
max_heap_pages: self.max_heap_pages,
try_heap_pages: Mutex::new((self.min_heap_pages, 0)),
}
}
}

// Number of executions to continue with the old heap_pages before reducing to the next lowest POT.
const DECAY_TIMEOUT: usize = 16;

impl WasmExecutor {

/// Create a new instance.
pub fn new(min_heap_pages: usize, max_heap_pages: usize) -> Self {
pub fn new(max_heap_pages: usize) -> Self {
WasmExecutor {
min_heap_pages,
max_heap_pages,
try_heap_pages: Mutex::new((min_heap_pages, 0)),
}
}

Expand Down Expand Up @@ -549,20 +536,14 @@ impl WasmExecutor {
.export_by_name("__indirect_function_table")
.and_then(|e| e.as_table().cloned());

let mut try_heap_pages = self.try_heap_pages.lock();
let mut fec = FunctionExecutor::new(memory.clone(), try_heap_pages.0, table, ext)?;
let mut fec = FunctionExecutor::new(memory.clone(), self.max_heap_pages, table, ext)?;

// finish instantiation by running 'start' function (if any).
let instance = intermediate_instance.run_start(&mut fec)?;

let size = data.len() as u32;
let offset = fec.heap.allocate(size);
if let Err(_) = memory.set(offset, &data) {
let old = try_heap_pages.0;
*try_heap_pages = ((old * 2).min(self.max_heap_pages), DECAY_TIMEOUT);
trace!(target: "wasm-executor", "Shrunk heap size too small at {} pages. Retrying with {}", old, try_heap_pages.0);
return Err(ErrorKind::PleaseRetry.into())
}
memory.set(offset, &data)?;

let result = instance.invoke_export(
method,
Expand All @@ -575,27 +556,12 @@ impl WasmExecutor {

let returned = match result {
Ok(x) => x,
Err(_) if try_heap_pages.0 < self.max_heap_pages => {
let old = try_heap_pages.0;
*try_heap_pages = ((old * 2).min(self.max_heap_pages), DECAY_TIMEOUT);
trace!(target: "wasm-executor", "Shrunk heap size too small at {} pages. Retrying with {}", old, try_heap_pages.0);
return Err(ErrorKind::PleaseRetry.into())
}
Err(e) => {
trace!(target: "wasm-executor", "Failed to execute code with {} pages", try_heap_pages.0);
trace!(target: "wasm-executor", "Failed to execute code with {} pages", self.max_heap_pages);
return Err(e.into())
},
};

let decay_timeout = try_heap_pages.1;
if decay_timeout == 0 {
if try_heap_pages.0 > self.min_heap_pages {
*try_heap_pages = (self.min_heap_pages.max(try_heap_pages.0 - 1), DECAY_TIMEOUT);
}
} else {
try_heap_pages.1 -= 1;
}

if let Some(I64(r)) = returned {
let offset = r as u32;
let length = (r >> 32) as u32 as usize;
Expand All @@ -607,22 +573,6 @@ impl WasmExecutor {
}
}

impl CodeExecutor for WasmExecutor {
type Error = Error;

fn call<E: Externalities>(
&self,
ext: &mut E,
code: &[u8],
method: &str,
data: &[u8],
_use_native: bool
) -> (Result<Vec<u8>>, bool) {
(Module::from_buffer(code).map_err(Into::into).and_then(|module|
self.call_in_wasm_module(ext, &module, method, data)
), false)
}
}

#[cfg(test)]
mod tests {
Expand Down
2 changes: 1 addition & 1 deletion substrate/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ uint = { git = "https://github.com/rphmeier/primitives.git", branch = "compile-f
twox-hash = { version = "1.1.0", optional = true }
byteorder = { version = "1.1", default_features = false }
blake2-rfc = { version = "0.2.18", optional = true }
wasmi = { version = "0.3", optional = true }
wasmi = { version = "0.4", optional = true }

[dev-dependencies]
substrate-serializer = { path = "../serializer" }
Expand Down
2 changes: 1 addition & 1 deletion substrate/runtime-sandbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build = "build.rs"
rustc_version = "0.2"

[dependencies]
wasmi = { version = "0.3", optional = true }
wasmi = { version = "0.4", optional = true }
substrate-primitives = { path = "../primitives", default_features = false }
substrate-runtime-std = { path = "../runtime-std", default_features = false }
substrate-runtime-io = { path = "../runtime-io", default_features = false }
Expand Down
4 changes: 2 additions & 2 deletions substrate/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub struct Service<Components: components::Components> {
pub fn new_client<Factory: components::ServiceFactory>(config: FactoryFullConfiguration<Factory>)
-> Result<Arc<ComponentClient<components::FullComponents<Factory>>>, error::Error>
{
let executor = NativeExecutor::with_heap_pages(config.min_heap_pages, config.max_heap_pages);
let executor = NativeExecutor::with_heap_pages(config.max_heap_pages);
let (client, _) = components::FullComponents::<Factory>::build_client(
&config,
executor,
Expand All @@ -118,7 +118,7 @@ impl<Components> Service<Components>
let (signal, exit) = ::exit_future::signal();

// Create client
let executor = NativeExecutor::with_heap_pages(config.min_heap_pages, config.max_heap_pages);
let executor = NativeExecutor::with_heap_pages(config.max_heap_pages);

let mut keystore = Keystore::open(config.keystore_path.as_str().into())?;
for seed in &config.keys {
Expand Down
Loading