MiniSearch / docs /performance-profile-2571.md
system's picture
system HF Staff
Sync from felladrin/MiniSearch@6c9fb20
1a6d0d2 verified
|
Raw History Blame Contribute Delete
3.89 kB

Long-task profile: search with AI response and page content

Issue: #2571 β€” keep long tasks off the main thread during search and generation.

This is the "recorded before and after" evidence the issue asks for. The measurement harness is e2e/main-thread-budget.spec.ts: a longtask PerformanceObserver installed with page.addInitScript before the app boots, so it sees every task the app runs, with a streaming window opened when the inference request is issued and closed once the full answer has rendered.

Method

  • Production build (npm run build && HOST=127.0.0.1 PORT=<p> npm run start), Chromium full channel β€” chrome-headless-shell never emits longtask entries, so it cannot be used for this.
  • Everything external is mocked (config, search, page content, and the OpenAI-compatible inference stream served by a local SSE server), so the numbers are not noisy and no live service is involved.
  • enablePageContentFetch: true, so the search-to-generation handoff is exercised.
  • The profiled answer is an 18.8 kB streamed answer: 70 markdown paragraphs, ~70 inline links and one JavaScript code block. The size is deliberate β€” the per-frame cost the issue describes grows with the answer, so a short answer shows nothing.
  • 5 runs per build, alternating, on the same machine.

Result

Build Long tasks in the streaming window Total main-thread time blocked Longest single task
main (before) 30, 31, 31, 32, 32 2008, 2050, 2143, 2149, 2207 ms 108–186 ms
All seven fixes merged (after) 0, 0, 1, 1, 1 0, 0, 51, 52, 52 ms ≀ 52 ms

Roughly 2.1 seconds of blocking main-thread work per streamed answer is gone, and the worst single task drops from ~160–190 ms to at or below the 50 ms long-task reporting threshold β€” i.e. after the change there is effectively nothing left to report.

What removed what

Long task on main Removed by
Full-markdown re-parse on every throttled frame (12 Hz), growing with the answer perf/memoized-markdown-blocks β€” top-level blocks are memoized, so a frame that only extends the last block re-renders that block
Shiki loading from shiki/bundle/full for the first code block perf/shiki-lazy-language-subset β€” web subset, and a language outside it degrades without a load attempt
One resize listener per citation link, each reading scrollWidth and calling setState perf/shared-resize-observer-citation-links β€” one shared ResizeObserver, and the expand animates transform/opacity instead of width
gptTokenizer.encode() over every full page body at the search-to-generation handoff perf/page-excerpt-tokenization-worker β€” the tokenization runs in a worker, with a synchronous fallback
Re-encoding the whole joined candidate per iteration in the rolling summary perf/incremental-rolling-summary-tokens β€” each part encoded once, running count kept

Reproducing

The committed spec asserts the 200 ms budget on a code-block-free answer, which is the path that passes on main too. To reproduce the table above, widen the answer (paragraphs + links + a code block), raise the budget so the assertion cannot fail, and read the collected task list against a main build and against the merged branches.

Caveats

  • Measured on one machine (arm64 Linux, Chromium 153). Absolute numbers move with the hardware; the before/after gap is large enough that the direction is not in doubt.
  • The numbers cover the mocked streaming path. A real deployment adds upstream latency, not main-thread work, so the main-thread picture is representative.
  • perf/e2e-long-task-budget is the branch that carries the guard; the seven fixes are the ones that moved these numbers.