-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
I'm running into some troubles with the Engine configuration settings around memories in wasmtime = "0.33". There may be a few different issues here, but because everything is entangled together I will post them all here.
I'm trying to spawn 100k wasm instances and am hitting a few problems that I assume are related around virtual memory exhaustion.
This is a minimal example that demonstrates the first issue:
use wasmtime::{Config, Engine, Memory, MemoryType, Store};
fn main() {
let mut config = Config::new();
config.static_memory_maximum_size(15 * 65536);
config.static_memory_guard_size(65536);
config.static_memory_forced(true);
let engine = Engine::new(&config).unwrap();
let mut hold = Vec::new();
for _ in 0..100_000 {
let mut store = Store::new(&engine, ());
Memory::new(&mut store, MemoryType::new(10, None)).unwrap();
hold.push(store);
}
}My reasoning here is:
config.static_memory_forced(true)will force the engine to always use static memory.config.static_memory_maximum_size(15 * 65536)will allocate up to 15 wasm pages of virtual memory.config.static_memory_guard_size(65536)will add one more wasm page of virtual memory.- There is also going to be one extra OS page (4kb?) of virtual memory as a guard before the memory.
This works fine on 64bit MacOs, but fails to finish on 64bit linux with an error:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value:
Insufficient resources: System call failed: Cannot allocate memory (os error 12)', src/main.rs:14:60
I have also unsuccessfully tried to use dynamic memories instead:
config.static_memory_maximum_size(0);
config.dynamic_memory_reserved_for_growth(15 * 65536);I assume this should force all memories to be dynamic (static_memory_maximum_size(0)), but only allocate up to 15 wasm pages of virtual memory initially. However, this fails again with an Insufficient resources error on Linux, but works on MacOs.
What would be a correct approach here that lets me specify a maximum virtual memory size, but also works on Linux so I can spawn more than 16k memories?
While experimenting with this, I also noticed another inconsistency. If we change the config.static_memory_maximum_size to a lower value than the wasm module is requesting:
use wasmtime::{Config, Engine, Memory, MemoryType, Store};
fn main() {
let mut config = Config::new();
// 5 Wasm pages
config.static_memory_maximum_size(5 * 65536);
config.static_memory_guard_size(4096);
config.static_memory_forced(true);
let engine = Engine::new(&config).unwrap();
let mut hold = Vec::new();
for _ in 0..100_000 {
let mut store = Store::new(&engine, ());
// 10 Wasm pages requested
Memory::new(&mut store, MemoryType::new(10, None)).unwrap();
hold.push(store);
}
}The Engine is going to use a dynamic memory, even it was configured with static_memory_forced(true). This previous example will be failing also on MacOs too with an Insufficient resources error. Only if we add a config.dynamic_memory_reserved_for_growth(65536); it will work.
Personal thoughts
There is a lot of documentation around these settings, but I have read multiple times through it and somehow still can't figure it out. The most annoying issue is that the documentation only lists defaults for 32 bit and 64 bit machines, but the behaviour also seems to differ depending on the OS you are running.
With memory64 and dynamic_memory_reserved_for_growth the lines are even more blured between static and dynamic memory. I'm wondering if maybe we could get away with just one memory model that makes it clearer what is happening in the background?
It would be a dynamic memory with the following settings:
memory_initial_virtual_size(u64)- The dynamic memory can grow until this point without copying.memory_virtual_resize(bool)- This would turn it into a static memory.memory_guard_size(u64)- The guard page.
The defaults (64bit systems) would be:
memory_initial_virtual_size(4G);
memory_virtual_resize(false);
memory_guard_size(2G);
Resulting in the same properties the current defaults have.
This model is less feature rich, but I feel like the current system is so complicated that it's almost impossible to reason about what is going to be happening when you mix multiple settings.