Skip to content

Add a profiling harness for the progress-save re-render - #81

Open
jonocodes wants to merge 1 commit into
mainfrom
profile-progress-rerender
Open

jonocodes wants to merge 1 commit into
mainfrom
profile-progress-rerender

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Answers a question raised during #76: ArticleScreen subscribes to the whole article row via useLiveQuery, so every reading-progress save re-renders the screen even though nothing on screen depends on progress. Does that cost anything? This adds a repeatable way to measure it.

Verdict: not worth fixing

With #76 applied, per save on a production build:

CPU Extra main-thread time Layout / style Long tasks
1× 2.5 ms 0 none
4× 8.7 ms 0 none
6× 12.1 ms 0 none

The save fires 1s after scrolling stops, so this happens at an idle moment, and it fits inside a single 16.7ms frame even at 6×. 125 components re-render, mostly MUI's Emotion styling layer and closed drawers. It's real but not something a reader would notice.

How it works

npm run profile:rerender does three things:

  1. Builds with build:dev (production React with debug hooks) and serves dist/ on the per-worktree e2e app port.
  2. Writes progress to the open article's row (the liveQuery re-emits and the screen re-renders), and makes the same number of writes to a different article's row (same IndexedDB cost, no re-render). The difference is the cost of the re-render.
  3. Reports per-save task/script/layout/style time from CDP at 1×/4×/6× CPU throttling, plus long tasks, commits, and which components re-rendered.

It counts components by installing a stub of the React DevTools hook before React loads, so no product code changes.

npm run profile:rerender                 # build, serve, profile
npm run profile:rerender -- --no-build   # reuse the existing dist/
npm run profile:rerender -- -g 6x        # extra args go to Playwright

Two safeguards:

  • It profiles the production build on purpose. Against the dev server the same measurement comes out 5–7× worse and shows constant long tasks, which would have made this look like a real problem.
  • It refuses to start if anything is already listening on the app port. Playwright's reuseExistingServer would silently profile whatever is there. This already caught a real case: a leftover preview server while I was testing this branch.

The spec only runs when SAVR_PROFILE=1 is set, so it never runs in the normal suite. It also asserts that the control writes don't re-render, otherwise the subtraction would be meaningless.

Evidence the harness detects regressions

This branch is cut from main, without #76. Run here, the harness shows the DOM rebuild that #76 removes:

CPU without #76 with #76
1× 10.0 ms, 3.0 ms layout 2.5 ms, 0 layout
4× 29.7 ms, 8.7 ms layout 8.7 ms, 0 layout
6× 46.0 ms, 13.5 ms layout 12.1 ms, 0 layout

The layout column is the tell: a metadata-only re-render should never cause layout. That's written up in docs/DEVELOPMENT.md as what a regression looks like.

Other changes

  • scripts/e2e-ports.js: moved the per-worktree port derivation out of run-e2e.js so the profiler serves on exactly the port the suite expects. Ports are unchanged; smoke tests pass.
  • docs/DEVELOPMENT.md: a Profiling section with baseline numbers, a table row for the spec, and a known issue: Chromium fails to launch inside Paseo sessions. Paseo puts its own alsa-lib on LD_LIBRARY_PATH, and that build needs a newer glibc than the nix-provided Playwright Chromium has. The workaround is env -u LD_LIBRARY_PATH npm run test:e2e.

Not measured

With header hiding on, every change of scroll direction calls setShowHeader, which probably re-renders the same 125-component tree during a scroll, when a frame actually matters. If anything here deserves work, it's that rather than the save. The harness could be extended to cover it.

Independent of #76 and can merge in either order. Running it on main before #76 lands will show the pre-fix numbers.

🤖 Generated with Claude Code

ArticleScreen subscribes to the whole article row via useLiveQuery, so
every reading-progress save re-renders the screen even though nothing on
screen depends on `progress`. This measures what that costs, so the
question can be answered with numbers rather than reasoning.

Method: identical progress writes to the open article's row (liveQuery
re-emits -> re-render) and to a different article's row (same IndexedDB
cost, no re-render). The difference is the re-render. Reports per-save
task/script/layout/style time at 1x/4x/6x CPU throttling via CDP, long
tasks, commits, and which components re-rendered (counted through a
React DevTools hook stub, so no product code changes).

`npm run profile:rerender` builds and serves a production React build
first: against the dev server the numbers come out 5-7x worse. It refuses
to run if anything already listens on the app port, since Playwright would
silently reuse it and profile the wrong build.

Current cost after #76: ~2.5/9/12ms per save at 1x/4x/6x, no long tasks,
no layout. Not worth fixing — it lands at an idle moment and fits in a
frame. On main without #76 the same run shows ~10/30/46ms with 3-13ms of
layout, which is the DOM rebuild that fix removes.

Also extracts the per-worktree port derivation from run-e2e.js into
scripts/e2e-ports.js so the profiler serves on the port the suite expects,
and documents a Paseo LD_LIBRARY_PATH issue that stops Chromium launching.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@netlify

netlify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for savrlist ready!

Name Link
🔨 Latest commit 0660447
🔍 Latest deploy log https://app.netlify.com/projects/savrlist/deploys/6ac1d9139e01a90008336f9b
😎 Deploy Preview https://deploy-preview-81--savrlist.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

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