Skip to content

perf(studio-server): preview steps skip the page parse when a tag scan answers - #7092

Merged
miguel-heygen merged 2 commits into
mainfrom
perf/preview-one-parse
Nov 3, 2026
Merged

miguel-heygen merged 2 commits into
mainfrom
perf/preview-one-parse

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

Three studio preview steps skip their whole-page parse when a cheap tag scan already gives the answer:

  • hasBaseElement asks the existing start-tag scanner (findStartTags) for a <base> instead of parsing the page.
  • lazyPreviewImages returns early when the scanner finds no <img>.
  • addScenePartsManifest returns early when the page never mentions data-hf-scene.

Why

The studio's preview build runs in the server process and parses the whole page in one step after another. On a large page each parse is a few hundred milliseconds, all of it blocking that process. These three steps parse only to look something up, and on most pages the lookup comes back empty.

Whole preview request for a stamped page with a 20,000-path inline SVG (about 1.4 MB), median of 5 runs on the same machine:

median
main before #7069 2259 ms
main with #7069 (stamped ensureHfIds skips its parse) 2019 ms
#7069 + this PR 903 ms

Related work

Follows #7069 (merged). Together they take a stamped page's preview build from 5 whole-page parses to 1. The one left is the read-tracking walk (recordPreviewReferences), which needs every element's decoded attributes. The bundler's own parses are separate work.

How

  • findStartTags already skips comments, raw text and <template> contents, as linkedom's querySelector does; hasBaseElement's existing tests (a <base> inside a script, a comment, a template, <basefont>, upper case) pass unchanged.
  • lazyPreviewImages already counted <img> start tags to line them up with the DOM's images; the count now runs first, and zero means nothing to mark.
  • addScenePartsManifest matched [data-hf-scene], which only finds the lowercase attribute, so a page whose text lacks that string has no parts and came back unchanged; it now comes back unchanged without the parse.

Test plan

Size

About 70 lines, most of it the test. The bundler's parses are a separate change in another package.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE · 0 blocker · 0 important · 0 nits · head 5a500783
All three early returns give the same result as the parse they skip. CI was still running when I reviewed, with nothing failing.

Scope
  • addScenePartsManifest: I confirmed with linkedom 0.18.15 that [data-hf-scene] does not match DATA-HF-SCENE or Data-Hf-Scene, because linkedom keeps attribute names as written. So includes(SCENE_PART_ATTR) skips only pages that would have had no parts anyway.
  • lazyPreviewImages: zero <img start tags already returned html through the tags.length !== images.length check, so returning early gives the same output.
  • hasBaseElement: I compared old (linkedom) against new (findStartTags) on 17 edge cases: <base> in title, textarea, noscript, iframe, svg, plaintext, an attribute value, a comment, style, script, nested <template>, upper case, and <base/>. They differ only on obsolete <xmp>, where the scanner reports a <base> and linkedom correctly does not. Not worth gating on.
  • Both callers checked: preview.ts:466 and subComposition.ts:341, which passes a head fragment.
  • This is a perf change, not a fix, so the fails without the fix: rule does not apply.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 119 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 4ceec01 Nov 3, 2026
94 of 95 checks passed
@miguel-heygen
miguel-heygen deleted the perf/preview-one-parse branch November 3, 2026 14:32
@WaterrrForever WaterrrForever mentioned this pull request Nov 3, 2026
1 task done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant