Repository navigation
hub-client: Phase 0 render-path measurement harness (bd-k8ptcrd9) - #968
Merged
Merged
Conversation
…8ptcrd9)
Phase 0 of the 2026-10-24 main-thread render-path plan: instrumentation
and baselines only, no behavior changes.
Gauges (QUARTO_PERF_STATS-gated, perf.<name> lines): render.started /
completed / wasm-call on the five leaf render entries,
render.discarded-stale on ReactPreview's lastContentRef guard,
wasm-envelope.parse (segment + bytes) via a parseEnvelope helper
replacing every JSON.parse in wasmRenderer, vfs.write (segment + bytes),
intelligence.semantic-tokens(.tokenize), parse.qmd-to-ast, and
router.format-check(.parse/.classify).
perfCounters now exists as two copies (hub-client/src/utils +
ts-packages/preview-runtime) sharing one globalThis gauge sink: one
table per JS realm, and hub-client app code no longer imports the
package copy through the package boundary, so the repo's partial
vi.mock('@quarto/preview-runtime') mocks are unaffected. The copies are
drift-guarded by perfCounters.sync.test.ts.
Characterization tests (Phase 1-3 contracts):
- renderQueue.test.ts: serial order, every-queued-render-executes
(the no-coalescing pin Phase 1 will deliberately flip), rejection
reaches its caller and does not jam the queue.
- previewModeClassification.wasm.test.ts: three editor-edge pins with
discovered outcomes (unknown scalar format key => class 'neither';
unterminated front matter resolves successfully as default html;
broken body still classifies from the front matter).
- Q2SandboxedPreviewIframe.test.tsx: exact 13-key UPDATE_AST payload
key set (the contract Phase 3 changes).
Baseline (plan file has the full table; Node driver, release WASM, warm
SASS cache, 1x ~99KB / 8x ~800KB): per-keystroke wall 431ms / 3410ms =
render 302/2735ms + discarded format-check parse 79/708ms (finding 1,
18% at 1x) + tokens 44/386ms + envelope parse 14/132ms over 16.3/134.5MB
+ 2 full-text VFS writes/keystroke (the amplifier). A 10-keystroke burst
executes 10 renders (finding 2 confirmed: no coalescing).
Gates: hub-client unit 1743 green, preview-runtime 82 green, wasm leg
green except one PRE-EXISTING smokeAll version-shortcode fixture failure
verified identical on pristine main (filed bd-ucxaqjn2). npm run
build:all green. eslint errors on touched files identical to main (6,
pre-existing). Browser smoke of the gauges not done (driver + tests
only), as recorded in the plan.
- performance-profiling.md gains a "Driving the real render path" paragraph (resolve-hook maps, file: fetch shim for initWasm, fake-indexeddb before initWasm for the SASS cache bridge), pointing at the Phase 0 driver. - Fix a Q-2-10 strict-render error in the Phase 0 baseline text (apostrophe-before-space read as a closing quote); both files now render clean under q2 render --strict.
Contributor
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
vi.mock('wasm-quarto-hub-client') only intercepts when Vite can resolve
the aliased glue file; in the Workspace TS suites CI job (no WASM build)
the file is absent, so the import fell through to native ESM and failed
with ERR_MODULE_NOT_FOUND. Install the stub via a test-only setter
(_setWasmModuleForTest) instead — initWasm() early-returns and the
dynamic import never fires. Verified locally with the glue symlink
temporarily removed: full preview-runtime suite (82 tests) passes.
PR review follow-up: restructure every instrumented wrapper to an early-return fast path. When profiling is off, each site is one perfEnabled() property read and then the exact pre-instrumentation code shape — no try/finally, no gauge calls. The perf sink object is cached at module scope, so perfEnabled() itself is a single property read. Re-measured with the Phase 0 microbench: payload-scaled overhead is gone (16MB envelope delta was +9.4us, now lost in noise; 130KB +2.1us became +0.3us), leaving a flat ~5ns residual per instrumented call. Enabled/disabled behavior is pinned by the new perfGauges.test.ts (enabled records the gauges; disabled returns identical results and records nothing). Both perfCounters copies updated identically (the sync drift test passes). Suites: preview-runtime 84, hub-client 1743, both also green with the wasm glue absent (CI TS-suites conditions); hub-client production build green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 0 of the main-thread render-path plan: instrumentation and baselines only. No behavior changes.
What changed
QUARTO_PERF_STATS=1,localStorage.quartoPerfStats = '1', or?perf=1): renders started/completed/discarded-stale, WASM envelope parse time and bytes, VFS write count and bytes, semantic-token requests, format-check timing.perfCountersnow exists as two copies (hub-client and@quarto/preview-runtime) sharing oneglobalThisgauge table, with a drift-guard test. This makes the package's WASM-boundary code instrumentable without breaking the repo's partialvi.mock('@quarto/preview-runtime')mocks.JSON.parseon the WASM boundary inwasmRenderer.tsnow goes through oneparseEnvelopehelper.Baselines (per keystroke, ~99 KB document, release WASM)
Cost of the gauges when disabled
Effectively zero. Every instrumented wrapper has an early-return fast path: one property read, then the exact pre-instrumentation code shape (no try/finally, no gauge calls). Microbenchmark shows a flat ~5 ns residual per call with no payload scaling; an end-to-end A/B driver run against pre-change code is identical within noise. Both paths are pinned by
perfGauges.test.ts(enabled records; disabled returns identical results and records nothing).Verification
npm run build:allpasses.wasm-quarto-hub-clientimport).smokeAll.wasmfixture failure is pre-existing on main and unrelated (confirmed on a pristine checkout; tracked as bd-ucxaqjn2).