Refactor configuration for the pooling allocator (#5205)
This commit changes the APIs in the `wasmtime` crate for configuring the pooling allocator. I plan on adding a few more configuration options in the near future and the current structure was feeling unwieldy for adding these new abstractions. The previous `struct`-based API has been replaced with a builder-style API in a similar shape as to `Config`. This is done to help make it easier to add more configuration options in the future through adding more methods as opposed to adding more field which could break prior initializations.
This commit is contained in:
@@ -13,9 +13,9 @@ mod codegen_settings;
|
||||
pub mod component_types;
|
||||
mod config;
|
||||
mod instance_allocation_strategy;
|
||||
mod instance_limits;
|
||||
mod memory;
|
||||
mod module;
|
||||
mod pooling_config;
|
||||
mod single_inst_module;
|
||||
mod spec_test;
|
||||
mod stacks;
|
||||
@@ -25,9 +25,9 @@ mod value;
|
||||
pub use codegen_settings::CodegenSettings;
|
||||
pub use config::{Config, WasmtimeConfig};
|
||||
pub use instance_allocation_strategy::InstanceAllocationStrategy;
|
||||
pub use instance_limits::InstanceLimits;
|
||||
pub use memory::{MemoryConfig, NormalMemoryConfig, UnalignedMemory, UnalignedMemoryCreator};
|
||||
pub use module::ModuleConfig;
|
||||
pub use pooling_config::PoolingAllocationConfig;
|
||||
pub use single_inst_module::SingleInstModule;
|
||||
pub use spec_test::SpecTest;
|
||||
pub use stacks::Stacks;
|
||||
|
||||
@@ -72,23 +72,20 @@ impl Config {
|
||||
config.canonicalize_nans = true;
|
||||
|
||||
// If using the pooling allocator, update the instance limits too
|
||||
if let InstanceAllocationStrategy::Pooling {
|
||||
instance_limits: limits,
|
||||
..
|
||||
} = &mut self.wasmtime.strategy
|
||||
{
|
||||
if let InstanceAllocationStrategy::Pooling(pooling) = &mut self.wasmtime.strategy {
|
||||
// One single-page memory
|
||||
limits.memories = config.max_memories as u32;
|
||||
limits.memory_pages = 10;
|
||||
pooling.instance_memories = config.max_memories as u32;
|
||||
pooling.instance_memory_pages = 10;
|
||||
|
||||
limits.tables = config.max_tables as u32;
|
||||
limits.table_elements = 1_000;
|
||||
pooling.instance_tables = config.max_tables as u32;
|
||||
pooling.instance_table_elements = 1_000;
|
||||
|
||||
limits.size = 1_000_000;
|
||||
pooling.instance_size = 1_000_000;
|
||||
|
||||
match &mut self.wasmtime.memory_config {
|
||||
MemoryConfig::Normal(config) => {
|
||||
config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
|
||||
config.static_memory_maximum_size =
|
||||
Some(pooling.instance_memory_pages * 0x10000);
|
||||
}
|
||||
MemoryConfig::CustomUnaligned => unreachable!(), // Arbitrary impl for `Config` should have prevented this
|
||||
}
|
||||
@@ -122,25 +119,22 @@ impl Config {
|
||||
config.max_memories = 1;
|
||||
config.max_tables = 5;
|
||||
|
||||
if let InstanceAllocationStrategy::Pooling {
|
||||
instance_limits: limits,
|
||||
..
|
||||
} = &mut self.wasmtime.strategy
|
||||
{
|
||||
if let InstanceAllocationStrategy::Pooling(pooling) = &mut self.wasmtime.strategy {
|
||||
// Configure the lower bound of a number of limits to what's
|
||||
// required to actually run the spec tests. Fuzz-generated inputs
|
||||
// may have limits less than these thresholds which would cause the
|
||||
// spec tests to fail which isn't particularly interesting.
|
||||
limits.memories = 1;
|
||||
limits.tables = limits.memories.max(5);
|
||||
limits.table_elements = limits.memories.max(1_000);
|
||||
limits.memory_pages = limits.memory_pages.max(900);
|
||||
limits.count = limits.count.max(500);
|
||||
limits.size = limits.size.max(64 * 1024);
|
||||
pooling.instance_memories = 1;
|
||||
pooling.instance_tables = pooling.instance_tables.max(5);
|
||||
pooling.instance_table_elements = pooling.instance_table_elements.max(1_000);
|
||||
pooling.instance_memory_pages = pooling.instance_memory_pages.max(900);
|
||||
pooling.instance_count = pooling.instance_count.max(500);
|
||||
pooling.instance_size = pooling.instance_size.max(64 * 1024);
|
||||
|
||||
match &mut self.wasmtime.memory_config {
|
||||
MemoryConfig::Normal(config) => {
|
||||
config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
|
||||
config.static_memory_maximum_size =
|
||||
Some(pooling.instance_memory_pages * 0x10000);
|
||||
}
|
||||
MemoryConfig::CustomUnaligned => unreachable!(), // Arbitrary impl for `Config` should have prevented this
|
||||
}
|
||||
@@ -173,8 +167,7 @@ impl Config {
|
||||
self.wasmtime.memory_guaranteed_dense_image_size,
|
||||
))
|
||||
.allocation_strategy(self.wasmtime.strategy.to_wasmtime())
|
||||
.generate_address_map(self.wasmtime.generate_address_map)
|
||||
.async_stack_zeroing(self.wasmtime.async_stack_zeroing);
|
||||
.generate_address_map(self.wasmtime.generate_address_map);
|
||||
|
||||
self.wasmtime.codegen.configure(&mut cfg);
|
||||
|
||||
@@ -319,11 +312,7 @@ impl<'a> Arbitrary<'a> for Config {
|
||||
|
||||
// If using the pooling allocator, constrain the memory and module configurations
|
||||
// to the module limits.
|
||||
if let InstanceAllocationStrategy::Pooling {
|
||||
instance_limits: limits,
|
||||
..
|
||||
} = &mut config.wasmtime.strategy
|
||||
{
|
||||
if let InstanceAllocationStrategy::Pooling(pooling) = &mut config.wasmtime.strategy {
|
||||
let cfg = &mut config.module_config.config;
|
||||
// If the pooling allocator is used, do not allow shared memory to
|
||||
// be created. FIXME: see
|
||||
@@ -333,19 +322,21 @@ impl<'a> Arbitrary<'a> for Config {
|
||||
// Force the use of a normal memory config when using the pooling allocator and
|
||||
// limit the static memory maximum to be the same as the pooling allocator's memory
|
||||
// page limit.
|
||||
if cfg.max_memory_pages < limits.memory_pages {
|
||||
limits.memory_pages = cfg.max_memory_pages;
|
||||
if cfg.max_memory_pages < pooling.instance_memory_pages {
|
||||
pooling.instance_memory_pages = cfg.max_memory_pages;
|
||||
} else {
|
||||
cfg.max_memory_pages = limits.memory_pages;
|
||||
cfg.max_memory_pages = pooling.instance_memory_pages;
|
||||
}
|
||||
config.wasmtime.memory_config = match config.wasmtime.memory_config {
|
||||
MemoryConfig::Normal(mut config) => {
|
||||
config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
|
||||
config.static_memory_maximum_size =
|
||||
Some(pooling.instance_memory_pages * 0x10000);
|
||||
MemoryConfig::Normal(config)
|
||||
}
|
||||
MemoryConfig::CustomUnaligned => {
|
||||
let mut config: NormalMemoryConfig = u.arbitrary()?;
|
||||
config.static_memory_maximum_size = Some(limits.memory_pages * 0x10000);
|
||||
config.static_memory_maximum_size =
|
||||
Some(pooling.instance_memory_pages * 0x10000);
|
||||
MemoryConfig::Normal(config)
|
||||
}
|
||||
};
|
||||
@@ -357,8 +348,8 @@ impl<'a> Arbitrary<'a> for Config {
|
||||
|
||||
// Force this pooling allocator to always be able to accommodate the
|
||||
// module that may be generated.
|
||||
limits.memories = cfg.max_memories as u32;
|
||||
limits.tables = cfg.max_tables as u32;
|
||||
pooling.instance_memories = cfg.max_memories as u32;
|
||||
pooling.instance_tables = cfg.max_tables as u32;
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
@@ -387,7 +378,6 @@ pub struct WasmtimeConfig {
|
||||
padding_between_functions: Option<u16>,
|
||||
generate_address_map: bool,
|
||||
native_unwind_info: bool,
|
||||
async_stack_zeroing: bool,
|
||||
}
|
||||
|
||||
impl WasmtimeConfig {
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
use super::PoolingAllocationConfig;
|
||||
use arbitrary::Arbitrary;
|
||||
|
||||
use super::InstanceLimits;
|
||||
|
||||
/// Configuration for `wasmtime::InstanceAllocationStrategy`.
|
||||
#[derive(Arbitrary, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum InstanceAllocationStrategy {
|
||||
/// Use the on-demand instance allocation strategy.
|
||||
OnDemand,
|
||||
/// Use the pooling instance allocation strategy.
|
||||
Pooling {
|
||||
/// The pooling strategy to use.
|
||||
strategy: PoolingAllocationStrategy,
|
||||
/// The instance limits.
|
||||
instance_limits: InstanceLimits,
|
||||
},
|
||||
Pooling(PoolingAllocationConfig),
|
||||
}
|
||||
|
||||
impl InstanceAllocationStrategy {
|
||||
@@ -21,37 +15,8 @@ impl InstanceAllocationStrategy {
|
||||
pub fn to_wasmtime(&self) -> wasmtime::InstanceAllocationStrategy {
|
||||
match self {
|
||||
InstanceAllocationStrategy::OnDemand => wasmtime::InstanceAllocationStrategy::OnDemand,
|
||||
InstanceAllocationStrategy::Pooling {
|
||||
strategy,
|
||||
instance_limits,
|
||||
} => wasmtime::InstanceAllocationStrategy::Pooling {
|
||||
strategy: strategy.to_wasmtime(),
|
||||
instance_limits: instance_limits.to_wasmtime(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for `wasmtime::PoolingAllocationStrategy`.
|
||||
#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum PoolingAllocationStrategy {
|
||||
/// Use next available instance slot.
|
||||
NextAvailable,
|
||||
/// Use random instance slot.
|
||||
Random,
|
||||
/// Use an affinity-based strategy.
|
||||
ReuseAffinity,
|
||||
}
|
||||
|
||||
impl PoolingAllocationStrategy {
|
||||
fn to_wasmtime(&self) -> wasmtime::PoolingAllocationStrategy {
|
||||
match self {
|
||||
PoolingAllocationStrategy::NextAvailable => {
|
||||
wasmtime::PoolingAllocationStrategy::NextAvailable
|
||||
}
|
||||
PoolingAllocationStrategy::Random => wasmtime::PoolingAllocationStrategy::Random,
|
||||
PoolingAllocationStrategy::ReuseAffinity => {
|
||||
wasmtime::PoolingAllocationStrategy::ReuseAffinity
|
||||
InstanceAllocationStrategy::Pooling(pooling) => {
|
||||
wasmtime::InstanceAllocationStrategy::Pooling(pooling.to_wasmtime())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
//! Generate instance limits for the pooling allocation strategy.
|
||||
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
|
||||
/// Configuration for `wasmtime::PoolingAllocationStrategy`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct InstanceLimits {
|
||||
pub count: u32,
|
||||
pub memories: u32,
|
||||
pub tables: u32,
|
||||
pub memory_pages: u64,
|
||||
pub table_elements: u32,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl InstanceLimits {
|
||||
/// Convert the generated limits to Wasmtime limits.
|
||||
pub fn to_wasmtime(&self) -> wasmtime::InstanceLimits {
|
||||
wasmtime::InstanceLimits {
|
||||
count: self.count,
|
||||
memories: self.memories,
|
||||
tables: self.tables,
|
||||
memory_pages: self.memory_pages,
|
||||
table_elements: self.table_elements,
|
||||
size: self.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Arbitrary<'a> for InstanceLimits {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
const MAX_COUNT: u32 = 100;
|
||||
const MAX_TABLES: u32 = 10;
|
||||
const MAX_MEMORIES: u32 = 10;
|
||||
const MAX_ELEMENTS: u32 = 1000;
|
||||
const MAX_MEMORY_PAGES: u64 = 160; // 10 MiB
|
||||
const MAX_SIZE: usize = 1 << 20; // 1 MiB
|
||||
|
||||
Ok(Self {
|
||||
tables: u.int_in_range(0..=MAX_TABLES)?,
|
||||
memories: u.int_in_range(0..=MAX_MEMORIES)?,
|
||||
table_elements: u.int_in_range(0..=MAX_ELEMENTS)?,
|
||||
memory_pages: u.int_in_range(0..=MAX_MEMORY_PAGES)?,
|
||||
count: u.int_in_range(1..=MAX_COUNT)?,
|
||||
size: u.int_in_range(0..=MAX_SIZE)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
81
crates/fuzzing/src/generators/pooling_config.rs
Normal file
81
crates/fuzzing/src/generators/pooling_config.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
//! Generate instance limits for the pooling allocation strategy.
|
||||
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
|
||||
/// Configuration for `wasmtime::PoolingAllocationStrategy`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct PoolingAllocationConfig {
|
||||
pub strategy: PoolingAllocationStrategy,
|
||||
pub instance_count: u32,
|
||||
pub instance_memories: u32,
|
||||
pub instance_tables: u32,
|
||||
pub instance_memory_pages: u64,
|
||||
pub instance_table_elements: u32,
|
||||
pub instance_size: usize,
|
||||
pub async_stack_zeroing: bool,
|
||||
}
|
||||
|
||||
impl PoolingAllocationConfig {
|
||||
/// Convert the generated limits to Wasmtime limits.
|
||||
pub fn to_wasmtime(&self) -> wasmtime::PoolingAllocationConfig {
|
||||
let mut cfg = wasmtime::PoolingAllocationConfig::default();
|
||||
|
||||
cfg.strategy(self.strategy.to_wasmtime())
|
||||
.instance_count(self.instance_count)
|
||||
.instance_memories(self.instance_memories)
|
||||
.instance_tables(self.instance_tables)
|
||||
.instance_memory_pages(self.instance_memory_pages)
|
||||
.instance_table_elements(self.instance_table_elements)
|
||||
.instance_size(self.instance_size)
|
||||
.async_stack_zeroing(self.async_stack_zeroing);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Arbitrary<'a> for PoolingAllocationConfig {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
const MAX_COUNT: u32 = 100;
|
||||
const MAX_TABLES: u32 = 10;
|
||||
const MAX_MEMORIES: u32 = 10;
|
||||
const MAX_ELEMENTS: u32 = 1000;
|
||||
const MAX_MEMORY_PAGES: u64 = 160; // 10 MiB
|
||||
const MAX_SIZE: usize = 1 << 20; // 1 MiB
|
||||
|
||||
Ok(Self {
|
||||
strategy: u.arbitrary()?,
|
||||
instance_tables: u.int_in_range(0..=MAX_TABLES)?,
|
||||
instance_memories: u.int_in_range(0..=MAX_MEMORIES)?,
|
||||
instance_table_elements: u.int_in_range(0..=MAX_ELEMENTS)?,
|
||||
instance_memory_pages: u.int_in_range(0..=MAX_MEMORY_PAGES)?,
|
||||
instance_count: u.int_in_range(1..=MAX_COUNT)?,
|
||||
instance_size: u.int_in_range(0..=MAX_SIZE)?,
|
||||
async_stack_zeroing: u.arbitrary()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for `wasmtime::PoolingAllocationStrategy`.
|
||||
#[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum PoolingAllocationStrategy {
|
||||
/// Use next available instance slot.
|
||||
NextAvailable,
|
||||
/// Use random instance slot.
|
||||
Random,
|
||||
/// Use an affinity-based strategy.
|
||||
ReuseAffinity,
|
||||
}
|
||||
|
||||
impl PoolingAllocationStrategy {
|
||||
fn to_wasmtime(&self) -> wasmtime::PoolingAllocationStrategy {
|
||||
match self {
|
||||
PoolingAllocationStrategy::NextAvailable => {
|
||||
wasmtime::PoolingAllocationStrategy::NextAvailable
|
||||
}
|
||||
PoolingAllocationStrategy::Random => wasmtime::PoolingAllocationStrategy::Random,
|
||||
PoolingAllocationStrategy::ReuseAffinity => {
|
||||
wasmtime::PoolingAllocationStrategy::ReuseAffinity
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user