mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-15 17:21:08 +00:00
WasmExecutor takes a cache directory (#8057)
That is useful for executors like wasmtime which produces compiled code and can actually benefit from caching under some circumstances
This commit is contained in:
@@ -75,6 +75,7 @@ fn call_in_wasm<E: Externalities>(
|
|||||||
Some(1024),
|
Some(1024),
|
||||||
HostFunctions::host_functions(),
|
HostFunctions::host_functions(),
|
||||||
8,
|
8,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
executor.call_in_wasm(
|
executor.call_in_wasm(
|
||||||
&wasm_binary_unwrap()[..],
|
&wasm_binary_unwrap()[..],
|
||||||
@@ -536,6 +537,7 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) {
|
|||||||
Some(17), // `17` is the initial number of pages compiled into the binary.
|
Some(17), // `17` is the initial number of pages compiled into the binary.
|
||||||
HostFunctions::host_functions(),
|
HostFunctions::host_functions(),
|
||||||
8,
|
8,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let err = executor.call_in_wasm(
|
let err = executor.call_in_wasm(
|
||||||
@@ -558,6 +560,7 @@ fn returns_mutable_static(wasm_method: WasmExecutionMethod) {
|
|||||||
&wasm_binary_unwrap()[..],
|
&wasm_binary_unwrap()[..],
|
||||||
HostFunctions::host_functions(),
|
HostFunctions::host_functions(),
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
).expect("Creates runtime");
|
).expect("Creates runtime");
|
||||||
|
|
||||||
let instance = runtime.new_instance().unwrap();
|
let instance = runtime.new_instance().unwrap();
|
||||||
@@ -591,6 +594,7 @@ fn restoration_of_globals(wasm_method: WasmExecutionMethod) {
|
|||||||
&wasm_binary_unwrap()[..],
|
&wasm_binary_unwrap()[..],
|
||||||
HostFunctions::host_functions(),
|
HostFunctions::host_functions(),
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
).expect("Creates runtime");
|
).expect("Creates runtime");
|
||||||
let instance = runtime.new_instance().unwrap();
|
let instance = runtime.new_instance().unwrap();
|
||||||
|
|
||||||
@@ -611,6 +615,7 @@ fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) {
|
|||||||
&wasm_binary_unwrap()[..],
|
&wasm_binary_unwrap()[..],
|
||||||
HostFunctions::host_functions(),
|
HostFunctions::host_functions(),
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
).expect("Creates runtime");
|
).expect("Creates runtime");
|
||||||
let instance = runtime.new_instance().unwrap();
|
let instance = runtime.new_instance().unwrap();
|
||||||
|
|
||||||
@@ -634,6 +639,7 @@ fn parallel_execution(wasm_method: WasmExecutionMethod) {
|
|||||||
Some(1024),
|
Some(1024),
|
||||||
HostFunctions::host_functions(),
|
HostFunctions::host_functions(),
|
||||||
8,
|
8,
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
let code_hash = blake2_256(wasm_binary_unwrap()).to_vec();
|
let code_hash = blake2_256(wasm_binary_unwrap()).to_vec();
|
||||||
let threads: Vec<_> = (0..8).map(|_|
|
let threads: Vec<_> = (0..8).map(|_|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ mod tests {
|
|||||||
Some(8),
|
Some(8),
|
||||||
sp_io::SubstrateHostFunctions::host_functions(),
|
sp_io::SubstrateHostFunctions::host_functions(),
|
||||||
8,
|
8,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
let res = executor.call_in_wasm(
|
let res = executor.call_in_wasm(
|
||||||
&wasm_binary_unwrap()[..],
|
&wasm_binary_unwrap()[..],
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use std::{
|
|||||||
panic::{UnwindSafe, AssertUnwindSafe},
|
panic::{UnwindSafe, AssertUnwindSafe},
|
||||||
result,
|
result,
|
||||||
sync::{Arc, atomic::{AtomicU64, Ordering}, mpsc},
|
sync::{Arc, atomic::{AtomicU64, Ordering}, mpsc},
|
||||||
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
use sp_version::{NativeVersion, RuntimeVersion};
|
use sp_version::{NativeVersion, RuntimeVersion};
|
||||||
@@ -102,6 +103,9 @@ pub struct WasmExecutor {
|
|||||||
cache: Arc<RuntimeCache>,
|
cache: Arc<RuntimeCache>,
|
||||||
/// The size of the instances cache.
|
/// The size of the instances cache.
|
||||||
max_runtime_instances: usize,
|
max_runtime_instances: usize,
|
||||||
|
/// The path to a directory which the executor can leverage for a file cache, e.g. put there
|
||||||
|
/// compiled artifacts.
|
||||||
|
cache_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmExecutor {
|
impl WasmExecutor {
|
||||||
@@ -112,19 +116,30 @@ impl WasmExecutor {
|
|||||||
/// `method` - Method used to execute Wasm code.
|
/// `method` - Method used to execute Wasm code.
|
||||||
///
|
///
|
||||||
/// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution.
|
/// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution.
|
||||||
/// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided.
|
/// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided.
|
||||||
|
///
|
||||||
|
/// `host_functions` - The set of host functions to be available for import provided by this
|
||||||
|
/// executor.
|
||||||
|
///
|
||||||
|
/// `max_runtime_instances` - The number of runtime instances to keep in memory ready for reuse.
|
||||||
|
///
|
||||||
|
/// `cache_path` - A path to a directory where the executor can place its files for purposes of
|
||||||
|
/// caching. This may be important in cases when there are many different modules with the
|
||||||
|
/// compiled execution method is used.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
method: WasmExecutionMethod,
|
method: WasmExecutionMethod,
|
||||||
default_heap_pages: Option<u64>,
|
default_heap_pages: Option<u64>,
|
||||||
host_functions: Vec<&'static dyn Function>,
|
host_functions: Vec<&'static dyn Function>,
|
||||||
max_runtime_instances: usize,
|
max_runtime_instances: usize,
|
||||||
|
cache_path: Option<PathBuf>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
WasmExecutor {
|
WasmExecutor {
|
||||||
method,
|
method,
|
||||||
default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES),
|
default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES),
|
||||||
host_functions: Arc::new(host_functions),
|
host_functions: Arc::new(host_functions),
|
||||||
cache: Arc::new(RuntimeCache::new(max_runtime_instances)),
|
cache: Arc::new(RuntimeCache::new(max_runtime_instances, cache_path.clone())),
|
||||||
max_runtime_instances,
|
max_runtime_instances,
|
||||||
|
cache_path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +225,7 @@ impl sp_core::traits::CallInWasm for WasmExecutor {
|
|||||||
&wasm_code,
|
&wasm_code,
|
||||||
self.host_functions.to_vec(),
|
self.host_functions.to_vec(),
|
||||||
allow_missing_host_functions,
|
allow_missing_host_functions,
|
||||||
|
self.cache_path.as_deref(),
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("Failed to create module: {:?}", e))?;
|
.map_err(|e| format!("Failed to create module: {:?}", e))?;
|
||||||
|
|
||||||
@@ -267,6 +283,7 @@ impl<D: NativeExecutionDispatch> NativeExecutor<D> {
|
|||||||
default_heap_pages,
|
default_heap_pages,
|
||||||
host_functions,
|
host_functions,
|
||||||
max_runtime_instances,
|
max_runtime_instances,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
NativeExecutor {
|
NativeExecutor {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ use codec::Decode;
|
|||||||
use sp_core::traits::{Externalities, RuntimeCode, FetchRuntimeCode};
|
use sp_core::traits::{Externalities, RuntimeCode, FetchRuntimeCode};
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
use std::panic::AssertUnwindSafe;
|
use std::panic::AssertUnwindSafe;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use sc_executor_common::wasm_runtime::{WasmModule, WasmInstance};
|
use sc_executor_common::wasm_runtime::{WasmModule, WasmInstance};
|
||||||
|
|
||||||
use sp_wasm_interface::Function;
|
use sp_wasm_interface::Function;
|
||||||
@@ -152,14 +153,22 @@ pub struct RuntimeCache {
|
|||||||
runtimes: Mutex<[Option<Arc<VersionedRuntime>>; MAX_RUNTIMES]>,
|
runtimes: Mutex<[Option<Arc<VersionedRuntime>>; MAX_RUNTIMES]>,
|
||||||
/// The size of the instances cache for each runtime.
|
/// The size of the instances cache for each runtime.
|
||||||
max_runtime_instances: usize,
|
max_runtime_instances: usize,
|
||||||
|
cache_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuntimeCache {
|
impl RuntimeCache {
|
||||||
/// Creates a new instance of a runtimes cache.
|
/// Creates a new instance of a runtimes cache.
|
||||||
pub fn new(max_runtime_instances: usize) -> RuntimeCache {
|
///
|
||||||
|
/// `max_runtime_instances` specifies the number of runtime instances preserved in an in-memory
|
||||||
|
/// cache.
|
||||||
|
///
|
||||||
|
/// `cache_path` allows to specify an optional directory where the executor can store files
|
||||||
|
/// for caching.
|
||||||
|
pub fn new(max_runtime_instances: usize, cache_path: Option<PathBuf>) -> RuntimeCache {
|
||||||
RuntimeCache {
|
RuntimeCache {
|
||||||
runtimes: Default::default(),
|
runtimes: Default::default(),
|
||||||
max_runtime_instances,
|
max_runtime_instances,
|
||||||
|
cache_path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +244,7 @@ impl RuntimeCache {
|
|||||||
host_functions.into(),
|
host_functions.into(),
|
||||||
allow_missing_func_imports,
|
allow_missing_func_imports,
|
||||||
self.max_runtime_instances,
|
self.max_runtime_instances,
|
||||||
|
self.cache_path.as_deref(),
|
||||||
);
|
);
|
||||||
if let Err(ref err) = result {
|
if let Err(ref err) = result {
|
||||||
log::warn!(target: "wasm-runtime", "Cannot create a runtime: {:?}", err);
|
log::warn!(target: "wasm-runtime", "Cannot create a runtime: {:?}", err);
|
||||||
@@ -271,22 +281,32 @@ pub fn create_wasm_runtime_with_code(
|
|||||||
code: &[u8],
|
code: &[u8],
|
||||||
host_functions: Vec<&'static dyn Function>,
|
host_functions: Vec<&'static dyn Function>,
|
||||||
allow_missing_func_imports: bool,
|
allow_missing_func_imports: bool,
|
||||||
|
cache_path: Option<&Path>,
|
||||||
) -> Result<Arc<dyn WasmModule>, WasmError> {
|
) -> Result<Arc<dyn WasmModule>, WasmError> {
|
||||||
match wasm_method {
|
match wasm_method {
|
||||||
WasmExecutionMethod::Interpreted =>
|
WasmExecutionMethod::Interpreted => {
|
||||||
|
// Wasmi doesn't have any need in a cache directory.
|
||||||
|
//
|
||||||
|
// We drop the cache_path here to silence warnings that cache_path is not used if compiling
|
||||||
|
// without the `wasmtime` flag.
|
||||||
|
drop(cache_path);
|
||||||
|
|
||||||
sc_executor_wasmi::create_runtime(
|
sc_executor_wasmi::create_runtime(
|
||||||
code,
|
code,
|
||||||
heap_pages,
|
heap_pages,
|
||||||
host_functions,
|
host_functions,
|
||||||
allow_missing_func_imports
|
allow_missing_func_imports,
|
||||||
).map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) }),
|
)
|
||||||
|
.map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) })
|
||||||
|
}
|
||||||
#[cfg(feature = "wasmtime")]
|
#[cfg(feature = "wasmtime")]
|
||||||
WasmExecutionMethod::Compiled =>
|
WasmExecutionMethod::Compiled =>
|
||||||
sc_executor_wasmtime::create_runtime(
|
sc_executor_wasmtime::create_runtime(
|
||||||
code,
|
code,
|
||||||
heap_pages,
|
heap_pages,
|
||||||
host_functions,
|
host_functions,
|
||||||
allow_missing_func_imports
|
allow_missing_func_imports,
|
||||||
|
cache_path,
|
||||||
).map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) }),
|
).map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,6 +339,7 @@ fn create_versioned_wasm_runtime(
|
|||||||
host_functions: Vec<&'static dyn Function>,
|
host_functions: Vec<&'static dyn Function>,
|
||||||
allow_missing_func_imports: bool,
|
allow_missing_func_imports: bool,
|
||||||
max_instances: usize,
|
max_instances: usize,
|
||||||
|
cache_path: Option<&Path>,
|
||||||
) -> Result<VersionedRuntime, WasmError> {
|
) -> Result<VersionedRuntime, WasmError> {
|
||||||
#[cfg(not(target_os = "unknown"))]
|
#[cfg(not(target_os = "unknown"))]
|
||||||
let time = std::time::Instant::now();
|
let time = std::time::Instant::now();
|
||||||
@@ -328,6 +349,7 @@ fn create_versioned_wasm_runtime(
|
|||||||
&code,
|
&code,
|
||||||
host_functions,
|
host_functions,
|
||||||
allow_missing_func_imports,
|
allow_missing_func_imports,
|
||||||
|
cache_path,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Call to determine runtime version.
|
// Call to determine runtime version.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use crate::state_holder;
|
|||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::path::Path;
|
||||||
use sc_executor_common::{
|
use sc_executor_common::{
|
||||||
error::{Result, WasmError},
|
error::{Result, WasmError},
|
||||||
wasm_runtime::{WasmModule, WasmInstance, InvokeMethod},
|
wasm_runtime::{WasmModule, WasmInstance, InvokeMethod},
|
||||||
@@ -119,20 +120,68 @@ impl WasmInstance for WasmtimeInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepare a directory structure and a config file to enable wasmtime caching.
|
||||||
|
///
|
||||||
|
/// In case of an error the caching will not be enabled.
|
||||||
|
fn setup_wasmtime_caching(
|
||||||
|
cache_path: &Path,
|
||||||
|
config: &mut Config,
|
||||||
|
) -> std::result::Result<(), String> {
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
let wasmtime_cache_root = cache_path.join("wasmtime");
|
||||||
|
fs::create_dir_all(&wasmtime_cache_root)
|
||||||
|
.map_err(|err| format!("cannot create the dirs to cache: {:?}", err))?;
|
||||||
|
|
||||||
|
// Canonicalize the path after creating the directories.
|
||||||
|
let wasmtime_cache_root = wasmtime_cache_root
|
||||||
|
.canonicalize()
|
||||||
|
.map_err(|err| format!("failed to canonicalize the path: {:?}", err))?;
|
||||||
|
|
||||||
|
// Write the cache config file
|
||||||
|
let cache_config_path = wasmtime_cache_root.join("cache-config.toml");
|
||||||
|
let config_content = format!(
|
||||||
|
"\
|
||||||
|
[cache]
|
||||||
|
enabled = true
|
||||||
|
directory = \"{cache_dir}\"
|
||||||
|
",
|
||||||
|
cache_dir = wasmtime_cache_root.display()
|
||||||
|
);
|
||||||
|
fs::write(&cache_config_path, config_content)
|
||||||
|
.map_err(|err| format!("cannot write the cache config: {:?}", err))?;
|
||||||
|
|
||||||
|
config
|
||||||
|
.cache_config_load(cache_config_path)
|
||||||
|
.map_err(|err| format!("failed to parse the config: {:?}", err))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to
|
/// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to
|
||||||
/// machine code, which can be computationally heavy.
|
/// machine code, which can be computationally heavy.
|
||||||
|
///
|
||||||
|
/// The `cache_path` designates where this executor implementation can put compiled artifacts.
|
||||||
pub fn create_runtime(
|
pub fn create_runtime(
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
heap_pages: u64,
|
heap_pages: u64,
|
||||||
host_functions: Vec<&'static dyn Function>,
|
host_functions: Vec<&'static dyn Function>,
|
||||||
allow_missing_func_imports: bool,
|
allow_missing_func_imports: bool,
|
||||||
|
cache_path: Option<&Path>,
|
||||||
) -> std::result::Result<WasmtimeRuntime, WasmError> {
|
) -> std::result::Result<WasmtimeRuntime, WasmError> {
|
||||||
// Create the engine, store and finally the module from the given code.
|
// Create the engine, store and finally the module from the given code.
|
||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize);
|
config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize);
|
||||||
|
if let Some(cache_path) = cache_path {
|
||||||
|
if let Err(reason) = setup_wasmtime_caching(cache_path, &mut config) {
|
||||||
|
log::warn!(
|
||||||
|
"failed to setup wasmtime cache. Performance may degrade significantly: {}.",
|
||||||
|
reason,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let engine = Engine::new(&config);
|
let engine = Engine::new(&config);
|
||||||
|
|
||||||
let module_wrapper = ModuleWrapper::new(&engine, code)
|
let module_wrapper = ModuleWrapper::new(&engine, code)
|
||||||
.map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?;
|
.map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?;
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ fn call_wasm_method_with_result<HF: HostFunctionsT>(
|
|||||||
Some(8),
|
Some(8),
|
||||||
host_functions,
|
host_functions,
|
||||||
8,
|
8,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
executor.call_in_wasm(
|
executor.call_in_wasm(
|
||||||
binary,
|
binary,
|
||||||
|
|||||||
Reference in New Issue
Block a user