Add paged_memory_initialization to Config.

This commit adds a `paged_memory_initialization` setting to `Config`.

The setting controls whether or not an attempt is made to organize data
segments into Wasm pages during compilation.

When used in conjunction with the `uffd` feature on Linux, Wasmtime can
completely skip initializing linear memories and instead initialize any pages
that are accessed for the first time during Wasm execution.
This commit is contained in:
Peter Huene
2021-08-25 20:05:48 -07:00
parent da5c82b786
commit e2b9b54301
3 changed files with 49 additions and 1 deletions

View File

@@ -352,6 +352,7 @@ pub struct Config {
pub(crate) async_support: bool, pub(crate) async_support: bool,
pub(crate) deserialize_check_wasmtime_version: bool, pub(crate) deserialize_check_wasmtime_version: bool,
pub(crate) parallel_compilation: bool, pub(crate) parallel_compilation: bool,
pub(crate) paged_memory_initialization: bool,
} }
impl Config { impl Config {
@@ -375,6 +376,8 @@ impl Config {
async_support: false, async_support: false,
deserialize_check_wasmtime_version: true, deserialize_check_wasmtime_version: true,
parallel_compilation: true, parallel_compilation: true,
// Default to paged memory initialization when using uffd on linux
paged_memory_initialization: cfg!(all(target_os = "linux", feature = "uffd")),
}; };
#[cfg(compiler)] #[cfg(compiler)]
{ {
@@ -984,6 +987,27 @@ impl Config {
self self
} }
/// Sets whether or not an attempt is made to initialize linear memories by page.
///
/// This setting is `false` by default and Wasmtime initializes linear memories
/// by copying individual data segments from the compiled module.
///
/// Setting this to `true` will cause compilation to attempt to organize the
/// data segments into WebAssembly pages and linear memories are initialized by
/// copying each page rather than individual data segments.
///
/// Modules that import a memory or have data segments that use a global base
/// will continue to be initialized by copying each data segment individually.
///
/// When combined with the `uffd` feature on Linux, this will allow Wasmtime
/// to delay initialization of a linear memory page until it is accessed
/// for the first time during WebAssembly execution; this may improve
/// instantiation performance as a result.
pub fn paged_memory_initialization(&mut self, value: bool) -> &mut Self {
self.paged_memory_initialization = value;
self
}
/// Configures the maximum size, in bytes, where a linear memory is /// Configures the maximum size, in bytes, where a linear memory is
/// considered static, above which it'll be considered dynamic. /// considered static, above which it'll be considered dynamic.
/// ///
@@ -1329,6 +1353,7 @@ impl Clone for Config {
async_stack_size: self.async_stack_size, async_stack_size: self.async_stack_size,
deserialize_check_wasmtime_version: self.deserialize_check_wasmtime_version, deserialize_check_wasmtime_version: self.deserialize_check_wasmtime_version,
parallel_compilation: self.parallel_compilation, parallel_compilation: self.parallel_compilation,
paged_memory_initialization: self.paged_memory_initialization,
} }
} }
} }

View File

@@ -380,7 +380,7 @@ impl Module {
// If configured, attempt to use paged memory initialization // If configured, attempt to use paged memory initialization
// instead of the default mode of memory initialization // instead of the default mode of memory initialization
if cfg!(all(feature = "uffd", target_os = "linux")) { if engine.config().paged_memory_initialization {
translation.try_paged_init(); translation.try_paged_init();
} }

View File

@@ -32,6 +32,29 @@ fn initializes_linear_memory() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn initializes_linear_memory_paged() -> Result<()> {
let wat = r#"
(module
(memory (export "memory") 2)
(data (i32.const 0) "Hello World!")
)"#;
let mut config = Config::new();
config.paged_memory_initialization(true);
let module = Module::new(&Engine::new(&config)?, wat)?;
let mut store = Store::new(module.engine(), ());
let instance = Instance::new(&mut store, &module, &[])?;
let memory = instance.get_memory(&mut store, "memory").unwrap();
let mut bytes = [0; 12];
memory.read(&store, 0, &mut bytes)?;
assert_eq!(bytes, "Hello World!".as_bytes());
Ok(())
}
#[test] #[test]
fn linear_memory_limits() -> Result<()> { fn linear_memory_limits() -> Result<()> {
// this test will allocate 4GB of virtual memory space, and may not work in // this test will allocate 4GB of virtual memory space, and may not work in