MarshallOfSound

#51852: build: calibrate PGO collection workloads and fix renderer profile loss

Merged
Created: Jun 2, 2026, 5:46:07 PM
Merged: Jun 2, 2026, 8:56:33 PM
3 comments
Target: main

What

Fixes two independent problems in the PGO profile collection pipeline that combined to produce the badly-shaped macOS arm64 profiles consumed by v42.3.1 (and generated for 43-x-y):

1. Renderer profile loss (the big one). The macOS collection runs the instrumented app sandboxed, and sandboxed child processes fail their exit-time profraw write (LLVM Profile Error: ... Operation not permitted). Which processes lose is a per-run race — the 42-x-y and 43-x-y collection runs both lost their web-benchmark renderer data, shipping profiles where Blink is essentially absent from the hot set (4 DOM functions in the top-50k vs ~400 expected). This is the root cause of the v42.3.1 Speedometer regression on Apple Silicon. Fixed with %c continuous-mode profiles on Darwin (counters live in an mmap established before sandbox lockdown — no exit-time write to lose) plus --no-sandbox on the macOS collect step, matching what the Linux step already does.

2. Count-budget distortion. The synthetic workloads were capped by wall time, so their tight loops dominated the merged profile: the top 2 blocks held ~10% of all counts, inflating the global hot threshold and demoting real Blink work out of the hot tier. Hotness is a threshold (~10^5 counts), not a share — capping the loops by iterations keeps every electron path 2–3 orders of magnitude above the bar while ending the distortion. MotionMark is soft-capped at 120s (it was 5.4 minutes — 57% of all web-benchmark time — and doubled Skia's weight vs Chrome's corpus), and the reclaimed time funds two extra Speedometer runs. A new async-churn workload (250k deliberately tiny async ops) keeps the per-async-op machinery (AsyncWrap, BaseObject lifecycle, stream plumbing) trained, since its counts ride on async op volume rather than loop iterations.

Also: the profraw directory is now cleaned at collection start — stale %m pool files from a previous run silently merge their counters into a new collection.

Validation

Instrumented 42-x-y build, same scripts CI runs, before → after:

Metric before after
profraw files collected 9 (renderers lost, 5 write errors) 23+ (zero errors)
blocks holding 90% of counts 1,710 14,000+
Blink DOM functions in top-50k 4 ~404 (Chrome-profile parity)
Blink share of hot mass 14% ~33–35%
top-2-block share ~10% 2.6%
node serialization paths 1.1B-count spikes hot at sane magnitudes (StringBytes::Write 410M, contextBridge 15M)

A useful side effect: the simdutf dispatcher drops below the hot threshold, so profiles from the fixed collection no longer trigger the LLVM unroller miscompile that #51848 guards against (that flag stays as defense-in-depth until the upstream fix rolls in).

Suggested follow-up (not in this PR): a shape gate in the generation workflow — fail the run if blocks@90% < 8,000 or Blink DOM functions in the top-50k < 100 — so a malformed profile can never silently ship again.

Notes: none

Backports

42-x-y
Merged
PR Number
#51854
Merged At
Jun 2, 2026, 8:57:20 PM
Released In
v42.3.3
Release Date
Jun 3, 2026, 2:18:37 PM
43-x-y
Merged
PR Number
#51855
Merged At
Jun 2, 2026, 8:57:24 PM
Released In
Not yet
Release Date
Not yet

Semver Impact

Major
Breaking changes
Minor
New features
Patch
Bug fixes
None
Docs, tests, etc.

Semantic Versioning helps users understand the impact of updates:

  • Major (X.y.z): Breaking changes that may require code modifications
  • Minor (x.Y.z): New features that maintain backward compatibility
  • Patch (x.y.Z): Bug fixes that don't change the API
  • None: Changes that don't affect using facing parts of Electron