Skip to content

Commit 5237dcf

Browse files
feat(jco): benchmark complete-component compilation speed
1 parent 44a574b commit 5237dcf

2 files changed

Lines changed: 335 additions & 156 deletions

File tree

‎packages/jco/test/bench/README.md‎

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,99 @@ node componentize-backends.mjs \
5555
--perry-source /path/to/perry-source
5656
```
5757

58-
The script checks every sample's output, prints sizes in KiB/MiB and timings in
59-
milliseconds, and writes exact byte counts, raw timing samples, tool versions,
58+
The script checks every build and runtime sample's output, prints sizes in KiB/MiB
59+
and timings in milliseconds, and writes exact byte counts, raw timing samples, tool versions,
6060
input hashes, and host details to
6161
`packages/jco/test/output/bench/componentize-backends/results.json`.
6262
Component artifacts and build metadata are retained in the same ignored directory.
63+
Use `--output-dir /path/to/artifacts` to select another location; pass the same
64+
location when using `--skip-build`.
6365
Use `--samples 50 --warmup 5` to change the default sample counts, or
6466
`--skip-build` to reuse unchanged components without requiring the toolchain.
6567
Cached builds from the old runtime recipe are rejected and must be rebuilt.
6668
The optional `--wasm-opt-bin` setting is passed to Jco's optimizer launcher hook.
67-
Runtime compilation, componentization, optimization, and transpilation time are
68-
excluded from the measurements.
69+
Componentization and optimization are measured separately from runtime execution.
70+
Compiler/engine/runtime builds and transpilation are excluded from both timing groups.
71+
72+
### Compilation speed
73+
74+
A full run measures **TS/JS source to a complete, usable Wasm component**, including
75+
the JavaScript engine for StarlingMonkey and QuickJS. It does not measure a
76+
script-only payload that requires a separately deployed engine.
77+
78+
The compilation table reports three medians:
79+
80+
- **TS/JS → component:** wall time around a fresh `jco componentize` process,
81+
through successful exit after writing the component. This includes CLI startup,
82+
TypeScript lowering and bundling, backend compilation or preinitialization,
83+
linking/component encoding, and writing the output. Perry's CLI capability
84+
probe is included as part of the normal Jco workflow.
85+
- **Optimize:** wall time around a fresh `jco opt` process on that sample's
86+
component, using the common optimization flags.
87+
- **TS/JS → optimized component:** median of each sample's componentization plus
88+
optimization time. This need not equal the sum of the first two medians.
89+
90+
The default is **five measured builds per backend after one warmup round**.
91+
`--build-samples` and `--build-warmup` control these independently of the runtime
92+
`--samples` and `--warmup` settings. Builds run sequentially, rotating backend order
93+
between rounds. Every build starts with fresh output and WIT directories; Jco's
94+
injected WIT imports cannot accumulate between samples. Prebuilt engine binaries,
95+
Perry's prepared runtime archive, and OS/filesystem caches are reused. These are
96+
warm-cache application builds, not clean-machine toolchain bootstrap timings.
97+
98+
The benchmark prepares its command wrapper and WIT fixture outside the timer;
99+
the timed CLI still performs the actual TypeScript transformation and bundling.
100+
Perry compiler installation, its one-time runtime archive preparation, dependency
101+
installation, transpilation and validation are also outside the timer. Every
102+
as-built **and** optimized component is transpiled and executed after the timed
103+
commands; incorrect output fails the benchmark instead of becoming a timing
104+
sample. The final optimized artifacts are reused for runtime measurements.
105+
106+
```sh
107+
node componentize-backends.mjs \
108+
--perry-cli-path /path/to/perry \
109+
--perry-source /path/to/perry-source \
110+
--build-samples 5 --build-warmup 1 \
111+
--samples 20 --warmup 3
112+
```
113+
114+
`build.json` and `results.json` retain compilation medians, every raw sample's
115+
stage timings and output sizes, round/order, sampling settings, host details,
116+
input hash, and measurement timestamps under `build.compilation` in the results.
117+
`--skip-build` runs only runtime measurements and explicitly labels compilation
118+
results as reused with their original timestamp. Older build receipts without
119+
compilation measurements remain usable but show no compilation table.
120+
121+
For environments needing a componentize-js build-time shim workaround,
122+
`JCO_BENCH_BUILD_PRELOAD=/absolute/path/to/preload.mjs` adds Node's `--import` only
123+
to componentization processes. The path is recorded in build metadata and its
124+
execution cost is included in compilation time. It is not loaded during
125+
optimization, validation, or runtime samples. Leave it unset for normal builds.
126+
127+
### Compilation results (2026-10-23)
128+
129+
Five measured builds per backend after one warmup, on the Linux x86_64 Intel
130+
Xeon 2.60 GHz host with Node.js 24.21.0:
131+
132+
| Backend | TS/JS → component (median ms) | Optimize (median ms) | TS/JS → optimized component (median ms) |
133+
| -------------- | ----------------------------: | -------------------: | --------------------------------------: |
134+
| perry | 3328.06 | 10924.11 | 14098.32 |
135+
| starlingmonkey | 6839.72 | 23536.53 | 30376.25 |
136+
| quickjs | 2619.39 | 3500.37 | 6119.76 |
137+
138+
QuickJS was fastest for both endpoints on this fixture. These measurements use
139+
componentize-qjs 0.4.5, componentize-js 0.23.0 without Weval AOT, the pinned Perry
140+
0.5.1659 compiler and reduced runtime recipe described above, and native Binaryen
141+
132 through `--wasm-opt-bin`. They do not use the experimental local Perry
142+
specialization or QuickJS 0.5's size runtime. Jco was rebuilt from the comparison
143+
branch for this run; package versions alone do not identify local compiler changes.
144+
145+
Output artifacts were written to tmpfs; normal OS/file caches were retained.
146+
The componentize-js 0.23.0 WIT-splicer shim needed the build-only preopen workaround
147+
described above; its startup cost is included. All 18 builds (warmups included)
148+
passed execution checks for both their as-built and optimized components. Results
149+
on other source graphs, hosts, disks, or tool versions can differ; this is not a
150+
universal compiler-throughput ranking.
69151

70152
### Recent results
71153

@@ -99,7 +181,7 @@ it was disabled for timings.
99181

100182
- **Component size:** the uncompressed `.wasm` component before transpilation,
101183
reported both as built and after optimization. MiB means 1,048,576 bytes.
102-
All timings use the optimized components.
184+
Runtime timings use the optimized components; compilation measures both output stages.
103185
- **Run:** the first command call on a fresh instance, including file read, stat, and output.
104186
- **Load + run:** importing and instantiating the transpiled component, then running it.
105187
- **Node process:** elapsed time from launching a fresh Node process until it exits,
@@ -108,8 +190,8 @@ it was disabled for timings.
108190
All three use explicit asynchronous instantiation with the same host setup.
109191
Each backend runs in a fresh process for every sample. The default is 20 measured
110192
samples per backend after three warmup rounds, rotating backend order between
111-
rounds. File and OS caches are not cleared. Builds and transpilation are excluded
112-
from the timing. To repeat measurements without rebuilding unchanged components:
193+
rounds. File and OS caches are not cleared. The runtime timing group excludes
194+
builds and transpilation; compilation is reported separately. To repeat measurements without rebuilding unchanged components:
113195

114196
```sh
115197
node componentize-backends.mjs --skip-build --samples 50 --warmup 5

0 commit comments

Comments
 (0)