Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

FYI: Frame rate throttling when no frames are produced

208 views
Skip to first unread message

Francois Pierre Doray

unread,
Feb 7, 2025, 1:03:02 PMFeb 7
to blink-dev, li1....@intel.com, Zheng, Hong
Hi,

We tested an intervention on Canary/Dev/Beta that reduces the frame rate by half (e.g., from 60fps to 30fps) after 4 consecutive frames without pixel changes. The frame rate returns to normal immediately upon pixel changes or input events. For example:

let last = performance.now();
let c = () => {
  window.requestAnimationFrame(c);
  let now = performance.now();
  console.log(now - last);
  last = now;
}
c();

The c() function's invocation frequency halves after 4 calls due to the lack of pixel updates. Note: As a result, a subsequent frame with pixel updates may be delayed by up to 1 frame (but frame rate returns to normal immediately after a frame with pixel updates).

This intervention significantly improves LCP, INP and CPU usage on Beta, confirming our prior observation that no-op frames often occupy the main thread during page load or input handling. To validate these results, we need a 1% stable experiment (user behavior differs between pre-stable and stable channels). Before proceeding, we'd appreciate feedback on potential issues this experiment might cause. We will determine our next steps based on input from this discussion.

Thanks,

François

[cc:  Chen, Li and Zheng, Hong from Intel who proposed and implemented this intervention]

Johnny Stenback

unread,
Feb 7, 2025, 2:13:11 PMFeb 7
to Francois Pierre Doray, Camillo Bruni, blink-dev, li1....@intel.com, Zheng, Hong
Hey Francois,

This sounds promising. My one concern is what effect(s) this might have on our performance benchmarking work. +Camillo Bruni for his thoughts. We should at the very least make sure speedometer/motionmark/etc numbers are not negatively impacted by this change.

Cheers,
Johnny

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.
To view this discussion visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/a6db8984-6c56-4e84-954b-7b0ffae8b461n%40chromium.org.

Michal Mocny

unread,
Feb 7, 2025, 2:18:21 PMFeb 7
to Francois Pierre Doray, blink-dev, li1....@intel.com, Zheng, Hong
Very cool experiment, and happy to see positive results!

Feedback RE: "no-op frames often occupy the main thread during page load or input handling"...

My past experience with interactions and smoothness is that no-op-raf-loops can have more subtle effects on UX than would be predicted by just occupying the main thread.  Perhaps some pages have expensive rAF callbacks, but commonly what I had seen in the past was something more like the following:
  • User input arrives.  A HIGHEST priority task is scheduled, jumping the task queue.
  • Some default action (or custom event listeners), trigger.  Page is updated and requires rendering.
  • If you haven't had a rAF this vsync yet, we will schedule BMF as soon as possible, and at highest priority, typically fast enough to get scheduled immediately after the event.
  • But, if you have already had a rAF scheduled for this animation frame already (such as would always be true with a raf loop), now we will now wait for the next vsync first.
  • This extra wait means we have some idle time after the event, and will schedule some other normal/background/idle priority tasks.  It was  these that would block the main thread.  (This could even include work that you explicitly deferred from the event listener, expecting it to run after rendering, and you thought you could delay a long-task until after INP triggers...)
