From 8652eb2fc0a1ad7c61c2ef95ee763a9d9fb83902 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 28 Jan 2021 11:20:28 -0600 Subject: [PATCH] use executor cache in polkadot-parachain executor (#2342) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use executor cache in polkadot-parachain executor * Update parachain/src/wasm_executor/mod.rs Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher --- polkadot/core-primitives/src/lib.rs | 4 +- polkadot/parachain/src/wasm_executor/mod.rs | 47 ++++++++++++++----- .../src/wasm_executor/validation_host.rs | 4 +- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/polkadot/core-primitives/src/lib.rs b/polkadot/core-primitives/src/lib.rs index d72322cf3f..96d4c00399 100644 --- a/polkadot/core-primitives/src/lib.rs +++ b/polkadot/core-primitives/src/lib.rs @@ -20,11 +20,13 @@ //! //! These core Polkadot types are used by the relay chain and the Parachains. -use sp_runtime::{generic, MultiSignature, traits::{Verify, BlakeTwo256, IdentifyAccount}}; +use sp_runtime::{generic, MultiSignature, traits::{Verify, IdentifyAccount}}; use parity_scale_codec::{Encode, Decode}; #[cfg(feature = "std")] use parity_util_mem::MallocSizeOf; +pub use sp_runtime::traits::{BlakeTwo256, Hash as HashT}; + /// The block number type used by Polkadot. /// 32-bits will allow for 136 years of blocks assuming 1 block per second. pub type BlockNumber = u32; diff --git a/polkadot/parachain/src/wasm_executor/mod.rs b/polkadot/parachain/src/wasm_executor/mod.rs index 90c689f490..5192bf115d 100644 --- a/polkadot/parachain/src/wasm_executor/mod.rs +++ b/polkadot/parachain/src/wasm_executor/mod.rs @@ -160,6 +160,25 @@ pub enum InternalError { WasmWorker(String), } +/// A cache of executors for different parachain Wasm instances. +/// +/// This should be reused across candidate validation instances. +pub struct ExecutorCache(sc_executor::WasmExecutor); + +impl Default for ExecutorCache { + fn default() -> Self { + ExecutorCache(sc_executor::WasmExecutor::new( + #[cfg(all(feature = "wasmtime", not(any(target_os = "android", target_os = "unknown"))))] + sc_executor::WasmExecutionMethod::Compiled, + #[cfg(any(not(feature = "wasmtime"), target_os = "android", target_os = "unknown"))] + sc_executor::WasmExecutionMethod::Interpreted, + // TODO: Make sure we don't use more than 1GB: https://github.com/paritytech/polkadot/issues/699 + Some(1024), + HostFunctions::host_functions(), + 8 + )) + } +} /// Validate a candidate under the given validation code. /// @@ -172,7 +191,12 @@ pub fn validate_candidate( ) -> Result { match isolation_strategy { IsolationStrategy::InProcess => { - validate_candidate_internal(validation_code, ¶ms.encode(), spawner) + validate_candidate_internal( + &ExecutorCache::default(), + validation_code, + ¶ms.encode(), + spawner, + ) }, #[cfg(not(any(target_os = "android", target_os = "unknown")))] IsolationStrategy::ExternalProcessSelfHost(pool) => { @@ -193,20 +217,12 @@ type HostFunctions = sp_io::SubstrateHostFunctions; /// /// This will fail if the validation code is not a proper parachain validation module. pub fn validate_candidate_internal( + executor: &ExecutorCache, validation_code: &[u8], encoded_call_data: &[u8], spawner: impl SpawnNamed + 'static, ) -> Result { - let executor = sc_executor::WasmExecutor::new( - #[cfg(all(feature = "wasmtime", not(any(target_os = "android", target_os = "unknown"))))] - sc_executor::WasmExecutionMethod::Compiled, - #[cfg(any(not(feature = "wasmtime"), target_os = "android", target_os = "unknown"))] - sc_executor::WasmExecutionMethod::Interpreted, - // TODO: Make sure we don't use more than 1GB: https://github.com/paritytech/polkadot/issues/699 - Some(1024), - HostFunctions::host_functions(), - 8 - ); + let executor = &executor.0; let mut extensions = Extensions::new(); extensions.register(sp_core::traits::TaskExecutorExt::new(spawner)); @@ -214,9 +230,16 @@ pub fn validate_candidate_internal( let mut ext = ValidationExternalities(extensions); + // Expensive, but not more-so than recompiling the wasm module. + // And we need this hash to access the `sc_executor` cache. + let code_hash = { + use polkadot_core_primitives::{BlakeTwo256, HashT}; + BlakeTwo256::hash(validation_code) + }; + let res = executor.call_in_wasm( validation_code, - None, + Some(code_hash.as_bytes().to_vec()), "validate_block", encoded_call_data, &mut ext, diff --git a/polkadot/parachain/src/wasm_executor/validation_host.rs b/polkadot/parachain/src/wasm_executor/validation_host.rs index 79de685199..2db89b4bba 100644 --- a/polkadot/parachain/src/wasm_executor/validation_host.rs +++ b/polkadot/parachain/src/wasm_executor/validation_host.rs @@ -151,6 +151,8 @@ pub fn run_worker(mem_id: &str) -> Result<(), String> { memory.set(Event::WorkerReady as usize, EventState::Signaled) .map_err(|e| format!("{} Error setting shared event: {:?}", process::id(), e))?; + let executor = super::ExecutorCache::default(); + loop { if watch_exit.load(atomic::Ordering::Relaxed) { break; @@ -184,7 +186,7 @@ pub fn run_worker(mem_id: &str) -> Result<(), String> { let (call_data, _) = rest.split_at_mut(MAX_RUNTIME_MEM); let (call_data, _) = call_data.split_at_mut(header.params_size as usize); - let result = validate_candidate_internal(code, call_data, task_executor.clone()); + let result = validate_candidate_internal(&executor, code, call_data, task_executor.clone()); debug!(target: LOG_TARGET, "{} Candidate validated: {:?}", process::id(), result); match result {