Skip to content
This repository was archived by the owner on Aug 12, 2026. It is now read-only.

Commit 66f19f8

Browse files
committed
0017 A: the per-instantiate linker stays, and 0014 C's instantiate figure was C's
0017 A argued stubs must be engine-scoped, on a 7 us upcall stub against a 20 us instantiate. Both numbers were on the wrong side of the boundary. Measured through cljwit.host itself: instantiate, no imports 56.9 us instantiate, one import 73.2 us So an import costs +16 us -- stub, import reflection and linker registration together -- on a 57 us baseline, not 7 us on 20. The prediction recorded before measuring was that reusing one linker across instantiates would save 2x or more. It works -- wasmtime does not refuse it -- and it saves 1.23-1.45x in C. Falsified. Against that, engine-scoped stubs need routing through the store data pointer to reach a per-instance Clojure function, which is exactly where 0018's review found destructor routing gets hard. So per-instantiate stays, and the falsifier is named: a component whose own imports number in the dozens. WASI is not one, because add_wasip2 supplies its interfaces without a stub each. Separately, 0014 C's cost table said instantiate is 0.02 ms. That is a C probe's number; this library pays 56.9 us for the same step, because instantiate makes many FFM calls and each pays the flat crossing 0013 priced. The ratio the decision rests on survives -- compilation is still 30-300x instantiation -- but the figure was not ours, which is the same family of mistake as 0011's whole cost accounting. That unblocks 0018, which was deferred on this question. Claude-Session: https://claude.ai/code/session_01XF5Hfq4Ca2N2XYEzWQQuHt
1 parent 15191d6 commit 66f19f8

2 files changed

Lines changed: 31 additions & 10 deletions

File tree

‎doc/design/0014-cljwit-host-api.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,13 @@ They differ by three orders of magnitude in cost and by everything in scope:
6262
|---|---|---:|
6363
| `engine` | process-wide, shared by everything | ~0 ms warm |
6464
| `compile` | Cranelift compilation of one component | **1.6 ms** (2.4 KB) … **19.2 ms** (103 KB) |
65-
| `instantiate` | a store plus an instance | 0.02 ms |
65+
| `instantiate` | a store plus an instance | 0.02 ms in C, **0.057 ms through `cljwit.host`** |
66+
67+
**Corrected 2026-07-30:** the 0.02 ms is a C probe's. `cljwit.host` measures
68+
**56.9 µs** for the same step, because `instantiate` makes many FFM calls and
69+
each pays the flat crossing `0013` priced. The ratio the decision rests on
70+
survives — compilation is still 30–300× instantiation — but the absolute figure
71+
was not this library's.
6672

6773
Compilation is 80–1000× instantiation and scales with module size. Fusing them
6874
would make `wasmtime_component_serialize`/`deserialize_file` unreachable, and

‎doc/design/0017-host-imports.md‎

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,30 @@ What is settled, measured 2026-07-30:
3030
The identity is the exact WIT name, `iface#func`, as `0014` B decided. One
3131
naming rule for both directions.
3232

33-
**The stubs should not be per-instance, even though the functions are.**
34-
`Linker.upcallStub` measures **~7 µs warm and 30–160 µs cold**, against a 20 µs
35-
instantiate; a fresh closure per request means a fresh stub and a fresh linker
36-
per request. The shape that avoids it — one stub per import *name*, cached on
37-
the artifact, dispatching through the store's data pointer
38-
(`wasmtime_store_new(engine, data, finalizer)` / `wasmtime_context_get_data`,
39-
both present in 47.0.1) to the per-instance Clojure function — keeps A's
40-
per-request state and makes stubs and linker engine-scoped. **Unimplemented and
41-
unverified**; recorded because the naive shape has a measured cost.
33+
**Amended 2026-07-30: the per-instantiate linker stays, measured.** The first
34+
version of this paragraph argued that stubs must be engine-scoped, on a 7 µs
35+
stub against a 20 µs instantiate. Both figures were the wrong side of the
36+
boundary. Measured through `cljwit.host` itself:
37+
38+
| | µs per instantiate |
39+
|---|---:|
40+
| no imports | **56.9** |
41+
| one import | **73.2** |
42+
43+
So an import costs **+16 µs on a 57 µs baseline** — stub, reflection and
44+
linker registration together — and the 20 µs came from a C probe, not from
45+
this library (see the correction in `0014` C).
46+
47+
Reusing one linker across instantiates *does* work — wasmtime does not refuse
48+
it — and measured in C it saves **1.23–1.45×**, not the multiple the argument
49+
assumed. Against that, engine-scoped stubs need routing through the store data
50+
pointer to reach a per-instance Clojure function, and `0018`'s review showed
51+
that is exactly where destructor routing becomes hard.
52+
53+
**So: per-instantiate, until a world with many *user* imports says otherwise.**
54+
WASI does not: `add_wasip2` supplies its interfaces without a stub each.
55+
Falsified by a component whose own imports number in the dozens, where 16 µs
56+
each would dominate.
4257

4358
### B. The host checks for *extra* imports, and lets wasmtime report missing ones
4459

0 commit comments

Comments
 (0)