ReviewOS

zig-utils/zig-js

Runtime: coordinate synchronous compiler CPU

#985
Closed chris opened this 8 days ago ·
8 days ago

Parent: #502 Related: #481, #480 Depends on: #983, #984

Goal

Coordinate the CPU cost of the existing synchronous JIT and WebAssembly compilers across typed engine threads and embedder host threads without introducing an asynchronous artifact queue that has no measured justification.

Current boundary

Baseline and optimizer compilation claim, compile, adopt, and publish synchronously at VM chunk entry. Stale optimizer generations and owner invalidation already reject publication. Wasm decode, validation, and compilation are likewise synchronous. Compilation requested by a typed JavaScript Thread already runs inside its runnable slot; compilation requested by an untyped embedder thread is outside scheduler accounting.

#481 permits background compilation only if measurement justifies it and artifact claims, teardown, no-GIL readers, W^X, memory pressure, and deterministic tests stay exact. Moving today's compiler async before that evidence would add a queue and cancellation lifetime with no existing queued work to manage.

Scope

  • Add an owned compile-pressure benchmark with JIT-off execution controls and 1/2/4/host-width independent Context lanes. Measure cold/warm wall time, compile wall/CPU attribution, publications/rejections, configured stacks, scheduler wait, and peak RSS from a clean revision.
  • Add process-wide internal-work classes for native JIT compilation and Wasm compilation.
  • A typed runtime thread enters internal work without consuming a second CPU slot. An untyped host compiler registers against the scheduler's reserved host lane; concurrent host compilers beyond that reservation join the bounded background priority queue.
  • Extend coherent telemetry with active/peak internal work, reserved-host-lane use, queued work, waits, starts, completions, and cancellations/rejections. The total-capacity invariant must include typed runnable threads and registered host work.
  • Wrap the two production VM tier compilers and the production Wasm module compile boundary. Keep claim/compile/publication synchronous and preserve the exact owner/tier generation checks.
  • Teardown/invalidation continues waiting for active synchronous claims; there is no stale queued artifact and therefore no synthetic cancellation path.
  • Add deterministic normal/TSan tests for nested typed compilation, reserved host work, excess-host queueing, invalidation during compile, and teardown.
  • Publish dated raw/report evidence only from a clean tracked revision after the runner and schema land separately.

Non-goals

Asynchronous/background compilation, speculative compiler helpers, changing tier thresholds, OS priority/affinity, and compile-result caching. Any later async queue belongs to #481 and must use the evidence from this issue.

Acceptance

  • Concurrent host compilation cannot make total accounted CPU work exceed detected or explicit scheduler capacity.
  • Typed compilation never double-counts its requesting runnable thread.
  • No thread waits for capacity while holding an artifact-owner lock; stale claims still cannot publish.
  • JIT-off controls separate Context/execution scaling from compile cost.
  • Forced-off/forced-on tier differentials, artifact lifetime, focused normal/TSan, full units, threadfuzz, and docs/audits pass.
  • Dated evidence states whether asynchronous compilation is justified; no queue is added if the data does not support it.
8 days ago

Runner/schema foundation landed in 52b6bdee.

The new ReleaseFast runner uses a pinned 64-function numeric fixture and records cold/warm wall time, process CPU/RSS, baseline and optimizer timing, publications/failures, generated bytes, and deterministic checksums. Its jit_off control forces required bytecode.

Validation covered JIT-on and JIT-off at 1, 2, 4, and the 11-logical-CPU host width (16 phase rows). JIT-on published exactly 64 baseline artifacts per lane; JIT-off published zero native artifacts. This is runner-only: no production scheduler or compiler behavior changed yet.

Next I am landing the raw/report collector and validator separately, then measuring the clean baseline before changing admission.

8 days ago

