Add the pooling-allocator feature.
This commit adds the `pooling-allocator` feature to both the `wasmtime` and `wasmtime-runtime` crates. The feature controls whether or not the pooling allocator implementation is built into the runtime and exposed as a supported instance allocation strategy in the wasmtime API. The feature is on by default for the `wasmtime` crate. Closes #3513.
This commit is contained in:
1
.github/workflows/main.yml
vendored
1
.github/workflows/main.yml
vendored
@@ -136,6 +136,7 @@ jobs:
|
|||||||
- run: cargo check -p wasmtime --no-default-features --features cache
|
- run: cargo check -p wasmtime --no-default-features --features cache
|
||||||
- run: cargo check -p wasmtime --no-default-features --features async
|
- run: cargo check -p wasmtime --no-default-features --features async
|
||||||
- run: cargo check -p wasmtime --no-default-features --features uffd
|
- run: cargo check -p wasmtime --no-default-features --features uffd
|
||||||
|
- run: cargo check -p wasmtime --no-default-features --features pooling-allocator
|
||||||
- run: cargo check -p wasmtime --no-default-features --features cranelift
|
- run: cargo check -p wasmtime --no-default-features --features cranelift
|
||||||
- run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache
|
- run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,11 @@ default = []
|
|||||||
|
|
||||||
async = ["wasmtime-fiber"]
|
async = ["wasmtime-fiber"]
|
||||||
|
|
||||||
|
# Enables support for the pooling instance allocator
|
||||||
|
pooling-allocator = []
|
||||||
|
|
||||||
# Enables support for userfaultfd in the pooling allocator when building on Linux
|
# Enables support for userfaultfd in the pooling allocator when building on Linux
|
||||||
uffd = ["userfaultfd"]
|
uffd = ["userfaultfd", "pooling-allocator"]
|
||||||
|
|
||||||
# Enables trap handling using POSIX signals instead of Mach exceptions on MacOS.
|
# Enables trap handling using POSIX signals instead of Mach exceptions on MacOS.
|
||||||
# It is useful for applications that do not bind their own exception ports and
|
# It is useful for applications that do not bind their own exception ports and
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ use wasmtime_environ::{
|
|||||||
SignatureIndex, TableInitializer, TrapCode, VMOffsets, WasmType, WASM_PAGE_SIZE,
|
SignatureIndex, TableInitializer, TrapCode, VMOffsets, WasmType, WASM_PAGE_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "pooling-allocator")]
|
||||||
mod pooling;
|
mod pooling;
|
||||||
|
|
||||||
|
#[cfg(feature = "pooling-allocator")]
|
||||||
pub use self::pooling::{
|
pub use self::pooling::{
|
||||||
InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator,
|
InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,9 +40,12 @@ pub use crate::export::*;
|
|||||||
pub use crate::externref::*;
|
pub use crate::externref::*;
|
||||||
pub use crate::imports::Imports;
|
pub use crate::imports::Imports;
|
||||||
pub use crate::instance::{
|
pub use crate::instance::{
|
||||||
InstanceAllocationRequest, InstanceAllocator, InstanceHandle, InstanceLimits,
|
InstanceAllocationRequest, InstanceAllocator, InstanceHandle, InstantiationError, LinkError,
|
||||||
InstantiationError, LinkError, ModuleLimits, OnDemandInstanceAllocator,
|
OnDemandInstanceAllocator, StorePtr,
|
||||||
PoolingAllocationStrategy, PoolingInstanceAllocator, StorePtr,
|
};
|
||||||
|
#[cfg(feature = "pooling-allocator")]
|
||||||
|
pub use crate::instance::{
|
||||||
|
InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator,
|
||||||
};
|
};
|
||||||
pub use crate::jit_int::GdbJitImageRegistration;
|
pub use crate::jit_int::GdbJitImageRegistration;
|
||||||
pub use crate::memory::{Memory, RuntimeLinearMemory, RuntimeMemoryCreator};
|
pub use crate::memory::{Memory, RuntimeLinearMemory, RuntimeMemoryCreator};
|
||||||
|
|||||||
@@ -364,6 +364,7 @@ impl Memory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether or not the underlying storage of the memory is "static".
|
/// Returns whether or not the underlying storage of the memory is "static".
|
||||||
|
#[cfg(feature = "pooling-allocator")]
|
||||||
pub(crate) fn is_static(&self) -> bool {
|
pub(crate) fn is_static(&self) -> bool {
|
||||||
if let Memory::Static { .. } = self {
|
if let Memory::Static { .. } = self {
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ impl Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether or not the underlying storage of the table is "static".
|
/// Returns whether or not the underlying storage of the table is "static".
|
||||||
|
#[cfg(feature = "pooling-allocator")]
|
||||||
pub(crate) fn is_static(&self) -> bool {
|
pub(crate) fn is_static(&self) -> bool {
|
||||||
if let Table::Static { .. } = self {
|
if let Table::Static { .. } = self {
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync" }
|
|||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ['async', 'cache', 'wat', 'jitdump', 'parallel-compilation', 'cranelift']
|
default = ['async', 'cache', 'wat', 'jitdump', 'parallel-compilation', 'cranelift', 'pooling-allocator']
|
||||||
|
|
||||||
# An on-by-default feature enabling runtime compilation of WebAssembly modules
|
# An on-by-default feature enabling runtime compilation of WebAssembly modules
|
||||||
# with the Cranelift compiler. Cranelift is the default compilation backend of
|
# with the Cranelift compiler. Cranelift is the default compilation backend of
|
||||||
@@ -76,8 +76,11 @@ cache = ["wasmtime-cache"]
|
|||||||
# `async fn` and calling functions asynchronously.
|
# `async fn` and calling functions asynchronously.
|
||||||
async = ["wasmtime-fiber", "wasmtime-runtime/async", "async-trait"]
|
async = ["wasmtime-fiber", "wasmtime-runtime/async", "async-trait"]
|
||||||
|
|
||||||
|
# Enables support for the pooling instance allocation strategy
|
||||||
|
pooling-allocator = ["wasmtime-runtime/pooling-allocator"]
|
||||||
|
|
||||||
# Enables userfaultfd support in the runtime's pooling allocator when building on Linux
|
# Enables userfaultfd support in the runtime's pooling allocator when building on Linux
|
||||||
uffd = ["wasmtime-runtime/uffd"]
|
uffd = ["wasmtime-runtime/uffd", "pooling-allocator"]
|
||||||
|
|
||||||
# Enables support for all architectures in Cranelift, allowing
|
# Enables support for all architectures in Cranelift, allowing
|
||||||
# cross-compilation using the `wasmtime` crate's API, notably the
|
# cross-compilation using the `wasmtime` crate's API, notably the
|
||||||
|
|||||||
@@ -12,281 +12,13 @@ use wasmparser::WasmFeatures;
|
|||||||
use wasmtime_cache::CacheConfig;
|
use wasmtime_cache::CacheConfig;
|
||||||
use wasmtime_environ::{CompilerBuilder, Tunables};
|
use wasmtime_environ::{CompilerBuilder, Tunables};
|
||||||
use wasmtime_jit::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
|
use wasmtime_jit::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator};
|
||||||
InstanceAllocator, OnDemandInstanceAllocator, PoolingInstanceAllocator, RuntimeMemoryCreator,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Represents the limits placed on a module for compiling with the pooling instance allocation strategy.
|
#[cfg(feature = "pooling-allocator")]
|
||||||
#[derive(Debug, Copy, Clone)]
|
mod pooling;
|
||||||
pub struct ModuleLimits {
|
|
||||||
/// The maximum number of imported functions for a module (default is 1000).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMFunctionImport` table and the
|
|
||||||
/// `VMCallerCheckedAnyfunc` table in each instance's `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the `VMFunctionImport` table will be `imported_functions * sizeof(VMFunctionImport)`
|
|
||||||
/// for each instance regardless of how many functions an instance imports.
|
|
||||||
///
|
|
||||||
/// The allocated size of the `VMCallerCheckedAnyfunc` table will be
|
|
||||||
/// `imported_functions * functions * sizeof(VMCallerCheckedAnyfunc)` for each instance regardless of
|
|
||||||
/// how many functions are imported and defined by an instance.
|
|
||||||
pub imported_functions: u32,
|
|
||||||
|
|
||||||
/// The maximum number of imported tables for a module (default is 0).
|
#[cfg(feature = "pooling-allocator")]
|
||||||
///
|
pub use self::pooling::*;
|
||||||
/// This value controls the capacity of the `VMTableImport` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `imported_tables * sizeof(VMTableImport)` for each
|
|
||||||
/// instance regardless of how many tables an instance imports.
|
|
||||||
pub imported_tables: u32,
|
|
||||||
|
|
||||||
/// The maximum number of imported linear memories for a module (default is 0).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMMemoryImport` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `imported_memories * sizeof(VMMemoryImport)` for each
|
|
||||||
/// instance regardless of how many memories an instance imports.
|
|
||||||
pub imported_memories: u32,
|
|
||||||
|
|
||||||
/// The maximum number of imported globals for a module (default is 0).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMGlobalImport` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `imported_globals * sizeof(VMGlobalImport)` for each
|
|
||||||
/// instance regardless of how many globals an instance imports.
|
|
||||||
pub imported_globals: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined types for a module (default is 100).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMSharedSignatureIndex` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `types * sizeof(VMSharedSignatureIndex)` for each
|
|
||||||
/// instance regardless of how many types are defined by an instance's module.
|
|
||||||
pub types: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined functions for a module (default is 10000).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMCallerCheckedAnyfunc` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the `VMCallerCheckedAnyfunc` table will be
|
|
||||||
/// `imported_functions * functions * sizeof(VMCallerCheckedAnyfunc)` for each instance
|
|
||||||
/// regardless of how many functions are imported and defined by an instance.
|
|
||||||
pub functions: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined tables for a module (default is 1).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMTableDefinition` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `tables * sizeof(VMTableDefinition)` for each
|
|
||||||
/// instance regardless of how many tables are defined by an instance's module.
|
|
||||||
pub tables: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined linear memories for a module (default is 1).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMMemoryDefinition` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `memories * sizeof(VMMemoryDefinition)` for each
|
|
||||||
/// instance regardless of how many memories are defined by an instance's module.
|
|
||||||
pub memories: u32,
|
|
||||||
|
|
||||||
/// The maximum number of defined globals for a module (default is 10).
|
|
||||||
///
|
|
||||||
/// This value controls the capacity of the `VMGlobalDefinition` table in each instance's
|
|
||||||
/// `VMContext` structure.
|
|
||||||
///
|
|
||||||
/// The allocated size of the table will be `globals * sizeof(VMGlobalDefinition)` for each
|
|
||||||
/// instance regardless of how many globals are defined by an instance's module.
|
|
||||||
pub globals: u32,
|
|
||||||
|
|
||||||
/// The maximum table elements for any table defined in a module (default is 10000).
|
|
||||||
///
|
|
||||||
/// If a table's minimum element limit is greater than this value, the module will
|
|
||||||
/// fail to compile.
|
|
||||||
///
|
|
||||||
/// If a table's maximum element limit is unbounded or greater than this value,
|
|
||||||
/// the maximum will be `table_elements` for the purpose of any `table.grow` instruction.
|
|
||||||
///
|
|
||||||
/// This value is used to reserve the maximum space for each supported table; table elements
|
|
||||||
/// are pointer-sized in the Wasmtime runtime. Therefore, the space reserved for each instance
|
|
||||||
/// is `tables * table_elements * sizeof::<*const ()>`.
|
|
||||||
pub table_elements: u32,
|
|
||||||
|
|
||||||
/// The maximum number of pages for any linear memory defined in a module (default is 160).
|
|
||||||
///
|
|
||||||
/// The default of 160 means at most 10 MiB of host memory may be committed for each instance.
|
|
||||||
///
|
|
||||||
/// If a memory's minimum page limit is greater than this value, the module will
|
|
||||||
/// fail to compile.
|
|
||||||
///
|
|
||||||
/// If a memory's maximum page limit is unbounded or greater than this value,
|
|
||||||
/// the maximum will be `memory_pages` for the purpose of any `memory.grow` instruction.
|
|
||||||
///
|
|
||||||
/// This value is used to control the maximum accessible space for each linear memory of an instance.
|
|
||||||
///
|
|
||||||
/// The reservation size of each linear memory is controlled by the
|
|
||||||
/// [`static_memory_maximum_size`](Config::static_memory_maximum_size) setting and this value cannot
|
|
||||||
/// exceed the configured static memory maximum size.
|
|
||||||
pub memory_pages: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ModuleLimits {
|
|
||||||
fn default() -> Self {
|
|
||||||
// Use the defaults from the runtime
|
|
||||||
let wasmtime_runtime::ModuleLimits {
|
|
||||||
imported_functions,
|
|
||||||
imported_tables,
|
|
||||||
imported_memories,
|
|
||||||
imported_globals,
|
|
||||||
types,
|
|
||||||
functions,
|
|
||||||
tables,
|
|
||||||
memories,
|
|
||||||
globals,
|
|
||||||
table_elements,
|
|
||||||
memory_pages,
|
|
||||||
} = wasmtime_runtime::ModuleLimits::default();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
imported_functions,
|
|
||||||
imported_tables,
|
|
||||||
imported_memories,
|
|
||||||
imported_globals,
|
|
||||||
types,
|
|
||||||
functions,
|
|
||||||
tables,
|
|
||||||
memories,
|
|
||||||
globals,
|
|
||||||
table_elements,
|
|
||||||
memory_pages,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
|
||||||
// without having to export runtime types from the Wasmtime API.
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl Into<wasmtime_runtime::ModuleLimits> for ModuleLimits {
|
|
||||||
fn into(self) -> wasmtime_runtime::ModuleLimits {
|
|
||||||
let Self {
|
|
||||||
imported_functions,
|
|
||||||
imported_tables,
|
|
||||||
imported_memories,
|
|
||||||
imported_globals,
|
|
||||||
types,
|
|
||||||
functions,
|
|
||||||
tables,
|
|
||||||
memories,
|
|
||||||
globals,
|
|
||||||
table_elements,
|
|
||||||
memory_pages,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
wasmtime_runtime::ModuleLimits {
|
|
||||||
imported_functions,
|
|
||||||
imported_tables,
|
|
||||||
imported_memories,
|
|
||||||
imported_globals,
|
|
||||||
types,
|
|
||||||
functions,
|
|
||||||
tables,
|
|
||||||
memories,
|
|
||||||
globals,
|
|
||||||
table_elements,
|
|
||||||
memory_pages,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the limits placed on instances by the pooling instance allocation strategy.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct InstanceLimits {
|
|
||||||
/// The maximum number of concurrent instances supported (default is 1000).
|
|
||||||
///
|
|
||||||
/// This value has a direct impact on the amount of memory allocated by the pooling
|
|
||||||
/// instance allocator.
|
|
||||||
///
|
|
||||||
/// The pooling instance allocator allocates three memory pools with sizes depending on this value:
|
|
||||||
///
|
|
||||||
/// * An instance pool, where each entry in the pool can store the runtime representation
|
|
||||||
/// of an instance, including a maximal `VMContext` structure (see [`ModuleLimits`](ModuleLimits)
|
|
||||||
/// for the various settings that control the size of each instance's `VMContext` structure).
|
|
||||||
///
|
|
||||||
/// * A memory pool, where each entry in the pool contains the reserved address space for each
|
|
||||||
/// linear memory supported by an instance.
|
|
||||||
///
|
|
||||||
/// * A table pool, where each entry in the pool contains the space needed for each WebAssembly table
|
|
||||||
/// supported by an instance (see `[ModuleLimits::table_elements`] to control the size of each table).
|
|
||||||
///
|
|
||||||
/// Additionally, this value will also control the maximum number of execution stacks allowed for
|
|
||||||
/// asynchronous execution (one per instance), when enabled.
|
|
||||||
///
|
|
||||||
/// The memory pool will reserve a large quantity of host process address space to elide the bounds
|
|
||||||
/// checks required for correct WebAssembly memory semantics. Even for 64-bit address spaces, the
|
|
||||||
/// address space is limited when dealing with a large number of supported instances.
|
|
||||||
///
|
|
||||||
/// For example, on Linux x86_64, the userland address space limit is 128 TiB. That might seem like a lot,
|
|
||||||
/// but each linear memory will *reserve* 6 GiB of space by default. Multiply that by the number of linear
|
|
||||||
/// memories each instance supports and then by the number of supported instances and it becomes apparent
|
|
||||||
/// that address space can be exhausted depending on the number of supported instances.
|
|
||||||
pub count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for InstanceLimits {
|
|
||||||
fn default() -> Self {
|
|
||||||
let wasmtime_runtime::InstanceLimits { count } =
|
|
||||||
wasmtime_runtime::InstanceLimits::default();
|
|
||||||
|
|
||||||
Self { count }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
|
||||||
// without having to export runtime types from the Wasmtime API.
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl Into<wasmtime_runtime::InstanceLimits> for InstanceLimits {
|
|
||||||
fn into(self) -> wasmtime_runtime::InstanceLimits {
|
|
||||||
let Self { count } = self;
|
|
||||||
|
|
||||||
wasmtime_runtime::InstanceLimits { count }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The allocation strategy to use for the pooling instance allocation strategy.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum PoolingAllocationStrategy {
|
|
||||||
/// Allocate from the next available instance.
|
|
||||||
NextAvailable,
|
|
||||||
/// Allocate from a random available instance.
|
|
||||||
Random,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PoolingAllocationStrategy {
|
|
||||||
fn default() -> Self {
|
|
||||||
match wasmtime_runtime::PoolingAllocationStrategy::default() {
|
|
||||||
wasmtime_runtime::PoolingAllocationStrategy::NextAvailable => Self::NextAvailable,
|
|
||||||
wasmtime_runtime::PoolingAllocationStrategy::Random => Self::Random,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
|
||||||
// without having to export runtime types from the Wasmtime API.
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl Into<wasmtime_runtime::PoolingAllocationStrategy> for PoolingAllocationStrategy {
|
|
||||||
fn into(self) -> wasmtime_runtime::PoolingAllocationStrategy {
|
|
||||||
match self {
|
|
||||||
Self::NextAvailable => wasmtime_runtime::PoolingAllocationStrategy::NextAvailable,
|
|
||||||
Self::Random => wasmtime_runtime::PoolingAllocationStrategy::Random,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the module instance allocation strategy to use.
|
/// Represents the module instance allocation strategy to use.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -303,6 +35,7 @@ pub enum InstanceAllocationStrategy {
|
|||||||
/// A pool of resources is created in advance and module instantiation reuses resources
|
/// A pool of resources is created in advance and module instantiation reuses resources
|
||||||
/// from the pool. Resources are returned to the pool when the `Store` referencing the instance
|
/// from the pool. Resources are returned to the pool when the `Store` referencing the instance
|
||||||
/// is dropped.
|
/// is dropped.
|
||||||
|
#[cfg(feature = "pooling-allocator")]
|
||||||
Pooling {
|
Pooling {
|
||||||
/// The allocation strategy to use.
|
/// The allocation strategy to use.
|
||||||
strategy: PoolingAllocationStrategy,
|
strategy: PoolingAllocationStrategy,
|
||||||
@@ -315,6 +48,7 @@ pub enum InstanceAllocationStrategy {
|
|||||||
|
|
||||||
impl InstanceAllocationStrategy {
|
impl InstanceAllocationStrategy {
|
||||||
/// The default pooling instance allocation strategy.
|
/// The default pooling instance allocation strategy.
|
||||||
|
#[cfg(feature = "pooling-allocator")]
|
||||||
pub fn pooling() -> Self {
|
pub fn pooling() -> Self {
|
||||||
Self::Pooling {
|
Self::Pooling {
|
||||||
strategy: PoolingAllocationStrategy::default(),
|
strategy: PoolingAllocationStrategy::default(),
|
||||||
@@ -1330,11 +1064,12 @@ impl Config {
|
|||||||
self.mem_creator.clone(),
|
self.mem_creator.clone(),
|
||||||
stack_size,
|
stack_size,
|
||||||
))),
|
))),
|
||||||
|
#[cfg(feature = "pooling-allocator")]
|
||||||
InstanceAllocationStrategy::Pooling {
|
InstanceAllocationStrategy::Pooling {
|
||||||
strategy,
|
strategy,
|
||||||
module_limits,
|
module_limits,
|
||||||
instance_limits,
|
instance_limits,
|
||||||
} => Ok(Box::new(PoolingInstanceAllocator::new(
|
} => Ok(Box::new(wasmtime_runtime::PoolingInstanceAllocator::new(
|
||||||
strategy.into(),
|
strategy.into(),
|
||||||
module_limits.into(),
|
module_limits.into(),
|
||||||
instance_limits.into(),
|
instance_limits.into(),
|
||||||
|
|||||||
273
crates/wasmtime/src/config/pooling.rs
Normal file
273
crates/wasmtime/src/config/pooling.rs
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
//! This module contains types exposed via `Config` relating to the pooling allocator feature.
|
||||||
|
|
||||||
|
/// Represents the limits placed on a module for compiling with the pooling instance allocation strategy.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ModuleLimits {
|
||||||
|
/// The maximum number of imported functions for a module (default is 1000).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMFunctionImport` table and the
|
||||||
|
/// `VMCallerCheckedAnyfunc` table in each instance's `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the `VMFunctionImport` table will be `imported_functions * sizeof(VMFunctionImport)`
|
||||||
|
/// for each instance regardless of how many functions an instance imports.
|
||||||
|
///
|
||||||
|
/// The allocated size of the `VMCallerCheckedAnyfunc` table will be
|
||||||
|
/// `imported_functions * functions * sizeof(VMCallerCheckedAnyfunc)` for each instance regardless of
|
||||||
|
/// how many functions are imported and defined by an instance.
|
||||||
|
pub imported_functions: u32,
|
||||||
|
|
||||||
|
/// The maximum number of imported tables for a module (default is 0).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMTableImport` table in each instance's
|
||||||
|
/// `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the table will be `imported_tables * sizeof(VMTableImport)` for each
|
||||||
|
/// instance regardless of how many tables an instance imports.
|
||||||
|
pub imported_tables: u32,
|
||||||
|
|
||||||
|
/// The maximum number of imported linear memories for a module (default is 0).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMMemoryImport` table in each instance's
|
||||||
|
/// `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the table will be `imported_memories * sizeof(VMMemoryImport)` for each
|
||||||
|
/// instance regardless of how many memories an instance imports.
|
||||||
|
pub imported_memories: u32,
|
||||||
|
|
||||||
|
/// The maximum number of imported globals for a module (default is 0).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMGlobalImport` table in each instance's
|
||||||
|
/// `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the table will be `imported_globals * sizeof(VMGlobalImport)` for each
|
||||||
|
/// instance regardless of how many globals an instance imports.
|
||||||
|
pub imported_globals: u32,
|
||||||
|
|
||||||
|
/// The maximum number of defined types for a module (default is 100).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMSharedSignatureIndex` table in each instance's
|
||||||
|
/// `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the table will be `types * sizeof(VMSharedSignatureIndex)` for each
|
||||||
|
/// instance regardless of how many types are defined by an instance's module.
|
||||||
|
pub types: u32,
|
||||||
|
|
||||||
|
/// The maximum number of defined functions for a module (default is 10000).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMCallerCheckedAnyfunc` table in each instance's
|
||||||
|
/// `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the `VMCallerCheckedAnyfunc` table will be
|
||||||
|
/// `imported_functions * functions * sizeof(VMCallerCheckedAnyfunc)` for each instance
|
||||||
|
/// regardless of how many functions are imported and defined by an instance.
|
||||||
|
pub functions: u32,
|
||||||
|
|
||||||
|
/// The maximum number of defined tables for a module (default is 1).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMTableDefinition` table in each instance's
|
||||||
|
/// `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the table will be `tables * sizeof(VMTableDefinition)` for each
|
||||||
|
/// instance regardless of how many tables are defined by an instance's module.
|
||||||
|
pub tables: u32,
|
||||||
|
|
||||||
|
/// The maximum number of defined linear memories for a module (default is 1).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMMemoryDefinition` table in each instance's
|
||||||
|
/// `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the table will be `memories * sizeof(VMMemoryDefinition)` for each
|
||||||
|
/// instance regardless of how many memories are defined by an instance's module.
|
||||||
|
pub memories: u32,
|
||||||
|
|
||||||
|
/// The maximum number of defined globals for a module (default is 10).
|
||||||
|
///
|
||||||
|
/// This value controls the capacity of the `VMGlobalDefinition` table in each instance's
|
||||||
|
/// `VMContext` structure.
|
||||||
|
///
|
||||||
|
/// The allocated size of the table will be `globals * sizeof(VMGlobalDefinition)` for each
|
||||||
|
/// instance regardless of how many globals are defined by an instance's module.
|
||||||
|
pub globals: u32,
|
||||||
|
|
||||||
|
/// The maximum table elements for any table defined in a module (default is 10000).
|
||||||
|
///
|
||||||
|
/// If a table's minimum element limit is greater than this value, the module will
|
||||||
|
/// fail to compile.
|
||||||
|
///
|
||||||
|
/// If a table's maximum element limit is unbounded or greater than this value,
|
||||||
|
/// the maximum will be `table_elements` for the purpose of any `table.grow` instruction.
|
||||||
|
///
|
||||||
|
/// This value is used to reserve the maximum space for each supported table; table elements
|
||||||
|
/// are pointer-sized in the Wasmtime runtime. Therefore, the space reserved for each instance
|
||||||
|
/// is `tables * table_elements * sizeof::<*const ()>`.
|
||||||
|
pub table_elements: u32,
|
||||||
|
|
||||||
|
/// The maximum number of pages for any linear memory defined in a module (default is 160).
|
||||||
|
///
|
||||||
|
/// The default of 160 means at most 10 MiB of host memory may be committed for each instance.
|
||||||
|
///
|
||||||
|
/// If a memory's minimum page limit is greater than this value, the module will
|
||||||
|
/// fail to compile.
|
||||||
|
///
|
||||||
|
/// If a memory's maximum page limit is unbounded or greater than this value,
|
||||||
|
/// the maximum will be `memory_pages` for the purpose of any `memory.grow` instruction.
|
||||||
|
///
|
||||||
|
/// This value is used to control the maximum accessible space for each linear memory of an instance.
|
||||||
|
///
|
||||||
|
/// The reservation size of each linear memory is controlled by the
|
||||||
|
/// [`static_memory_maximum_size`](super::Config::static_memory_maximum_size) setting and this value cannot
|
||||||
|
/// exceed the configured static memory maximum size.
|
||||||
|
pub memory_pages: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ModuleLimits {
|
||||||
|
fn default() -> Self {
|
||||||
|
// Use the defaults from the runtime
|
||||||
|
let wasmtime_runtime::ModuleLimits {
|
||||||
|
imported_functions,
|
||||||
|
imported_tables,
|
||||||
|
imported_memories,
|
||||||
|
imported_globals,
|
||||||
|
types,
|
||||||
|
functions,
|
||||||
|
tables,
|
||||||
|
memories,
|
||||||
|
globals,
|
||||||
|
table_elements,
|
||||||
|
memory_pages,
|
||||||
|
} = wasmtime_runtime::ModuleLimits::default();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
imported_functions,
|
||||||
|
imported_tables,
|
||||||
|
imported_memories,
|
||||||
|
imported_globals,
|
||||||
|
types,
|
||||||
|
functions,
|
||||||
|
tables,
|
||||||
|
memories,
|
||||||
|
globals,
|
||||||
|
table_elements,
|
||||||
|
memory_pages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
||||||
|
// without having to export runtime types from the Wasmtime API.
|
||||||
|
#[doc(hidden)]
|
||||||
|
impl Into<wasmtime_runtime::ModuleLimits> for ModuleLimits {
|
||||||
|
fn into(self) -> wasmtime_runtime::ModuleLimits {
|
||||||
|
let Self {
|
||||||
|
imported_functions,
|
||||||
|
imported_tables,
|
||||||
|
imported_memories,
|
||||||
|
imported_globals,
|
||||||
|
types,
|
||||||
|
functions,
|
||||||
|
tables,
|
||||||
|
memories,
|
||||||
|
globals,
|
||||||
|
table_elements,
|
||||||
|
memory_pages,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
wasmtime_runtime::ModuleLimits {
|
||||||
|
imported_functions,
|
||||||
|
imported_tables,
|
||||||
|
imported_memories,
|
||||||
|
imported_globals,
|
||||||
|
types,
|
||||||
|
functions,
|
||||||
|
tables,
|
||||||
|
memories,
|
||||||
|
globals,
|
||||||
|
table_elements,
|
||||||
|
memory_pages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the limits placed on instances by the pooling instance allocation strategy.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct InstanceLimits {
|
||||||
|
/// The maximum number of concurrent instances supported (default is 1000).
|
||||||
|
///
|
||||||
|
/// This value has a direct impact on the amount of memory allocated by the pooling
|
||||||
|
/// instance allocator.
|
||||||
|
///
|
||||||
|
/// The pooling instance allocator allocates three memory pools with sizes depending on this value:
|
||||||
|
///
|
||||||
|
/// * An instance pool, where each entry in the pool can store the runtime representation
|
||||||
|
/// of an instance, including a maximal `VMContext` structure (see [`ModuleLimits`](ModuleLimits)
|
||||||
|
/// for the various settings that control the size of each instance's `VMContext` structure).
|
||||||
|
///
|
||||||
|
/// * A memory pool, where each entry in the pool contains the reserved address space for each
|
||||||
|
/// linear memory supported by an instance.
|
||||||
|
///
|
||||||
|
/// * A table pool, where each entry in the pool contains the space needed for each WebAssembly table
|
||||||
|
/// supported by an instance (see `[ModuleLimits::table_elements`] to control the size of each table).
|
||||||
|
///
|
||||||
|
/// Additionally, this value will also control the maximum number of execution stacks allowed for
|
||||||
|
/// asynchronous execution (one per instance), when enabled.
|
||||||
|
///
|
||||||
|
/// The memory pool will reserve a large quantity of host process address space to elide the bounds
|
||||||
|
/// checks required for correct WebAssembly memory semantics. Even for 64-bit address spaces, the
|
||||||
|
/// address space is limited when dealing with a large number of supported instances.
|
||||||
|
///
|
||||||
|
/// For example, on Linux x86_64, the userland address space limit is 128 TiB. That might seem like a lot,
|
||||||
|
/// but each linear memory will *reserve* 6 GiB of space by default. Multiply that by the number of linear
|
||||||
|
/// memories each instance supports and then by the number of supported instances and it becomes apparent
|
||||||
|
/// that address space can be exhausted depending on the number of supported instances.
|
||||||
|
pub count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InstanceLimits {
|
||||||
|
fn default() -> Self {
|
||||||
|
let wasmtime_runtime::InstanceLimits { count } =
|
||||||
|
wasmtime_runtime::InstanceLimits::default();
|
||||||
|
|
||||||
|
Self { count }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
||||||
|
// without having to export runtime types from the Wasmtime API.
|
||||||
|
#[doc(hidden)]
|
||||||
|
impl Into<wasmtime_runtime::InstanceLimits> for InstanceLimits {
|
||||||
|
fn into(self) -> wasmtime_runtime::InstanceLimits {
|
||||||
|
let Self { count } = self;
|
||||||
|
|
||||||
|
wasmtime_runtime::InstanceLimits { count }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The allocation strategy to use for the pooling instance allocation strategy.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum PoolingAllocationStrategy {
|
||||||
|
/// Allocate from the next available instance.
|
||||||
|
NextAvailable,
|
||||||
|
/// Allocate from a random available instance.
|
||||||
|
Random,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PoolingAllocationStrategy {
|
||||||
|
fn default() -> Self {
|
||||||
|
match wasmtime_runtime::PoolingAllocationStrategy::default() {
|
||||||
|
wasmtime_runtime::PoolingAllocationStrategy::NextAvailable => Self::NextAvailable,
|
||||||
|
wasmtime_runtime::PoolingAllocationStrategy::Random => Self::Random,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This exists so we can convert between the public Wasmtime API and the runtime representation
|
||||||
|
// without having to export runtime types from the Wasmtime API.
|
||||||
|
#[doc(hidden)]
|
||||||
|
impl Into<wasmtime_runtime::PoolingAllocationStrategy> for PoolingAllocationStrategy {
|
||||||
|
fn into(self) -> wasmtime_runtime::PoolingAllocationStrategy {
|
||||||
|
match self {
|
||||||
|
Self::NextAvailable => wasmtime_runtime::PoolingAllocationStrategy::NextAvailable,
|
||||||
|
Self::Random => wasmtime_runtime::PoolingAllocationStrategy::Random,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user