mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 11:07:56 +00:00
Make wasm runtime cache size configurable (#10177)
* Make wasm runtime cache size configurable * apply review comments * remove VersionedRuntimeValue * fix compilation * VersionedRuntime: replace clone by Arc * fmt * fix warnings * fix tests compilation * fmt
This commit is contained in:
@@ -130,6 +130,7 @@ fn call_in_wasm<E: Externalities>(
|
||||
HostFunctions::host_functions(),
|
||||
8,
|
||||
None,
|
||||
2,
|
||||
);
|
||||
executor.uncached_call(
|
||||
RuntimeBlob::uncompress_if_needed(&wasm_binary_unwrap()[..]).unwrap(),
|
||||
@@ -480,6 +481,7 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) {
|
||||
HostFunctions::host_functions(),
|
||||
8,
|
||||
None,
|
||||
2,
|
||||
);
|
||||
|
||||
let err = executor
|
||||
@@ -593,6 +595,7 @@ fn parallel_execution(wasm_method: WasmExecutionMethod) {
|
||||
HostFunctions::host_functions(),
|
||||
8,
|
||||
None,
|
||||
2,
|
||||
));
|
||||
let threads: Vec<_> = (0..8)
|
||||
.map(|_| {
|
||||
|
||||
@@ -80,6 +80,7 @@ mod tests {
|
||||
sp_io::SubstrateHostFunctions::host_functions(),
|
||||
8,
|
||||
None,
|
||||
2,
|
||||
);
|
||||
let res = executor
|
||||
.uncached_call(
|
||||
|
||||
@@ -130,12 +130,17 @@ impl WasmExecutor {
|
||||
host_functions: Vec<&'static dyn Function>,
|
||||
max_runtime_instances: usize,
|
||||
cache_path: Option<PathBuf>,
|
||||
runtime_cache_size: u8,
|
||||
) -> Self {
|
||||
WasmExecutor {
|
||||
method,
|
||||
default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES),
|
||||
host_functions: Arc::new(host_functions),
|
||||
cache: Arc::new(RuntimeCache::new(max_runtime_instances, cache_path.clone())),
|
||||
cache: Arc::new(RuntimeCache::new(
|
||||
max_runtime_instances,
|
||||
cache_path.clone(),
|
||||
runtime_cache_size,
|
||||
)),
|
||||
cache_path,
|
||||
}
|
||||
}
|
||||
@@ -330,6 +335,7 @@ impl<D: NativeExecutionDispatch> NativeElseWasmExecutor<D> {
|
||||
fallback_method: WasmExecutionMethod,
|
||||
default_heap_pages: Option<u64>,
|
||||
max_runtime_instances: usize,
|
||||
runtime_cache_size: u8,
|
||||
) -> Self {
|
||||
let extended = D::ExtendHostFunctions::host_functions();
|
||||
let mut host_functions = sp_io::SubstrateHostFunctions::host_functions()
|
||||
@@ -351,6 +357,7 @@ impl<D: NativeExecutionDispatch> NativeElseWasmExecutor<D> {
|
||||
host_functions,
|
||||
max_runtime_instances,
|
||||
None,
|
||||
runtime_cache_size,
|
||||
);
|
||||
|
||||
NativeElseWasmExecutor {
|
||||
@@ -636,6 +643,7 @@ mod tests {
|
||||
WasmExecutionMethod::Interpreted,
|
||||
None,
|
||||
8,
|
||||
2,
|
||||
);
|
||||
my_interface::HostFunctions::host_functions().iter().for_each(|function| {
|
||||
assert_eq!(executor.wasm.host_functions.iter().filter(|f| f == &function).count(), 2);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
use crate::error::{Error, WasmError};
|
||||
use codec::Decode;
|
||||
use lru::LruCache;
|
||||
use parking_lot::Mutex;
|
||||
use sc_executor_common::{
|
||||
runtime_blob::RuntimeBlob,
|
||||
@@ -54,20 +55,24 @@ impl Default for WasmExecutionMethod {
|
||||
}
|
||||
}
|
||||
|
||||
/// A Wasm runtime object along with its cached runtime version.
|
||||
struct VersionedRuntime {
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
struct VersionedRuntimeId {
|
||||
/// Runtime code hash.
|
||||
code_hash: Vec<u8>,
|
||||
/// Wasm runtime type.
|
||||
wasm_method: WasmExecutionMethod,
|
||||
/// Shared runtime that can spawn instances.
|
||||
module: Arc<dyn WasmModule>,
|
||||
/// The number of WebAssembly heap pages this instance was created with.
|
||||
heap_pages: u64,
|
||||
}
|
||||
|
||||
/// A Wasm runtime object along with its cached runtime version.
|
||||
struct VersionedRuntime {
|
||||
/// Shared runtime that can spawn instances.
|
||||
module: Arc<dyn WasmModule>,
|
||||
/// Runtime version according to `Core_version` if any.
|
||||
version: Option<RuntimeVersion>,
|
||||
/// Cached instance pool.
|
||||
instances: Vec<Mutex<Option<Box<dyn WasmInstance>>>>,
|
||||
instances: Arc<Vec<Mutex<Option<Box<dyn WasmInstance>>>>>,
|
||||
}
|
||||
|
||||
impl VersionedRuntime {
|
||||
@@ -137,8 +142,6 @@ impl VersionedRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_RUNTIMES: usize = 2;
|
||||
|
||||
/// Cache for the runtimes.
|
||||
///
|
||||
/// When an instance is requested for the first time it is added to this cache. Metadata is kept
|
||||
@@ -149,12 +152,12 @@ const MAX_RUNTIMES: usize = 2;
|
||||
/// the memory reset to the initial memory. So, one runtime instance is reused for every fetch
|
||||
/// request.
|
||||
///
|
||||
/// The size of cache is equal to `MAX_RUNTIMES`.
|
||||
/// The size of cache is configurable via the cli option `--runtime-cache-size`.
|
||||
pub struct RuntimeCache {
|
||||
/// A cache of runtimes along with metadata.
|
||||
///
|
||||
/// Runtimes sorted by recent usage. The most recently used is at the front.
|
||||
runtimes: Mutex<[Option<Arc<VersionedRuntime>>; MAX_RUNTIMES]>,
|
||||
runtimes: Mutex<LruCache<VersionedRuntimeId, Arc<VersionedRuntime>>>,
|
||||
/// The size of the instances cache for each runtime.
|
||||
max_runtime_instances: usize,
|
||||
cache_path: Option<PathBuf>,
|
||||
@@ -163,13 +166,24 @@ pub struct RuntimeCache {
|
||||
impl RuntimeCache {
|
||||
/// Creates a new instance of a runtimes cache.
|
||||
///
|
||||
/// `max_runtime_instances` specifies the number of runtime instances preserved in an in-memory
|
||||
/// cache.
|
||||
/// `max_runtime_instances` specifies the number of instances per runtime 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 { runtimes: Default::default(), max_runtime_instances, cache_path }
|
||||
///
|
||||
/// `runtime_cache_size` specifies the number of different runtimes versions preserved in an
|
||||
/// in-memory cache.
|
||||
pub fn new(
|
||||
max_runtime_instances: usize,
|
||||
cache_path: Option<PathBuf>,
|
||||
runtime_cache_size: u8,
|
||||
) -> RuntimeCache {
|
||||
RuntimeCache {
|
||||
runtimes: Mutex::new(LruCache::new(runtime_cache_size.into())),
|
||||
max_runtime_instances,
|
||||
cache_path,
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepares a WASM module instance and executes given function for it.
|
||||
@@ -221,71 +235,55 @@ impl RuntimeCache {
|
||||
let code_hash = &runtime_code.hash;
|
||||
let heap_pages = runtime_code.heap_pages.unwrap_or(default_heap_pages);
|
||||
|
||||
let versioned_runtime_id =
|
||||
VersionedRuntimeId { code_hash: code_hash.clone(), heap_pages, wasm_method };
|
||||
|
||||
let mut runtimes = self.runtimes.lock(); // this must be released prior to calling f
|
||||
let pos = runtimes.iter().position(|r| {
|
||||
r.as_ref().map_or(false, |r| {
|
||||
r.wasm_method == wasm_method &&
|
||||
r.code_hash == *code_hash &&
|
||||
r.heap_pages == heap_pages
|
||||
})
|
||||
});
|
||||
let versioned_runtime = if let Some(versioned_runtime) = runtimes.get(&versioned_runtime_id)
|
||||
{
|
||||
versioned_runtime.clone()
|
||||
} else {
|
||||
let code = runtime_code.fetch_runtime_code().ok_or(WasmError::CodeNotFound)?;
|
||||
|
||||
let runtime = match pos {
|
||||
Some(n) => runtimes[n]
|
||||
.clone()
|
||||
.expect("`position` only returns `Some` for entries that are `Some`"),
|
||||
None => {
|
||||
let code = runtime_code.fetch_runtime_code().ok_or(WasmError::CodeNotFound)?;
|
||||
let time = std::time::Instant::now();
|
||||
|
||||
let time = std::time::Instant::now();
|
||||
let result = create_versioned_wasm_runtime(
|
||||
&code,
|
||||
ext,
|
||||
wasm_method,
|
||||
heap_pages,
|
||||
host_functions.into(),
|
||||
allow_missing_func_imports,
|
||||
self.max_runtime_instances,
|
||||
self.cache_path.as_deref(),
|
||||
);
|
||||
|
||||
let result = create_versioned_wasm_runtime(
|
||||
&code,
|
||||
code_hash.clone(),
|
||||
ext,
|
||||
wasm_method,
|
||||
heap_pages,
|
||||
host_functions.into(),
|
||||
allow_missing_func_imports,
|
||||
self.max_runtime_instances,
|
||||
self.cache_path.as_deref(),
|
||||
);
|
||||
match result {
|
||||
Ok(ref result) => {
|
||||
log::debug!(
|
||||
target: "wasm-runtime",
|
||||
"Prepared new runtime version {:?} in {} ms.",
|
||||
result.version,
|
||||
time.elapsed().as_millis(),
|
||||
);
|
||||
},
|
||||
Err(ref err) => {
|
||||
log::warn!(target: "wasm-runtime", "Cannot create a runtime: {:?}", err);
|
||||
},
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(ref result) => {
|
||||
log::debug!(
|
||||
target: "wasm-runtime",
|
||||
"Prepared new runtime version {:?} in {} ms.",
|
||||
result.version,
|
||||
time.elapsed().as_millis(),
|
||||
);
|
||||
},
|
||||
Err(ref err) => {
|
||||
log::warn!(target: "wasm-runtime", "Cannot create a runtime: {:?}", err);
|
||||
},
|
||||
}
|
||||
let versioned_runtime = Arc::new(result?);
|
||||
|
||||
Arc::new(result?)
|
||||
},
|
||||
// Save new versioned wasm runtime in cache
|
||||
runtimes.put(versioned_runtime_id, versioned_runtime.clone());
|
||||
|
||||
versioned_runtime
|
||||
};
|
||||
|
||||
// Rearrange runtimes by last recently used.
|
||||
match pos {
|
||||
Some(0) => {},
|
||||
Some(n) =>
|
||||
for i in (1..n + 1).rev() {
|
||||
runtimes.swap(i, i - 1);
|
||||
},
|
||||
None => {
|
||||
runtimes[MAX_RUNTIMES - 1] = Some(runtime.clone());
|
||||
for i in (1..MAX_RUNTIMES).rev() {
|
||||
runtimes.swap(i, i - 1);
|
||||
}
|
||||
},
|
||||
}
|
||||
// Lock must be released prior to calling f
|
||||
drop(runtimes);
|
||||
|
||||
Ok(runtime.with_instance(ext, f))
|
||||
Ok(versioned_runtime.with_instance(ext, f))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +394,6 @@ pub fn read_embedded_version(blob: &RuntimeBlob) -> Result<Option<RuntimeVersion
|
||||
|
||||
fn create_versioned_wasm_runtime(
|
||||
code: &[u8],
|
||||
code_hash: Vec<u8>,
|
||||
ext: &mut dyn Externalities,
|
||||
wasm_method: WasmExecutionMethod,
|
||||
heap_pages: u64,
|
||||
@@ -449,7 +446,7 @@ fn create_versioned_wasm_runtime(
|
||||
let mut instances = Vec::with_capacity(max_instances);
|
||||
instances.resize_with(max_instances, || Mutex::new(None));
|
||||
|
||||
Ok(VersionedRuntime { code_hash, module: runtime, version, heap_pages, wasm_method, instances })
|
||||
Ok(VersionedRuntime { module: runtime, version, instances: Arc::new(instances) })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user