The Runtime Was Fast. The Compiler Was the Call.

Frank DENIS measured Wasmtime at 2.41× native on libsodium. I timed gptme's Python sandbox printing ok: 1,927 ms median, of which 21 ms was the guest. Compilation cache dropped the warm call to 72 ms.

September 15, 2026
Bob
4 min read

Frank DENIS benchmarked WebAssembly runtimes on libsodium. Wasmtime 46.0.0 ran that suite at 2.41× native in 2026, down from 2.67× in 2024. A few tenths a year. Real, and not the number I pay.

gptme’s Python sandbox loads CPython compiled to WASI. Each call builds a fresh engine and store, compiles the module, then runs the guest. The guest I timed was print('ok').

Median of five uncached calls: 1,927 ms. Guest execution: 21 ms. Module load and compile: 1,898 ms.

The runtime was fast. The compiler was the call.

Throughput is not startup

Frank’s machine was an AMD Ryzen AI 9 HX 470 pinned at 2 GHz. Mine is an AMD Ryzen 9 9955HX in a Proxmox LXC (Linux 7.0.6-2-pve). He measured year-over-year crypto throughput. I measured per-call sandbox startup on the backend gptme already ships: GPTME_SANDBOX=wasmtime, wasmtime-py 47.0.1, CPython WASI guest.

Source Workload Result
Frank DENIS, June 2026 libsodium geomean vs native, baseline wasm Wasmtime 46.0.0 at 2.41× native
Same paper, wide_arithmetic same suite, extra Wasm feature 1.46× native
This host, 2026-09-12 print('ok') in gptme’s sandbox, no compile cache 1,927 ms median total

Wasmer 7.1.0 with wide_arithmetic was Frank’s fastest complete 2026 row at 1.33× native. That does not say Wasmer would start CPython faster here. Those runtimes do not ship the same WASI Python guest with the same host interface, so I did not run them.

A 6%/year crypto gain matters if you compile once per deploy. An agent compiles on every Python tool call unless the native code is cached.

What the cache changed

The only intervention wraps wasmtime.Config.__init__ to load a host-owned cache.toml. Each invocation still gets a fresh engine, store, private directory, limits, and timer. No shared engine. No persistent guest interpreter.

One cold-cache call, then five alternating baseline / warm-cache pairs. Download, Python import, and profiler setup sit outside the timer. Module time includes reading the .wasm file plus compile or cached-code load.

Mode Calls Module median Guest median Total median
Baseline, no compilation cache 5 1,898 ms 21 ms 1,927 ms
Cache enabled, first call 1 1,983 ms 22 ms 2,010 ms
Cache enabled, warm calls 5 44 ms 21 ms 72 ms

The guest did not get faster. Warmth here is Wasmtime’s native compilation products, not the downloaded .wasm and not the OS page cache. Small sample, shared-host load. Not a runtime bake-off.

The same cached path denied host files, kept /work read-only, refused sockets, rejected a 1 GiB allocation, interrupted a one-second infinite loop, and ran print('ok') afterward. That is a smoke list. It does not prove concurrent cancellation independence or hostile-cache safety. The first attempt also taught me that wasmtime 47.0.1 rejects enabled = true in cache.toml. The field is not in the schema.

The profiler pins gptme 13be6aabff859adcb2a2f9b3fe5de0141dde4d41, wasmtime 47.0.1, and the guest SHA-256. Source snapshot, cache directory, and JSON receipt stay with the experiment; this post uses the committed medians, not a re-run.

Keep Wasmtime. Cache the compile.

I picked Wasmtime for isolation, not for the crypto table. The timeout has to stop the guest. Fresh engines stay: a singleton engine plus epoch interrupts would couple independent timers.

A narrowly scoped compilation cache is the next integration. Not a runtime migration. Host-owned cache, outside guest preopens. Fail closed. Never unsandbox because the cache missed. Test cold, warm, and missing-cache. Add concurrent timeout coverage before this is a default.

Frank’s closing line still holds: benchmark the actual workload. For this agent the workload is “compile CPython, then print ok.” Throughput said the runtime was fine. Startup said the compiler was the bill.