Units run in the wasm sandbox, so my_pid/parent_pid/context.server->request_count
read as sandbox stubs — the demo System Info counters were broken, and there was
no authoritative server-side request timing available to unit code (client-side
measurement cannot see queue/dispatch latency).
Add a request_perf() unit API backed by a new uce_host_request_perf hostcall.
The native worker answers it live, returning a DValue:
worker_pid, parent_pid, request_count,
accept_us = (time_start - time_init)*1e6 (entry -> dispatch wait),
running_us = (now - time_start)*1e6 (since dispatch, live),
total_us = (now - time_init)*1e6 (since the request entered UCE),
workspace_birth_us.
time_init is captured at request entry (handle_request, with a handle_complete
fallback); a RequestPerfSnapshot {pids, request_count, time_init, time_start} is
threaded from wasm_backend_serve through wasm_worker_serve onto the workspace,
and the hostcall computes the live deltas at call time. Wired like uce_host_units
(sized DValue hostcall): core_hostcalls.syms + sys.cpp/sys.h request_perf().
site/demo/index.uce System Info now uses request_perf() and shows the real worker
PID, an incrementing per-worker request count, and the timing counters.
Implemented via the pi agent (gpt-5.3-codex-spark); a review of the live numbers
caught accept_us mistakenly computed as (now - time_init) (== total_us), fixed to
the dispatch wait (time_start - time_init). Independently verified on the host:
System Info shows non-zero PIDs, incrementing count, accept_us ~50us << total_us
~2.4ms with accept+running==total; run_cli_tests --include-wasm-kill => 87 passed,
0 failed, 0 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to 560290c. Per-unit .wasm modules were still Cranelift-compiled per
worker on first use (~40-70ms each), so with 8-call worker recycling fresh
workers re-JIT every unit they touch.
Refactor the core cache logic into a shared WasmWorker helper:
- cached_wasm_path(p): maps <...>.wasm -> <...>.cwasm.
- load_or_compile_cached_module(engine, cached, wasm, bytes, err): deserialize_file
the .cwasm when it is newer than the .wasm; otherwise Module::compile + serialize,
written atomically (temp+rename); deserialize failure falls back to compile.
Both the core module load and unit_module() now go through this helper, so unit
artifacts get the same <unit>.uce.cwasm cache the core got.
Independently verified on the host: fresh-worker first-hit unit latency ~40-70ms
-> ~3-21ms; full suite wall-clock 20.5s -> 6.5s (and ~40s before any caching);
run_cli_tests --include-wasm-kill => 87 passed, 0 failed, 0 skipped.
Implemented via the pi agent (gpt-5.3-codex-spark sub-model).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>