Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upHigh CPU usage during a RAF loop #9844
Comments
|
This is a lot better since #9858 |
|
Still around ~15% here. Compared to Gecko: ~3%. |
|
Yeah, so this is another case of Servo's design currently making it impossible to catch up to other browsers here. Safari, Gecko, and Chrome all use |
|
This test shows that there's an actual overhead even when drawing: https://gist.github.com/paulrouget/99442585974f6661b7c5 25% CPU without rAF, 55% CPU with rAF. |
|
OK, that is suspicious. Event handling is expected to be somewhat expensive, but not that expensive. |
|
It is true that we drop a few frames with |
|
@pcwalton any idea what's going on here? |
|
Here's something interesting I found (feel free to confirm, @paulrouget): The CPU usage between rAF and non-rAF seems nearly-identical (about 21%) for me if the window is in the background. But if the window is in the foreground, then the CPU usage of rAF jumps up. |
|
I can't reproduce. Do you use the test from the first comment? Is anything being drawn in your loop? |
|
Yes, I'm using the test from the first comment. By not being able to repro, do you mean that the rAF usage is always high and the setTimeout is low, or that both are low? |
|
I'm using the test from the first comment. With rAF, I get ~20% with window in the background and foreground. With setTimeout instead, I get around ~5% with window in the background and foreground. |
|
Interesting. I get 20% for both rAF and setTimeout with the window in the background. |
|
This is the code I use for <script>
var x = 0;
window.requestAnimationFrame = c => setTimeout(c, 16);
setInterval(() => {
console.log(x);
x = 0;
}, 1000);
function foo() {
x++;
requestAnimationFrame(foo);
}
</script>
<button onclick="foo()">Start rAF</button> |
|
So this is at least part of it: When using setTimeout(), we get 53 FPS. When using rAF, we get 60 FPS. More FPS == more frames painted == more CPU. |
|
I'm seeing 27% with @paulrouget, your 5% CPU usage when using |
|
Tested on my 2015 Macbook, too, with different results: using |
|
Describing my protocol:
Results with my macbook 2015:
|
Ok, when actually testing the right code (I was testing with this), I see roughly the same result. Only difference is that with rAF I have 14%-16% CPU usage. |
repeatedly creating a new one when waking up the event loop from another thread. This also avoids sending this event to the `NSApplication`, since that's needless overhead. Reduces CPU usage of a simple `requestAnimationFrame()` loop in Servo by 23%. Partially addresses servo/servo#9844.
repeatedly creating a new one when waking up the event loop from another thread. This also avoids sending this event to the `NSApplication`, since that's needless overhead. Reduces CPU usage of a simple `requestAnimationFrame()` loop in Servo by 23%. Partially addresses servo/servo#9844.
cocoa: Reuse a single thread-local `NSEvent` instance instead of repeatedly creating a new one when waking up the event loop from another thread. This also avoids sending this event to the `NSApplication`, since that's needless overhead. Reduces CPU usage of a simple `requestAnimationFrame()` loop in Servo by 23%. Partially addresses servo/servo#9844. r? @paulrouget <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/glutin/87) <!-- Reviewable:end -->
typical `requestAnimationFrame()` animations.
This skips useless message traffic when `requestAnimationFrame()` is
called during an animation frame callback. It reduces CPU usage of the
following snippet by 49%:
<script>
function foo() {
requestAnimationFrame(foo);
}
</script>
<button onclick="foo()">Start rAF</button>
Partially addresses servo#9844.
typical `requestAnimationFrame()` animations.
This skips useless message traffic when `requestAnimationFrame()` is
called during an animation frame callback. It reduces CPU usage of the
following snippet by 49%:
<script>
function foo() {
requestAnimationFrame(foo);
}
</script>
<button onclick="foo()">Start rAF</button>
Partially addresses servo#9844.
…s, r=jdm
script: Avoid needless `ChangeRunningAnimationsState` messages during typical `requestAnimationFrame()` animations.
This skips useless message traffic when `requestAnimationFrame()` is
called during an animation frame callback. It reduces CPU usage of the
following snippet by 49%:
<script>
function foo() {
requestAnimationFrame(foo);
}
</script>
<button onclick="foo()">Start rAF</button>
Partially addresses #9844.
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/11205)
<!-- Reviewable:end -->
…s, r=jdm
script: Avoid needless `ChangeRunningAnimationsState` messages during typical `requestAnimationFrame()` animations.
This skips useless message traffic when `requestAnimationFrame()` is
called during an animation frame callback. It reduces CPU usage of the
following snippet by 49%:
<script>
function foo() {
requestAnimationFrame(foo);
}
</script>
<button onclick="foo()">Start rAF</button>
Partially addresses #9844.
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/11205)
<!-- Reviewable:end -->
…s, r=jdm
script: Avoid needless `ChangeRunningAnimationsState` messages during typical `requestAnimationFrame()` animations.
This skips useless message traffic when `requestAnimationFrame()` is
called during an animation frame callback. It reduces CPU usage of the
following snippet by 49%:
<script>
function foo() {
requestAnimationFrame(foo);
}
</script>
<button onclick="foo()">Start rAF</button>
Partially addresses #9844.
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/11205)
<!-- Reviewable:end -->
|
@paulrouget That doesn't seem to be true anymore since #11205. |
|
Just want to make sure, you are using latest master right? |
Yes. |
|
So now that #11260 appears to not be a problem anymore, here are the latest results on my macbook:
|
|
@paulrouget Should we consider it fixed? |
Good enough to run browserhtml. Divided by 2 the initial overhead. I still don't understand why it takes more CPU than |
|
@paulrouget @pcwalton says that's the necessary cost to vsync. |
|
@paulrouget I'll explain the issue. The ways to do that are limited. On the Mac and iOS, Apple recommends a class called CVDisplayLink. This is what Safari and Firefox use. When I used it, however, I got tearing if the swap interval was not set to 1, which is unexpected. Investigating further, I found that CVDisplayLink simply queries the refresh rate of the screen and sets up a timer for that interval—effectively, it just does a glorified The other, cross-platform method is through glXSwapBuffers on X11 and CGLFlushDrawable on the Mac, with the swap interval set to 1. The swap interval can be configured with glXSwapIntervalEXT on X11 and kCGLPFASwapInterval on the Mac. This causes glXSwapBuffers/CGLFlushDrawable to finish rendering, block until the next VBLANK, swap, and then return. This is the most reliable way to actually get the correct VBLANK timing—it has to be, or else tearing would occur. So this is the method we use. Now note that glXSwapBuffers/CGLFlushDrawable do two things: they swap the front and back buffer, and they block until the next VBLANK. In the case in which there is no rendering to be done, this is a waste: we don't want to swap buffers, but we do want to block until the nearest VBLANK. However, the API fuses both operations. So we actually have to paint the frame and perform the buffer swap. This uses a significant amount of CPU and GPU time, both in preparing the rendering commands and in the driver. As mentioned before, Safari and Firefox dodge this by using CVDisplayLink on the Mac, which only performs VBLANK notifications, which is why you see them use less CPU in this test case. But those VBLANK notifications are not as accurate as the ones we get from swapping buffers, at least according to my investigation. So there's a tradeoff: more CPU usage (though note that there's using not much if any more CPU in the case of actual rendering) versus more accurate timing. I made the latter choice. |
typical `requestAnimationFrame()` animations.
This skips useless message traffic when `requestAnimationFrame()` is
called during an animation frame callback. It reduces CPU usage of the
following snippet by 49%:
<script>
function foo() {
requestAnimationFrame(foo);
}
</script>
<button onclick="foo()">Start rAF</button>
Partially addresses servo#9844.
repeatedly creating a new one when waking up the event loop from another thread. This also avoids sending this event to the `NSApplication`, since that's needless overhead. Reduces CPU usage of a simple `requestAnimationFrame()` loop in Servo by 23%. Partially addresses servo/servo#9844.
Something as simple at that:
… shows a 30% to 50% CPU usage on my macbook.