Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wacker: set allocation_strategy to pooling for HttpEngine #95

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions wacker/src/runtime/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use async_trait::async_trait;
use std::fs::File;
use wasi_common::{tokio, I32Exit};
use wasmtime::component::{Component, ResourceTable};
use wasmtime::{Module, Store};
use wasmtime::{Config, Module, Store};
use wasmtime_wasi::{bindings::Command, WasiCtxBuilder};
use wasmtime_wasi_http::WasiHttpCtx;

Expand All @@ -23,8 +23,10 @@ enum RunTarget {
}

impl CliEngine {
pub fn new(engine: wasmtime::Engine) -> Self {
Self { engine }
pub fn new(config: &Config) -> Result<Self> {
Ok(Self {
engine: wasmtime::Engine::new(config)?,
})
}

async fn load_module_contents(&self, engine: &wasmtime::Engine, path: &str) -> Result<RunTarget> {
Expand Down
50 changes: 47 additions & 3 deletions wacker/src/runtime/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::sync::{
Arc,
};
use wasmtime::component::{Component, InstancePre, Linker, ResourceTable};
use wasmtime::Store;
use wasmtime::{Config, InstanceAllocationStrategy, Memory, MemoryType, PoolingAllocationConfig, Store};
use wasmtime_wasi::WasiCtxBuilder;
use wasmtime_wasi_http::{
bindings::http::types as http_types, body::HyperOutgoingBody, hyper_response_error, io::TokioIo, WasiHttpCtx,
Expand All @@ -26,8 +26,16 @@ pub struct HttpEngine {
}

impl HttpEngine {
pub fn new(engine: wasmtime::Engine) -> Self {
Self { engine }
pub fn new(config: &Config) -> Result<Self> {
let mut config = config.clone();
if use_pooling_allocator_by_default().unwrap_or(false) {
let pooling_config = PoolingAllocationConfig::default();
config.allocation_strategy(InstanceAllocationStrategy::Pooling(pooling_config));
}

Ok(Self {
engine: wasmtime::Engine::new(&config)?,
})
}

fn new_store(&self, req_id: u64, stdout: File) -> Result<Store<Host>> {
Expand Down Expand Up @@ -199,3 +207,39 @@ async fn handle_request(
}
}
}

// ref: https://github.com/bytecodealliance/wasmtime/blob/ee9e1ca54586516c14d0c4a8dae63691a1d4b50c/src/commands/serve.rs#L561-L597

/// The pooling allocator is tailor made for the `wasmtime serve` use case, so
/// try to use it when we can. The main cost of the pooling allocator, however,
/// is the virtual memory required to run it. Not all systems support the same
/// amount of virtual memory, for example some aarch64 and riscv64 configuration
/// only support 39 bits of virtual address space.
///
/// The pooling allocator, by default, will request 1000 linear memories each
/// sized at 6G per linear memory. This is 6T of virtual memory which ends up
/// being about 42 bits of the address space. This exceeds the 39 bit limit of
/// some systems, so there the pooling allocator will fail by default.
///
/// This function attempts to dynamically determine the hint for the pooling
/// allocator. This returns `Some(true)` if the pooling allocator should be used
/// by default, or `None` or an error otherwise.
///
/// The method for testing this is to allocate a 0-sized 64-bit linear memory
/// with a maximum size that's N bits large where we force all memories to be
/// static. This should attempt to acquire N bits of the virtual address space.
/// If successful that should mean that the pooling allocator is OK to use, but
/// if it fails then the pooling allocator is not used and the normal mmap-based
/// implementation is used instead.
fn use_pooling_allocator_by_default() -> Result<bool> {
const BITS_TO_TEST: u32 = 42;
let mut config = Config::new();
config.wasm_memory64(true);
config.static_memory_maximum_size(1 << BITS_TO_TEST);
let engine = wasmtime::Engine::new(&config)?;
let mut store = Store::new(&engine, ());
// NB: the maximum size is in wasm pages to take out the 16-bits of wasm
// page size here from the maximum size.
let ty = MemoryType::new64(0, Some(1 << (BITS_TO_TEST - 16)));
Ok(Memory::new(&mut store, ty).is_ok())
}
17 changes: 7 additions & 10 deletions wacker/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
use std::fs;
use std::fs::File;
use std::sync::Arc;
use wasmtime::Config;

#[derive(Clone, Default, Serialize, Deserialize)]
pub struct ProgramMeta {
Expand All @@ -26,30 +27,26 @@ pub trait Engine: Send + Sync + 'static {
}

pub fn new_engines() -> Result<AHashMap<u32, Arc<dyn Engine>>> {
let wasmtime_engine = new_wasmtime_engine()?;
let cli_engine: Arc<dyn Engine> = Arc::new(cli::CliEngine::new(wasmtime_engine.clone()));
let http_engine: Arc<dyn Engine> = Arc::new(http::HttpEngine::new(wasmtime_engine));
let config = default_wasmtime_config()?;
let cli_engine: Arc<dyn Engine> = Arc::new(cli::CliEngine::new(&config)?);
let http_engine: Arc<dyn Engine> = Arc::new(http::HttpEngine::new(&config)?);

Ok(AHashMap::from([
(PROGRAM_TYPE_CLI, cli_engine),
(PROGRAM_TYPE_HTTP, http_engine),
]))
}

fn new_wasmtime_engine() -> Result<wasmtime::Engine> {
let mut config = wasmtime::Config::new();
fn default_wasmtime_config() -> Result<Config> {
let mut config = Config::new();
// We need this engine's `Store`s to be async, and consume fuel, so
// that they can co-operatively yield during execution.
config.async_support(true);
config.consume_fuel(true);
config.cache_config_load_default()?;
config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize);
config.wasm_component_model(true);

// Initialize global per-process state. This state will be shared amongst all
// threads. Notably this includes the compiled module as well as a `Linker`,
// which contains all our host functions we want to define.
wasmtime::Engine::new(&config)
Ok(config)
}

async fn read(path: &str) -> Result<Vec<u8>> {
Expand Down
Loading