perf: cache the compiled core module so fresh workers deserialize, not recompile
Workers recycle every 8 requests (calls_until_termination=8). Each fresh worker ran wasmtime::Module::compile() on the 6.8MB bin/wasm/core.wasm — a ~1.3s Cranelift JIT — on its first request, so every ~8th request spiked to ~1.3s and dominated suite wall-clock. Cache the compiled artifact: on worker core-module load, if bin/wasm/core.cwasm exists and is newer than core.wasm, load it via Module::deserialize_file() (mmap, ~ms); otherwise Module::compile() as before and atomically (temp+rename) write the serialized artifact for the next worker. Deserialize failure / stale cache falls back to a normal compile, so it is self-healing; rebuilding core.wasm (newer mtime) invalidates the cache. Engine config (epoch_interruption, signals_based_traps(false)) is unchanged, which the serialized format requires. Implemented via the pi agent (delegated to a gpt-5.3-codex-spark sub-model). Independently verified on the host: worker-startup request latency drops from ~1.3s to ~23ms (warm 12x /demo/hello.uce: all <=0.023s, no spikes); full suite wall-clock ~40s -> 20.5s; run_cli_tests --include-wasm-kill => 87 passed, 0 failed, 0 skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bfd6d33829
commit
560290ca1d
@ -32,6 +32,7 @@
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@ -247,10 +248,54 @@ public:
|
||||
String parse_error;
|
||||
if(!wasm_parse_sections(bytes, core_dylink, abi_ignored, parse_error))
|
||||
return("core module: " + parse_error);
|
||||
|
||||
String core_cached_path = cfg.core_wasm_path;
|
||||
if(core_cached_path.size() >= 5 && core_cached_path.rfind(".wasm", core_cached_path.size() - 5) == core_cached_path.size() - 5)
|
||||
core_cached_path = core_cached_path.substr(0, core_cached_path.size() - 5) + ".cwasm";
|
||||
else
|
||||
core_cached_path += ".cwasm";
|
||||
struct stat core_stat;
|
||||
struct stat cached_stat;
|
||||
if(stat(core_cached_path.c_str(), &cached_stat) == 0 && stat(cfg.core_wasm_path.c_str(), &core_stat) == 0)
|
||||
{
|
||||
if(cached_stat.st_mtime > core_stat.st_mtime)
|
||||
{
|
||||
auto deserialized = wasmtime::Module::deserialize_file(engine, core_cached_path);
|
||||
if(deserialized)
|
||||
{
|
||||
core_module.emplace(deserialized.ok());
|
||||
return("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto compiled = wasmtime::Module::compile(engine, bytes);
|
||||
if(!compiled)
|
||||
return("core module compile failed: " + String(compiled.err().message()));
|
||||
core_module.emplace(compiled.ok());
|
||||
|
||||
auto serialized = core_module->serialize();
|
||||
if(serialized)
|
||||
{
|
||||
String tmp = core_cached_path + "." + std::to_string((long long)getpid()) + ".tmp";
|
||||
auto data = serialized.ok();
|
||||
{
|
||||
std::ofstream out(tmp, std::ios::binary | std::ios::trunc);
|
||||
if(out)
|
||||
{
|
||||
out.write((const char*)data.data(), (std::streamsize)data.size());
|
||||
if(out)
|
||||
{
|
||||
out.flush();
|
||||
out.close();
|
||||
if(std::rename(tmp.c_str(), core_cached_path.c_str()) != 0)
|
||||
(void)std::remove(tmp.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
(void)std::remove(tmp.c_str());
|
||||
}
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user