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
v43.0.0-beta.1
Release Date
Jun 4, 2026, 3:31:55 PM

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