In other words: in the presence of raf-loops, you tend to get extra normal/idle priority tasks sneak after input and before rendering (assuming the event handler doesn't itself run long and into the next vsync).  That was often the worst culprit of latency, that I noticed.

One specific example I recall: fetch() calls from an event listener.  If the resource was already cached, the .then() handler would either get scheduled before or after the BMF, depending if we had to wait for vsync or not (i.e. such as animations, or the presence of raf-loop based monitoring).  And the fetch().then() handler might be very expensive and the developer expects it to always be scheduled way later than the input event, and so doesn't think it affects responsiveness.  (For example, several SPA routers historically would do this by default-- and route prefetching on hover made INP performance worse!)

(Note: Some of the above is changing with an experiment Scott Hasely is running to defer task scheduling between input and rendering.)

---

All that said, if I'm right about the above, it seems to me that decreasing frame rates would only fix the latency issue ~50% of the time.  Assuming that the rAF still gets scheduled at the start of the vsync cycle when it does trigger, just happens to be at half the frame rate?  That's a good step -- but I wonder if we could use the signal (4 raf frames without pixel updates) in the future to go even further?
  • Could we also defer the scheduling of the BMF tasks to be at the end of the vsync cycle (i.e. by also delaying BMF task for 1vsync duration)?
    • This way, if input does arrive even mid-frame for a frame that wasn't throttled, the event would still be scheduled first.
    • Something like this could still hit 30hz, though each frame has a bit more latency.  Since these frames will likely not commit an update anyway, perhaps frame deadlines don't matter?

  • And, what about the opposite: if we know that rAF's are producing pixel updates consistently, could we schedule BMF with higher priority?
    • vsync frame rates are one thing, but today we also have a policy of defaulting the BMF task to NORMAL priority (for 100ms, then boosting to HIGHEST).
    • On some pages we see this policy decreasing main thread rendering rates to 8-10hz if other tasks are already queued in the scheduler.
    • Perhaps this signal should be used to drive rendering task priority?
    • I've heard that the current scheduling policy works well for early loading use cases to balance latency and throughput-- and that we've wanted to experiment with a higher priority rendering mode when post-load, but worried about over-scheduling no-op raf loops... maybe this feature helps alleviate that risk?
Cheers!



--

Camillo Bruni

unread,
Feb 10, 2025, 9:22:44 AMFeb 10
to Johnny Stenback, Francois Pierre Doray, blink-dev, li1....@intel.com, Zheng, Hong
+1 to double checking the benchmark
Most likely we're good as we should have at most a single idle rAF.
Camillo Bruni | Software Engineer, V8 | Google Germany GmbH | Erika-Mann Str. 33, 80636 München 

Registergericht und -nummer: Hamburg, HRB 86891 | Sitz der Gesellschaft: Hamburg | Geschäftsführer: Paul Manicle, Halimah DeLaine Prado

Diese E-Mail ist vertraulich. Falls Ssie diese fälschlicherweise erhalten haben sollten, leiten Sie diese bitte nicht an jemand anderes weiter, löschen Sie alle Kopien und Anhänge davon und lassen Sie mich bitte wissen, dass die E-Mail an die falsche Person gesendet wurde.  This e-mail is confidential. If you received this communication by mistake, please don't forward it to anyone else, please erase all copies and attachments, and please let me know that it has gone to the wrong person.

Li1 Chen

unread,
Feb 12, 2025, 3:41:56 PMFeb 12
to blink-dev, Camillo Bruni, Francois Pierre Doray, blink-dev, li1....@intel.com, Zheng, Hong, Johnny Stenback

Hi,

 

We have tried Speedometer3 and MotionMark for this feature on pinpoint.

Speedometer3:

https://pinpoint-dot-chromeperf.appspot.com/job/11eca91ea10000

https://pinpoint-dot-chromeperf.appspot.com/job/166ca91ea10000

MotionMark:

https://pinpoint-dot-chromeperf.appspot.com/job/11289a3ba10000

https://pinpoint-dot-chromeperf.appspot.com/job/14289a3ba10000

 

From the pinpoint data we can see that this feature does not affect the performance of Speedometer3. It only causes regression in the MotionMark subcase Design. The root cause is that the Design case uses rAF to calculate the frame length(https://github.com/WebKit/MotionMark/blob/baf37c1fcb690abbe048e249e248e7847bddd34f/MotionMark/tests/resources/main.js#L182), but there is no frame update. Since the frame rate is throttled after 4 DidNotProduceFrame, the frame length is reduced to 33ms, which affects the complexity calculation in the case and causes regression. In MotionMark only Design case uses no-op frame to calculate frame length, which does not seem reasonable, so only this subcase is regressed.

 

Thanks

Li

uazo

unread,
Feb 14, 2025, 3:26:47 AMFeb 14
to blink-dev, Li1 Chen, Camillo Bruni, fdo...@chromium.org, blink-dev, hong....@intel.com, jste...@chromium.org
>  We tested an intervention on Canary/Dev/Beta that reduces the frame rate by half (e.g., from 60fps to 30fps) after 4 consecutive frames without pixel changes. 

would it be possible to study the changes you have made in the code? is there an associated bugid? thank you.

Li1 Chen

unread,
Feb 18, 2025, 11:25:36 AMFeb 18
to blink-dev, uazo, blink-dev

François Doray

unread,
Mar 10, 2025, 2:23:08 PM (12 days ago) Mar 10
to blink-dev, Li1 Chen, uazo, blink-dev
Hi,

We will proceed with the Stable 1% experiment for this intervention.

Rationale:
    • The MotionMark regression is well understood. I discussed with +Camillo Bruni and we concluded that it isn't a blocker for measuring the impact of this intervention on Stable 1%. However, we will address any MotionMark regression before launching a change.
    • Michal Mocny suggested worthwhile refinements. We will start by measuring the impact of this intervention as-is on Stable 1%. If we observe good results, it will be a good justification for investing in a "launchable" intervention, including integrating Michal Mocny's suggestions.
Have a nice day,

François
Reply all
Reply to author
Forward
0 new messages