Repository navigation
Conversation
✅ Deploy Preview for webkit-speedometer-preview ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
Exercises chat-room switching in a two-pane chat UI, so timeline teardown/rebuild cost can be profiled. Original React + Vite code with deterministic, server-free fixtures and no third-party assets. Experimental, so it is off by default and does not affect the official score. SwitchRooms uses flushSync so each programmatic click commits one room switch.
flushSync stopped React 18 batching a burst of programmatic clicks into one deferred render, but it is not what a real app does and it bypasses the concurrent scheduler this workload exists to exercise. The switch is a plain state update now, with the step yielding a task between clicks, which makes the suite async. The yield is a MessageChannel round-trip: an async step is awaited inside the measured window, so setTimeout(0)'s clamp would be reported as workload time, and a microtask would keep all 30 switches in one task. Driving 30 switches: microtask 329ms, flushSync 348ms, MessageChannel 397ms, setTimeout(0) 427ms.
Chat clients open a room at its most recent message; the timeline mounted at offset 0. A layout effect now assigns scrollTop = scrollHeight after commit, so SwitchRooms measures mount plus the scroll to bottom. Not flex-direction: column-reverse, which inverts scrollTop semantics in ways browsers have disagreed on, and later steps drive scrollTop explicitly.
…ctive Bodies were a single plain sentence, so rows were near-uniform in height and a windowing timeline built on that would inherit an unrealistically easy height model. They are now structured blocks -- paragraphs, code, blockquotes, lists, with inline code, links and @mention/#room pills -- giving 48 distinct row heights over 150 rows. Generation moves to a Math.imul integer hash so fields derive independently from the seed. Reaction pills, room pills and reply quotes become real controls, with state in components so the fixtures stay immutable across iterations. The reply jump assigns scrollTop from measured rects rather than scrollIntoView, keeping smooth scrolling out of a timed step. Also fixes senders never repeating, so grouping never triggered, and room pills slugifying 20 names against 40 rooms. SwitchRooms grows from ~245ms to ~450ms, the cost of mounting 150 rich rows.
The app was a read-only viewer. There is now a text input and a Send button below the timeline; sending appends to the end and scrolls to bottom. Sent messages live in Timeline state rather than the fixtures, so the generated data stays immutable and rooms do not grow across iterations. Timestamps continue from the room's last message rather than reading the clock. Enter is an explicit onKeyDown, since a dispatched keydown triggers no default action. No new step: SwitchRooms is unchanged, so this is app surface only.
Message bodies gain an image attachment and an unfurl card block, covering about 11% of messages. Artwork is SVG data URIs built from a palette and shape variant by a new graphics module, so the workload still ships no image assets. The URIs are memoized on a reduced palette/variant key -- with the raw hashes every message got a distinct gradient id, so the browser decoded 652 images instead of 127. Images carry explicit width and height, or a row would resize once the image decoded and invalidate the height cache. Also fixes bullet lists repeating a line about 13% of the time. Row heights now span 24px to 278px with 58 distinct values, up from 229px and 48. SwitchRooms grows from ~367ms to ~442ms.
Speedometer has almost no scroll coverage: across the 32 default suites the only scrolling inside a timed step is a single scrollIntoView. A chat timeline is the natural vehicle for that, but only once it is windowed. Rooms grow from 150 to 1500 messages and the timeline mounts ~15 rows. The virtualizer is hand-rolled, since a generic one assumes a height it can know up front and a chat row's depends on how its text wraps. The new height model estimates, measures, caches and corrects, keeping prefix sums so an offset can be turned back into a row index without touching the DOM. Three constraints it has to respect -- the scroller's height lagging the model by a commit, the row estimate having to sit under the real mean, and overflow-anchor having to be off -- are documented at their sites. ScrollTimeline is the new step, driving explicit offsets back through history and then in long strides. SwitchRooms is unchanged but costs ~4.5x less. PageElement gains scrollTop/scrollHeight/clientHeight, since scrollIntoView cannot reach an unmounted row.
Preserving the scroll position while content is inserted above the viewport is the chat and feed pattern nothing else in Speedometer exercises, and the one real clients visibly get wrong. A room now opens on the newest 300 of its 1500 messages and fetches 120 more whenever a scroll lands within a viewport of the top. The height model grows at the front as well as the end: a row keeps its index when older history arrives, so measured heights stay valid and only the offsets shift, uniformly, which makes the correction exact. Anchored by hand rather than with the browser's scroll anchoring, which would apply a second correction for the same insertion. LoadOlderMessages is the new step; ScrollTimeline stops one stride short of the top so the two stay independent. Following a reply quote can now load history too.
A profile shows a ~14ms nursery collection landing inside a timed step in three of the thirty measured windows, and those three are the slowest iteration of their step. The fixture build is not itself measured, but it pushes ~117MB of permanently-live data through the nursery per iteration, and a minor GC costs what it has to promote. The fixture now shares what it can: bodies come from a pool of 2048 rather than one per message, reaction rows collapse to the nine distinct values they always had, and timestamps and initials stop being rebuilt 60,000 times. The pool is drawn from the same seed space, so the spread of row heights is unchanged. The per-message preview string is gone; nothing rendered it. Building the fixtures drops from 197ms to 31ms and retains 14MB instead of 117MB, taking the unmeasured prepare phase from 335ms to 48ms per iteration.
Turning a message id back into a row index is only needed when a reply quote is clicked, and neither a scroll nor a room switch clicks one. Building it during render rebuilt a map over the room's whole history on every switch, inside the timed step, where the profile puts it at 8.8% of SwitchRooms -- the largest single piece of the app's own JavaScript in the run. With the fixture sharing beneath it, this takes the content process from 1.04GB of net allocation to 459MB, minor collections from 77 to 16, and the ones inside a timed step from 14.1ms to 7.2ms on average. What is left is React's own reconciliation filling the nursery, which is the work the steps exist to measure.
Per-render chrome that chat clients carry and this workload had none of: a labelled rule where the conversation crosses a day, and a marker above the first message the reader has not seen. Neither is a row of its own -- a row here is one message, and the height cache, spacers and offset search are all indexed by message index -- so both are drawn inside the row they belong to, keyed off the message array rather than the mounted window. Day lengths now vary between 8 and 56 messages. At a fixed size the boundary always landed just above the newest page, so no room opened with a separator in view; twenty of the forty now do. Cost is noise: 100.65ms -> 105.00ms over ten Firefox iterations against a +/-20ms spread.
Output of `npm run build` in suites-experimental/chat-room. Build artifacts only, so it can be dropped and regenerated whenever the source commits below it change.
Moves ChatRoom-React's steps into a chatRoomTests array, so other ports of the workload can be measured on identical interactions.
A Svelte 5 port of ChatRoom-React that renders the same DOM from the same fixtures.
An Angular port of ChatRoom-React that renders the same DOM from the same fixtures. It uses zone.js rather than zoneless change detection: zoneless Angular renders in a setTimeout/rAF race that the steps' MessageChannel yield does not wait for in Firefox, so interactions would coalesce and the suite would measure less work than the other ports. The repository's Prettier predates Angular's control flow syntax, so .prettierrc turns off embedded formatting for this workload's TypeScript files.
canova
force-pushed
the
chatroom-svelte-angular
branch
from
November 12, 2026 11:30
73ad925 to
e8d32b1
Compare
mstange
added a commit
to mozilla/Speedometer
that referenced
this pull request
Nov 12, 2026
The earlier ChatRoom-React commits are already on sp4-exp. PR WebKit#597 changes them by giving Vite's output files stable names. This brings suites-experimental/chat-room up to the PR's version.
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.
This PR adds
ChatRoom-SvelteandChatRoom-Angular, ports ofChatRoom-Reactthat render the same DOM from the same fixtures. The steps move into a sharedchatRoomTestsarray, so all three suites are measured on identical interactions.?tags=chat-roomruns them side by side.Depends on #583, only the last five commits are new. Each port has a source commit and a separate build commit.