Skip to content

Latest commit

 

History

History
80 lines (69 loc) · 5.59 KB

File metadata and controls

80 lines (69 loc) · 5.59 KB

Release performance profiles

CI also records the package footprint after each passing gate: staged-tree ZIP and unpacked bytes, bundled DNR rules, manifest-injected scripts, and content and worker source bytes. The limits in static-budget.json are compared with the staged package by node tools/check-performance-budget.js; CI keeps the measurements as a build artifact for 90 days. A release ZIP archived from a commit includes its commit ID in the ZIP comment, so its byte count can differ from the staged-tree metric; the published SHA-256 covers the actual release ZIP. These are size and rule-count checks. The browser profile below measures execution time and heap with the worker held awake; what a worker wake costs the browser is measured separately, under Cold worker wakes.

Each profile-<commit>.json here is an enabled-versus-disabled measurement of that release candidate in a real browser with the real extension loaded, produced by node tools/perf-profile.js; the .md beside it is the readable summary, and the JSON keeps the raw per-run values, the readiness evidence and the metadata (commit, browser and extension versions, CPU, throttle, date). Raw DevTools traces from --trace land in traces/, which is not tracked (tens of MB each).

The separate YouTube memory review records the owner's 1.2 GB Brave warning, controlled playback measurements, and what remains unproven.

Cold worker wakes

The worker stops after about thirty quiet seconds, and the next tab close, popup open or navigation starts it again. Whatever a wake asks of Chrome is answered on the browser's UI thread, the one that draws the tab strip and routes scrollbar drags. node tools/browser-cold-wake.js waits for the remote lists to install, lets the worker sleep, then closes a tab and opens the popup, each on a sleeping worker, and fails on any UI-thread stall answering a rule-API call or on more than 300 ms of UI-thread stalls in all. It needs Edge and network access and takes about three minutes, so it is not part of the gate.

Measured on 2026-10-01 (Edge 150, i5-12400F, 22,134 dynamic rules). Six appliers read every dynamic rule on each wake to filter their own band; Chrome builds each reply on the UI thread in 170-270 ms and sends it as a ~21.8 MB message. Closing a tab on a sleeping worker froze the UI thread six times, 1,123 ms in all; dragging the popup's scrollbar straight after opening it saw a worst move of 500 ms to 5 s. With band-filtered reads (getDynamicRulesInBand) the same tab close shows no rule-API stall (UI thread 226-252 ms busy over 5 s, against 1,260 ms) and the popup drag's worst move is 66 ms. The harness above keeps a debugger attached to the worker, which keeps it awake, so it cannot see any of this; releaseWorker in perf-profile.js lets a tool measure a wake.

How to read one

  • off is the same browser with no extension; on is WardenOne loaded from the working tree. The profile is the difference, per page, per phase (cold = first navigation with the cache cleared, warm = a reload), over five runs: median, p90 and max.
  • The pages are synthetic and local: an article with cross-site links and a sign-in form, a page that builds a deep DOM tree in-document the way frameworks do (the mutation-scan stress case), and a page with eight same-origin frames. They are meant to be harder than an ordinary page, not representative of one; the numbers are ceilings for those shapes, and a change between two profiles matters more than any absolute.
  • Compare cells within one profile. Between browser sessions the frames page in particular moves by up to 2x (code-cache state for the per-frame scripts), while the five runs inside one session stay tight; the harness runs every variant in its own fresh session for that reason, and a cross-session comparison is only meaningful when the change is larger than that.
  • regress:mutation-dedup is a deliberately broken copy of the extension -- the outermost-node dedup of the mutation scan undone -- and exists to prove the harness can see a known structural regression before its numbers are trusted as a gate. --expect-regression fails the run if it cannot.
  • The harness fails closed: an "on" variant whose worker does not report the repository's version, or whose engine did not stamp its readiness marker in the page, refuses to measure; an "off" variant with the marker present refuses too. Chrome 152 no longer loads unpacked extensions from the command line, so the browser is Microsoft Edge, and its version is in every profile.

1.0.1 at 4cd3119 (2026-09-20)

Edge 150, i5-12400F, no CPU throttle, 5 runs per cell. Main-thread task time, cold, median: article 43 ms off / 95 ms on (+52 ms); churn stress page 192 ms / 1,094 ms (+902 ms, one long task of ~170 ms); eight frames 50 ms / 546 ms (+496 ms). Heap after GC: +0.8 MB on the article, +2.5 MB on the churn page, +5.9 MB with eight frames. The known regression showed as 1,458 ms against 1,094 ms on the churn page (+33%, long-task time 751 ms against 166 ms), so the harness is sensitive enough to serve.

What the numbers say, beyond "it costs something": the per-frame cost is the standout. The frames page adds roughly 60 ms of main-thread time per same-origin frame, which is the all-frames MAIN-world scripts (domain-utils.js, anti-redirect.js, fingerprint-realm.js, permission-chain.js) and the isolated bridge being evaluated in every frame; a page with many frames pays it many times. That is a measurement, not a fix, and it is recorded here so the next profile can be compared against it.