id: SMH-RESEARCH-TX8NKI5B
type: research
title: "Wasmtime Hosting, Compilation, and Resource Control"
Wasmtime Hosting, Compilation, and Resource Control
Scope and evidence
How can Smith use Wasmtime efficiently without weakening isolation, cancellation, or reproducibility?
Evidence combines current Smith source, installed Wasmtime 49.0.1 source, and official documentation.
Versioned API links govern version-sensitive claims; unversioned guides supply context only.
No workloads were benchmarked; recommendations below are candidates, not measured optima.
No linked filesystem/network/random interfaces; not ambient full WASI
Integration
Echo component lifecycle exercised
Provider plugins, generation replacement, and render-shaped workloads do not yet establish runtime performance
Compilation: timing versus execution
In Wasmtime 49, Component::new/from_file compiles synchronously before returning.
Execution subsequently uses compiled code; there is no automatic hot-method JIT tiering in this path.
AOT changes when and where compilation happens, not necessarily steady-state generated code quality.
Core Module APIs and Component APIs are distinct; Smith needs Component APIs.
Strategy
Distribution
First use
Cross-platform/trust cost
Current local compilation
Portable component bytes
Validate, compile, link, instantiate
Compiler present on each supported host
Retained compiled component
Same portable bytes
Compile once per artifact/engine, reuse in process
No native deserialization boundary; cache lifetime/eviction still required
Local persistent compiled cache
Portable bytes plus locally produced derivative
Compile on miss; load compatible cached artifact on hit
Cache integrity, engine compatibility, invalidation, disk bounds
Distributed native AOT
Trusted compiled artifact per target/configuration
Load, link, instantiate
CPU/OS/version/config matrix; provenance and immutable artifact trust
Candidate hybrid path separates portable distribution from machine-local acceleration.
flowchart TD
W["Portable component bytes"] --> H["Artifact identity"]
H --> C{"Trusted compatible local cache?"}
C -->|No| V["Validate and compile locally"]
C -->|Yes| L["Load trusted compiled artifact"]
V --> R["Retain compiled component"]
L --> R
R --> I["Link policy and bounded Store"]
I --> X["Execute generated code"]
For native compiled artifacts, compatibility includes Wasmtime version, target architecture/OS, CPU features, compiler settings, enabled Wasm features, and runtime tunables.
Default artifact version checks use the Wasmtime package version; equal version alone is insufficient.
Engine::precompile_compatibility_hash describes engine compatibility, not a promised portable on-disk hash format.
Smith-specific admission/grants/WIT validation remains necessary even when an engine artifact is compatible.
Compiled artifacts are executable native code, not untrusted Wasm.
Component::deserialize is unsafe and inherits Module::deserialize's trust requirements.
Compatibility checks do not authenticate bytes or establish sandbox safety for malicious native artifacts.
A content hash identifies bytes; it does not establish their provenance.
Smith-produced cache entries need protected ownership, immutable verified contents during loading, and safe invalidation.
Direct unsafe deserialization conflicts with Smith's current library unsafe boundary; persistent-cache design needs review rather than copying documentation examples.
Wasmtime's optional built-in cache is a separate integration candidate, not currently enabled.
Compiler-free AOT deployments reduce runtime compiler dependencies but require a trusted compilation pipeline and compatible artifacts for every deployment target.
Candidate ownership model separates compiled code from mutable guest execution.
flowchart TD
E["Shared Engine"] --> C["Compiled Component per artifact"]
C --> P["InstancePre per component and import policy"]
P --> A["Store and instance A"]
P --> B["Store and instance B"]
A --> SA["Private guest and host state"]
B --> SB["Private guest and host state"]
Engine is cheaply cloneable and shareable; Component clones share compiled code.
Linker::instantiate_pre resolves import lookup and type checking before allocating an instance.
Reuse requires unchanged engine, component, and applicable import policy; never reuse a broader capability configuration for a less-trusted plugin.
Each synchronous call exclusively borrows its Store; parallel calls need independently owned execution state.
Dropping a Store releases its associated runtime objects; repeatedly allocating instances/resources into one immortal Store is not equivalent to reclaiming them individually.
Candidate generation retirement stops new dispatch, drains pinned calls, then drops guest state and host resources.
A running Store cannot safely be freed by another thread as a cancellation shortcut.
Pooling allocator slots are not permission to reuse logical guest state across plugins.
Deterministic instruction count or interruption inside a blocked host call
ResourceLimiter
Guest memory/table growth and Store resource counts
All host allocations, aggregate plugin RSS, compiler memory
Host request bounds
Input/output sizes and host-effect work
Guest CPU interruption
Allocation instrumentation
Allocations routed through the instrumented allocator
Complete accounting of mapped guest/JIT memory or process RSS
Fuel starts at zero until assigned and remains Store-scoped.
Candidate call budgeting explicitly separates instantiation/declaration/invocation budgets and refills at intended call boundaries.
Fuel units are neither milliseconds nor a stable performance metric across compiler/runtime upgrades.
Epoch interruption requires engine configuration, Store deadlines, and an external epoch-advancement mechanism.
Timer-driven epochs are nondeterministic; record resulting outcomes rather than assuming replay interrupts at identical instructions.
Both fuel and epochs leave synchronous host-import blocking to Smith's own cancellation/deadline design.
A fixed epoch interval alone is not a hard end-to-end cancellation-latency guarantee.
Per-memory bounds need explicit counts and aggregate policy; multiple memories and tables can multiply allowed usage.
ResourceLimiter does not account for host strings, canonical-ABI conversion buffers, or all Store bookkeeping.
Wasmtime 49 does not invoke its memory-growth limiter for shared memories; rejecting unsupported guest proposals needs explicit admission/configuration checks.
Virtual memory reservation is not committed memory: a large address reservation cannot be equated with physical RSS.
Pooling trades reserved capacity for predictable reuse; its workload benefit and component limits need measurement before enabling it.
NaN canonicalization alone does not cover relaxed SIMD, ambient imports, resource-exhaustion outcomes, or external interruption timing.
Wasmtime 49 enables relaxed SIMD by default; deterministic lowering is separate and defaults off.
Candidates: disable relaxed SIMD, or enable deterministic lowering and test identical guest results across intended targets.
A runtime memory ceiling does not prove fixed-size declared memories or deterministic growth success.
Growth admission, allocation failure, and fixed-memory validation need an explicit policy compatible with real guest toolchains.
Typed component calls already exist in Smith.
Strings/lists cross the canonical ABI through conversion and copying as required; borrowed Rust inputs do not imply cross-boundary zero-copy.
In Wasmtime 49, TypedFunc::call automatically performs post-return when supplied; manual extra cleanup copied from older examples is incorrect.
Candidate boundaries remain coarse: tool invocation, provider chunk batch, or frame-layout batch, not calls per field/widget.
Bound inputs and outputs before materialization where possible; account separately for host buffers and guest linear memory.
Host-side compilation, linking, and effects belong off the render thread.
Cranelift is Smith's enabled optimizing compiler.
Winch is a baseline native compiler, not an interpreter or automatic tier-up mode.
Wasmtime 49's Winch configuration rejects epoch interruption and several Wasm features; it is not an equivalent substitute for Smith's intended interruption model.
Pulley interprets Wasmtime bytecode and can support targets without a native Cranelift backend; it is distinct from a native AOT artifact.
Feature/backend availability does not establish compatibility with Smith's actual components.
Keep Cranelift-backed Wasmtime while real plugin consumers establish workload shape.
Retaining compiled Components and pre-resolved imports is a narrower first optimization candidate than distributing native AOT artifacts.
Portable components plus trusted local compilation/cache are the leading cross-platform hybrid candidate, not a measured optimum.
Correct budget semantics, aggregate limits, explicit arithmetic configuration, and interruptible host effects matter before compiler substitution.
Do not infer JIT necessity, AOT superiority, or interpreter inadequacy from generic benchmark ratios or dependency warnings.
Unresolved questions and evidence needed
Question
Evidence needed
Does compilation dominate admission?
Separate validation/compilation, linking, instantiation, declaration costs
Does compiled reuse matter?
Same guest and engine; cold admission versus retained Component/InstancePre
Are host crossings dominant?
Real tool/provider/layout payloads; instructions, allocation counts, bytes transferred
Can cancellation meet its budget?
Busy guest, bulk-memory work, slow host effect; actual request-to-stop latency and outcome recording
Which memory policy fits guests?
Rust and another guest toolchain; multi-memory/table limits, growth behavior, host allocation plus mapped-memory evidence
Cold/warm startup, invalidation, corrupted-cache rejection, ownership and TOCTOU review
Does native AOT justify a target matrix?
Compatible CPU/OS/config combinations, distribution size, signing/provenance, compiler-free deployment need
Does replay need guest re-execution?
Distinguish recorded-effect playback from deterministic kernel re-execution before defining compiled-artifact retention
Compare compiler modes using identical guests, inputs, engine settings, hardware, and compiler version.
Measure cold admission, warm instantiation, and steady execution independently; use instruction/allocation signals plus same-session wall-clock measurements.
Do not persist wall-clock results as budgets.
Native Windows/macOS measurements remain unavailable while CI only provides Linux containers; no cross-platform performance result is claimed.
---
id: SMH-RESEARCH-TX8NKI5B
type: research
title: "Wasmtime Hosting, Compilation, and Resource Control"
---
# Wasmtime Hosting, Compilation, and Resource Control
## Scope and evidence
How can Smith use Wasmtime efficiently without weakening isolation, cancellation, or reproducibility?
Evidence combines current Smith source, installed Wasmtime 49.0.1 source, and official documentation.
Versioned API links govern version-sensitive claims; unversioned guides supply context only.
No workloads were benchmarked; recommendations below are candidates, not measured optima.
## Current host
Source: [host](../../../smith-harness/src/wasm.rs), [sealed imports](../../../smith-harness/src/wasm/sealed_wasi.rs), [WIT](../../../wit/smith-plugin.wit), [manifest](../../../Cargo.toml), [host tests](../../../smith-harness/tests/wasm_host.rs).
| Concern | Observed implementation | Consequence |
| --- | --- | --- |
| Engine | Wasmtime 49.0.1; runtime, component-model, cranelift, demangle | Optimizing compiler available; pooling and persistent cache features not selected |
| Loading | Each load calls Component::from_file and constructs a Linker | Validation/compilation repeats; no retained compiled-component cache |
| Fuel | Enabled; 10 billion units assigned once at load | Declaration and invocations consume one balance despite the per-call comment |
| Memory | Limiter accepts each memory up to 64 MiB, each table up to 10,000 elements | Not an aggregate plugin-memory ceiling; resource counts retain engine defaults |
| Determinism | NaN canonicalization enabled; relaxed SIMD not explicitly restricted | Cross-platform arithmetic equivalence is not fully configured |
| Cancellation | No epoch setup or plugin-call cancellation path | Fuel exhaustion exists; bounded abort latency is unproven |
| Diagnostics | Backtrace limit is one frame | Comment saying backtraces are disabled is inaccurate |
| Imports | Empty environment, closed stdin, discarded output, injected clock, trapped exit | No linked filesystem/network/random interfaces; not ambient full WASI |
| Integration | Echo component lifecycle exercised | Provider plugins, generation replacement, and render-shaped workloads do not yet establish runtime performance |
## Compilation: timing versus execution
In Wasmtime 49, Component::new/from_file compiles synchronously before returning.
Execution subsequently uses compiled code; there is no automatic hot-method JIT tiering in this path.
AOT changes when and where compilation happens, not necessarily steady-state generated code quality.
Core Module APIs and Component APIs are distinct; Smith needs Component APIs.
| Strategy | Distribution | First use | Cross-platform/trust cost |
| --- | --- | --- | --- |
| Current local compilation | Portable component bytes | Validate, compile, link, instantiate | Compiler present on each supported host |
| Retained compiled component | Same portable bytes | Compile once per artifact/engine, reuse in process | No native deserialization boundary; cache lifetime/eviction still required |
| Local persistent compiled cache | Portable bytes plus locally produced derivative | Compile on miss; load compatible cached artifact on hit | Cache integrity, engine compatibility, invalidation, disk bounds |
| Distributed native AOT | Trusted compiled artifact per target/configuration | Load, link, instantiate | CPU/OS/version/config matrix; provenance and immutable artifact trust |
Candidate hybrid path separates portable distribution from machine-local acceleration.
```mermaid
flowchart TD
W["Portable component bytes"] --> H["Artifact identity"]
H --> C{"Trusted compatible local cache?"}
C -->|No| V["Validate and compile locally"]
C -->|Yes| L["Load trusted compiled artifact"]
V --> R["Retain compiled component"]
L --> R
R --> I["Link policy and bounded Store"]
I --> X["Execute generated code"]
```
For native compiled artifacts, compatibility includes Wasmtime version, target architecture/OS, CPU features, compiler settings, enabled Wasm features, and runtime tunables.
Default artifact version checks use the Wasmtime package version; equal version alone is insufficient.
Engine::precompile_compatibility_hash describes engine compatibility, not a promised portable on-disk hash format.
Smith-specific admission/grants/WIT validation remains necessary even when an engine artifact is compatible.
**Compiled artifacts are executable native code, not untrusted Wasm.**
Component::deserialize is unsafe and inherits Module::deserialize's trust requirements.
Compatibility checks do not authenticate bytes or establish sandbox safety for malicious native artifacts.
A content hash identifies bytes; it does not establish their provenance.
Smith-produced cache entries need protected ownership, immutable verified contents during loading, and safe invalidation.
Direct unsafe deserialization conflicts with Smith's current library unsafe boundary; persistent-cache design needs review rather than copying documentation examples.
Wasmtime's optional built-in cache is a separate integration candidate, not currently enabled.
Compiler-free AOT deployments reduce runtime compiler dependencies but require a trusted compilation pipeline and compatible artifacts for every deployment target.
Sources: [Component 49.0.1](https://docs.rs/wasmtime/49.0.1/wasmtime/component/struct.Component.html), [Engine 49.0.1](https://docs.rs/wasmtime/49.0.1/wasmtime/struct.Engine.html), [precompilation guide](https://docs.wasmtime.dev/examples-pre-compiling-wasm.html), [cache guide](https://docs.wasmtime.dev/cli-cache.html).
## Reuse without sharing mutable guest state
Candidate ownership model separates compiled code from mutable guest execution.
```mermaid
flowchart TD
E["Shared Engine"] --> C["Compiled Component per artifact"]
C --> P["InstancePre per component and import policy"]
P --> A["Store and instance A"]
P --> B["Store and instance B"]
A --> SA["Private guest and host state"]
B --> SB["Private guest and host state"]
```
Engine is cheaply cloneable and shareable; Component clones share compiled code.
Linker::instantiate_pre resolves import lookup and type checking before allocating an instance.
Reuse requires unchanged engine, component, and applicable import policy; never reuse a broader capability configuration for a less-trusted plugin.
Each synchronous call exclusively borrows its Store; parallel calls need independently owned execution state.
Dropping a Store releases its associated runtime objects; repeatedly allocating instances/resources into one immortal Store is not equivalent to reclaiming them individually.
Candidate generation retirement stops new dispatch, drains pinned calls, then drops guest state and host resources.
A running Store cannot safely be freed by another thread as a cancellation shortcut.
Pooling allocator slots are not permission to reuse logical guest state across plugins.
Sources: [Store](https://docs.rs/wasmtime/49.0.1/wasmtime/struct.Store.html), [InstancePre](https://docs.rs/wasmtime/49.0.1/wasmtime/component/struct.InstancePre.html), [Linker](https://docs.rs/wasmtime/49.0.1/wasmtime/component/struct.Linker.html), [instantiation guide](https://docs.wasmtime.dev/examples-fast-instantiation.html).
## Separate resource controls
| Mechanism | Controls | Does not establish |
| --- | --- | --- |
| Fuel | Instrumented guest execution budget | Wall-clock deadline or bounded host-import work |
| Epoch interruption | External interruption at guest checks | Deterministic instruction count or interruption inside a blocked host call |
| ResourceLimiter | Guest memory/table growth and Store resource counts | All host allocations, aggregate plugin RSS, compiler memory |
| Host request bounds | Input/output sizes and host-effect work | Guest CPU interruption |
| Allocation instrumentation | Allocations routed through the instrumented allocator | Complete accounting of mapped guest/JIT memory or process RSS |
Fuel starts at zero until assigned and remains Store-scoped.
Candidate call budgeting explicitly separates instantiation/declaration/invocation budgets and refills at intended call boundaries.
Fuel units are neither milliseconds nor a stable performance metric across compiler/runtime upgrades.
Epoch interruption requires engine configuration, Store deadlines, and an external epoch-advancement mechanism.
Timer-driven epochs are nondeterministic; record resulting outcomes rather than assuming replay interrupts at identical instructions.
Both fuel and epochs leave synchronous host-import blocking to Smith's own cancellation/deadline design.
A fixed epoch interval alone is not a hard end-to-end cancellation-latency guarantee.
Per-memory bounds need explicit counts and aggregate policy; multiple memories and tables can multiply allowed usage.
ResourceLimiter does not account for host strings, canonical-ABI conversion buffers, or all Store bookkeeping.
Wasmtime 49 does not invoke its memory-growth limiter for shared memories; rejecting unsupported guest proposals needs explicit admission/configuration checks.
Virtual memory reservation is not committed memory: a large address reservation cannot be equated with physical RSS.
Pooling trades reserved capacity for predictable reuse; its workload benefit and component limits need measurement before enabling it.
Sources: [ResourceLimiter](https://docs.rs/wasmtime/49.0.1/wasmtime/trait.ResourceLimiter.html), [Config fuel and epochs](https://docs.rs/wasmtime/49.0.1/wasmtime/struct.Config.html), [Store fuel/deadlines](https://docs.rs/wasmtime/49.0.1/wasmtime/struct.Store.html).
## Determinism and host boundaries
NaN canonicalization alone does not cover relaxed SIMD, ambient imports, resource-exhaustion outcomes, or external interruption timing.
Wasmtime 49 enables relaxed SIMD by default; deterministic lowering is separate and defaults off.
Candidates: disable relaxed SIMD, or enable deterministic lowering and test identical guest results across intended targets.
A runtime memory ceiling does not prove fixed-size declared memories or deterministic growth success.
Growth admission, allocation failure, and fixed-memory validation need an explicit policy compatible with real guest toolchains.
Typed component calls already exist in Smith.
Strings/lists cross the canonical ABI through conversion and copying as required; borrowed Rust inputs do not imply cross-boundary zero-copy.
In Wasmtime 49, TypedFunc::call automatically performs post-return when supplied; manual extra cleanup copied from older examples is incorrect.
Candidate boundaries remain coarse: tool invocation, provider chunk batch, or frame-layout batch, not calls per field/widget.
Bound inputs and outputs before materialization where possible; account separately for host buffers and guest linear memory.
Host-side compilation, linking, and effects belong off the render thread.
Sources: [TypedFunc 49.0.1](https://docs.rs/wasmtime/49.0.1/wasmtime/component/struct.TypedFunc.html), [deterministic execution](https://docs.wasmtime.dev/examples-deterministic-wasm-execution.html), [Config](https://docs.rs/wasmtime/49.0.1/wasmtime/struct.Config.html).
## Backend alternatives inside Wasmtime
Cranelift is Smith's enabled optimizing compiler.
Winch is a baseline native compiler, not an interpreter or automatic tier-up mode.
Wasmtime 49's Winch configuration rejects epoch interruption and several Wasm features; it is not an equivalent substitute for Smith's intended interruption model.
Pulley interprets Wasmtime bytecode and can support targets without a native Cranelift backend; it is distinct from a native AOT artifact.
Feature/backend availability does not establish compatibility with Smith's actual components.
Sources: [versioned configuration](https://docs.rs/wasmtime/49.0.1/wasmtime/struct.Config.html), [platform support](https://docs.wasmtime.dev/stability-platform-support.html).
## Candidate conclusions
Keep Cranelift-backed Wasmtime while real plugin consumers establish workload shape.
Retaining compiled Components and pre-resolved imports is a narrower first optimization candidate than distributing native AOT artifacts.
Portable components plus trusted local compilation/cache are the leading cross-platform hybrid candidate, not a measured optimum.
Correct budget semantics, aggregate limits, explicit arithmetic configuration, and interruptible host effects matter before compiler substitution.
Do not infer JIT necessity, AOT superiority, or interpreter inadequacy from generic benchmark ratios or dependency warnings.
## Unresolved questions and evidence needed
| Question | Evidence needed |
| --- | --- |
| Does compilation dominate admission? | Separate validation/compilation, linking, instantiation, declaration costs |
| Does compiled reuse matter? | Same guest and engine; cold admission versus retained Component/InstancePre |
| Are host crossings dominant? | Real tool/provider/layout payloads; instructions, allocation counts, bytes transferred |
| Can cancellation meet its budget? | Busy guest, bulk-memory work, slow host effect; actual request-to-stop latency and outcome recording |
| Which memory policy fits guests? | Rust and another guest toolchain; multi-memory/table limits, growth behavior, host allocation plus mapped-memory evidence |
| Is pooling worthwhile? | Simultaneous instance counts, churn, retained/peak memory, cleanup correctness |
| Is persistent cache worth its trust cost? | Cold/warm startup, invalidation, corrupted-cache rejection, ownership and TOCTOU review |
| Does native AOT justify a target matrix? | Compatible CPU/OS/config combinations, distribution size, signing/provenance, compiler-free deployment need |
| Does replay need guest re-execution? | Distinguish recorded-effect playback from deterministic kernel re-execution before defining compiled-artifact retention |
Compare compiler modes using identical guests, inputs, engine settings, hardware, and compiler version.
Measure cold admission, warm instantiation, and steady execution independently; use instruction/allocation signals plus same-session wall-clock measurements.
Do not persist wall-clock results as budgets.
Native Windows/macOS measurements remain unavailable while CI only provides Linux containers; no cross-platform performance result is claimed.