Make memfd image creation lazy (on first instantiation).

As a followup to the recent memfd allocator work, this PR makes the
memfd image creation occur on the first instantiation, rather than
immediately when the `Module` is loaded.

This shaves off a potentially surprising cost spike that would have
otherwise occurred: prior to the memfd work, no allocator eagerly read
the module's initial heap state into RAM. The behavior should now more
closely resemble what happened before (and the improvements in overall
instantiation time and performance, as compared to either eager init
with pure-mmap memory or user-mode pagefault handling with uffd,
remain).
This commit is contained in:
Chris Fallin
2022-02-02 15:55:36 -08:00
parent 605c79fd05
commit 2a24a0fbde
4 changed files with 25 additions and 14 deletions

View File

@@ -37,6 +37,7 @@ lazy_static = "1.4"
rayon = { version = "1.0", optional = true }
object = { version = "0.27", default-features = false, features = ['read_core', 'elf'] }
async-trait = { version = "0.1.51", optional = true }
once_cell = "1.9"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = "0.3.7"

View File

@@ -690,6 +690,9 @@ impl<'a> Instantiator<'a> {
// properly referenced while in use by the store.
store.modules_mut().register(&self.cur.module);
// Initialize any memfd images now.
let memfds = self.cur.module.memfds()?;
unsafe {
// The first thing we do is issue an instance allocation request
// to the instance allocator. This, on success, will give us an
@@ -708,7 +711,7 @@ impl<'a> Instantiator<'a> {
.allocate(InstanceAllocationRequest {
module: compiled_module.module(),
unique_id: Some(compiled_module.unique_id()),
memfds: self.cur.module.memfds(),
memfds,
image_base: compiled_module.code().as_ptr() as usize,
functions: compiled_module.functions(),
imports: self.cur.build(),

View File

@@ -4,6 +4,7 @@ use crate::{
};
use crate::{Engine, ModuleType};
use anyhow::{bail, Context, Result};
use once_cell::sync::OnceCell;
use std::fs;
use std::mem;
use std::path::Path;
@@ -108,8 +109,11 @@ struct ModuleInner {
types: Arc<TypeTables>,
/// Registered shared signature for the module.
signatures: Arc<SignatureCollection>,
/// a set of memfd images for memories, if any.
memfds: Option<Arc<ModuleMemFds>>,
/// A set of memfd images for memories, if any. Note that module
/// instantiation (hence the need for lazy init) may happen for the
/// same module concurrently in multiple Stores, so we use a
/// OnceCell.
memfds: OnceCell<Option<Arc<ModuleMemFds>>>,
}
impl Module {
@@ -531,8 +535,6 @@ impl Module {
})
.collect::<Result<Vec<_>>>()?;
let memfds = ModuleMemFds::new(module.module(), module.wasm_data())?;
return Ok(Self {
inner: Arc::new(ModuleInner {
engine: engine.clone(),
@@ -541,7 +543,7 @@ impl Module {
artifact_upvars: modules,
module_upvars,
signatures,
memfds,
memfds: OnceCell::new(),
}),
});
@@ -555,13 +557,12 @@ impl Module {
signatures: &Arc<SignatureCollection>,
) -> Result<Module> {
let module = artifacts[module_index].clone();
let memfds = ModuleMemFds::new(module.module(), module.wasm_data())?;
Ok(Module {
inner: Arc::new(ModuleInner {
engine: engine.clone(),
types: types.clone(),
module,
memfds,
memfds: OnceCell::new(),
artifact_upvars: artifact_upvars
.iter()
.map(|i| artifacts[*i].clone())
@@ -682,13 +683,12 @@ impl Module {
modules: &PrimaryMap<ModuleIndex, Module>,
) -> Result<Module> {
let module = self.inner.artifact_upvars[artifact_index].clone();
let memfds = ModuleMemFds::new(module.module(), module.wasm_data())?;
Ok(Module {
inner: Arc::new(ModuleInner {
types: self.inner.types.clone(),
engine: self.inner.engine.clone(),
module,
memfds,
memfds: OnceCell::new(),
artifact_upvars: artifact_upvars
.iter()
.map(|i| self.inner.artifact_upvars[*i].clone())
@@ -723,8 +723,14 @@ impl Module {
&self.inner.signatures
}
pub(crate) fn memfds(&self) -> Option<&Arc<ModuleMemFds>> {
self.inner.memfds.as_ref()
pub(crate) fn memfds(&self) -> Result<Option<&Arc<ModuleMemFds>>> {
Ok(self
.inner
.memfds
.get_or_try_init(|| {
ModuleMemFds::new(self.inner.module.module(), self.inner.module.wasm_data())
})?
.as_ref())
}
/// Looks up the module upvar value at the `index` specified.