@@ -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,
6060input hashes, and host details to
6161` packages/jco/test/output/bench/componentize-backends/results.json ` .
6262Component 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 ` .
6365Use ` --samples 50 --warmup 5 ` to change the default sample counts, or
6466` --skip-build ` to reuse unchanged components without requiring the toolchain.
6567Cached builds from the old runtime recipe are rejected and must be rebuilt.
6668The 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.
108190All three use explicit asynchronous instantiation with the same host setup.
109191Each backend runs in a fresh process for every sample. The default is 20 measured
110192samples 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
115197node componentize-backends.mjs --skip-build --samples 50 --warmup 5
0 commit comments