The clean pre-admission baseline is published in 76768e28:

  • 9 fresh-process samples after 1 warmup at 1/2/4/11 lanes, for JIT-on and forced-bytecode controls
  • 144 validated phase rows; exact checksums and 64 baseline publications per JIT lane; zero native control/warm publications
  • JIT-on cold medians: 18.97 / 20.24 / 22.84 / 54.98 ms
  • aggregate throughput scaling: 1.00x / 1.87x / 3.32x / 3.80x
  • host width: 482.89 ms process CPU, 460.16 ms summed compiler time, 22.17 MiB generated code, 70.58 MiB peak RSS
  • maximum cold wall RSD: 9.14%

Evidence:

  • docs/.data/compiler-pressure-2026-09-22.json
  • docs/.data/compiler-pressure-2026-09-22.md

The runner, collector, raw evidence, 51-page docs build, and manifest check all pass. I am now implementing synchronous internal-work admission without changing the existing claim/publication lifetime.

8 days ago

Pushed the synchronous compiler coordination implementation in 04e6477.

Native JIT and WebAssembly compilation now enter the process-wide runtime CPU coordinator without changing their synchronous claim/publication lifetimes. Typed engine threads reuse their runnable slot. Untyped host callers use one automatic reserved lane, then join the allocation-free background queue for general capacity. Schema-v7 telemetry records requests, starts, completions, active/peak work, admission source, wait counts/time, and reserved/general occupancy.

Validation:

  • full sharded unit suite: 2,506 passed, 1 skipped, 0 failed, 0 leaked across 2,507 linked tests
  • focused JIT: 440 passed, 1 skipped
  • focused VM and concurrency: 4/4 each
  • standalone runtime scheduler: 9/9 normal and 9/9 under TSan
  • runtime-thread audit: 6 production sites, 6 resource classes, 143 reviewed test-only direct spawns
  • compiler-pressure self-test: identical JIT-on/JIT-off checksum; 64 baseline and one optimizer publication in the cold JIT phase
  • documentation: 51 pages built and the owned manifest matched

The test262 corpus checkout is unavailable in this worktree, so no conformance flip count is claimed. Next I am adding the new admission telemetry to the benchmark output and publishing the post-change comparison.

8 days ago

Completed and pushed to main.

Implementation and evidence:

  • 04e6477 coordinates native and Wasm synchronous compilation through schema-v7 runtime admission.
  • be5bdd1 adds the schema-v2 pressure harness and mandatory 2x-host oversubscription rows.
  • a125bd7 records exact configured stacks and renders the async-compilation decision.
  • bcc32b7 publishes the final clean evidence in docs/.data/compiler-pressure-2026-09-23.{json,md}.

Results:

  • At the 11-lane host width, cold JIT wall is 54.02 ms versus 54.98 ms before admission (-1.7%). Peak compiler work is exactly 11: 10 general slots plus one reserved host lane.
  • At 22 lanes, all nine samples queue work. The median queues 1,372 of 1,496 attempts while peak active compilation remains 11.
  • Each lane records a 16 MiB configured stack reservation. The 22-lane row therefore makes the 352 MiB configured reservation explicit alongside peak RSS.
  • JIT-off/JIT-on checksums match at every width; cold JIT publishes exactly 64 baseline artifacts per lane; warm phases publish and admit zero compiler work.
  • The data does not justify an asynchronous artifact queue: synchronous admission saturates host capacity and applies bounded backpressure without material host-width wall regression.

Validation:

  • sharded full units: 2,506 passed, 1 skipped, 0 failed, 0 leaked
  • focused JIT: 440 passed, 1 skipped
  • focused VM and concurrency: 4/4 each
  • runtime coordinator: 9/9 normal and 9/9 TSan
  • threadfuzz default: 400 programs, seed 1, 0 failures
  • GC relocation inventory, runtime-thread audit, benchmark runner/harness, and 51-page documentation manifest passed
  • test262 flips were not measured because this worktree has no corpus checkout; the change does not alter JavaScript semantics

The integrated TSan-only failure for the pre-existing deep-source RangeError witness is byte-for-byte reproducible on the exact parent and reports no race. It is tracked separately as #986 rather than hidden or attributed to this change.

Closing #985 as complete.

Sign in to comment on this issue.