You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
performance regression in average_frame_rasterizer_time_millis #194807
A PR has caused a regression in this benchmark. It seems to be attributed to #194341. Any change to rasterizer times as a result of a dart change seems surprising.
We should have someone do a local bisect to see if the problem can be tracked down.
I bisected and debugged this with help from AI. Analysis is below. I discussed with @gaaclarke and will close this issue.
Summary
This is not a real rasterizer regression. A Dart VM change in the roll reduced how often background garbage collection (GC) runs during this benchmark. Because the CPU is doing less background work overall, the OS scales down the CPU clock frequency, making the short per-frame raster bursts take longer in wall-clock time (while still finishing around ~2 ms, well within the 16 ms frame budget). Because this down-clocking only happens when the overall CPU workload is light, it will not make heavy frames slower and is not expected to increase visible jank in real world apps.
What caused the shift
The regression is a ~1.5× jump in average_frame_rasterizer_time_millis when running the complex_layout_scroll_perf_bad benchmark on an iPhone. We were able to reproduce the same regression running locally on macOS. Bisecting the Dart roll range narrows the regression to 3.15.0-26.dev → 3.15.0-27.dev. The only VM change contained within that version bump is dart-lang/sdk@0e87827aa370 ([vm, gc] Increase page size to 2 MB). (cc @rmacnak-google as an FYI)
That commit quadrupled the Dart heap page size (Page::kPageSize) from 512 KB to 2 MB, which also raised the threshold for triggering an idle old-generation GC between scroll bursts (from +1 MB to +4 MB). As a result, the VM runs significantly less background GC work (~30–45% fewer old-gen GCs) during the benchmark:
Local (macOS): Old-gen GCs dropped from 36 → 20, and background GC marking time (ConcurrentMark in the timeline trace) dropped from 1.94 s → 1.05 s.
Why less GC work makes raster times look higher
The raster thread in this benchmark only runs for ~0.5–1 ms per frame (~5% of the time). When background GC worker threads are running on other cores, they keep the CPU cluster spun up at full clock speed. With fewer background GCs, the CPU spends more time in a down-clocked / low-power state when the raster thread wakes up. Analyzing timeline trace output (from output of flutter drive -t test_driver/scroll_perf_bad.dart -d macos --profile --trace-startup) shows:
No specific raster bottleneck: Timeline traces show every single sub-phase of the raster thread (preroll, drawing, glyph atlas, presentation) is uniformly ~1.7× slower.
Frames during GC are still fast: Within a single trace, frames that overlap with background GC run at the same fast speed before and after the roll (p90 ≈ 0.8–0.9 ms), whereas the slower frames (p90 ≈ 2.0–2.2 ms) only happen during quiet periods when no background GC is active.
Proof: Keeping the CPU busy erases the difference
To confirm this is purely CPU frequency scaling (DVFS), we ran two unrelated background busy-loops (yes > /dev/null) alongside the benchmark to keep the CPU cluster at full clock speed:
Condition
p50 raster (ms)
p90 raster (ms)
Before (3.15.0-26.0.dev), idle
0.57
1.26
After (3.15.0-27.0.dev), idle
0.73
2.23
Before (3.15.0-26.0.dev), with CPU load
0.34
0.60
After (3.15.0-27.0.dev), with CPU load
0.45
0.60
Keeping the CPU warm makes both versions faster than the idle pre-roll baseline and completely eliminates the 90th-percentile gap (0.60 ms vs. 0.60 ms).
Conclusion
The Dart VM change is a net CPU/GC efficiency improvement, and missed_frame_rasterizer_budget_count on CI did not change. The jump on the graph is a power-management (DVFS) side effect on a lightly loaded device.
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.
Activity
b-luk commentedon Oct 21, 2026
I bisected and debugged this with help from AI. Analysis is below. I discussed with @gaaclarke and will close this issue.
Summary
This is not a real rasterizer regression. A Dart VM change in the roll reduced how often background garbage collection (GC) runs during this benchmark. Because the CPU is doing less background work overall, the OS scales down the CPU clock frequency, making the short per-frame raster bursts take longer in wall-clock time (while still finishing around ~2 ms, well within the 16 ms frame budget). Because this down-clocking only happens when the overall CPU workload is light, it will not make heavy frames slower and is not expected to increase visible jank in real world apps.
What caused the shift
The regression is a ~1.5× jump in
average_frame_rasterizer_time_milliswhen running thecomplex_layout_scroll_perf_badbenchmark on an iPhone. We were able to reproduce the same regression running locally on macOS. Bisecting the Dart roll range narrows the regression to 3.15.0-26.dev → 3.15.0-27.dev. The only VM change contained within that version bump is dart-lang/sdk@0e87827aa370 ([vm, gc] Increase page size to 2 MB). (cc @rmacnak-google as an FYI)That commit quadrupled the Dart heap page size (
Page::kPageSize) from 512 KB to 2 MB, which also raised the threshold for triggering an idle old-generation GC between scroll bursts (from +1 MB to +4 MB). As a result, the VM runs significantly less background GC work (~30–45% fewer old-gen GCs) during the benchmark:old_gen_gc_countdropped from 30 → 22, and overall deviceaverage_cpu_usagedropped from ~40% → ~35%.ConcurrentMarkin the timeline trace) dropped from 1.94 s → 1.05 s.Why less GC work makes raster times look higher
The raster thread in this benchmark only runs for ~0.5–1 ms per frame (~5% of the time). When background GC worker threads are running on other cores, they keep the CPU cluster spun up at full clock speed. With fewer background GCs, the CPU spends more time in a down-clocked / low-power state when the raster thread wakes up. Analyzing timeline trace output (from output of
flutter drive -t test_driver/scroll_perf_bad.dart -d macos --profile --trace-startup) shows:Proof: Keeping the CPU busy erases the difference
To confirm this is purely CPU frequency scaling (DVFS), we ran two unrelated background busy-loops (
yes > /dev/null) alongside the benchmark to keep the CPU cluster at full clock speed:3.15.0-26.0.dev), idle3.15.0-27.0.dev), idle3.15.0-26.0.dev), with CPU load3.15.0-27.0.dev), with CPU loadKeeping the CPU warm makes both versions faster than the idle pre-roll baseline and completely eliminates the 90th-percentile gap (0.60 ms vs. 0.60 ms).
Conclusion
The Dart VM change is a net CPU/GC efficiency improvement, and
missed_frame_rasterizer_budget_counton CI did not change. The jump on the graph is a power-management (DVFS) side effect on a lightly loaded device.github-actions commentedon Nov 4, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.