Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

High CPU usage for tab loading animation #1384

Closed
SimonSapin opened this issue Sep 27, 2017 · 53 comments
Closed

High CPU usage for tab loading animation #1384

SimonSapin opened this issue Sep 27, 2017 · 53 comments

Comments

@SimonSapin
Copy link

Short description

The @keyframes throbber animation is expensive. "Normal" tabs in Firefox 57 / 58 have a similar-looking animation but it’s implemented in a completely different way:

  • Tree Style Tabs has a zero-size pseudo-element with a solid border, border-radius: 100%, and animates the width and left properties.
  • Firefox has an SVG sprite (chrome://browser/skin/tabbrowser/loading.svg) as a background-image in a pseudo-element that animates transform: translateX(…) with a steps(60) timing function.

As far as I understand, the latter is cheaper in current Firefox because things with animated transform are often painted into a separate layer that can be preserved and re-composited without re-painting when the transform changes. In comparison, changing the width of something with border-radius requires re-painting it.

Steps to reproduce

  1. Start Firefox with clean profile.
  2. Install TST.
  3. In a terminal, run netcat -l 8888 or some other command to start a TCP server that will accept an HTTP request but never send a response
  4. In Firefox, navigate to http://localhost:8888. This causes the tab to be "loading" until closed or stopped, and shows the "throbber" animation in place of the tab’s favicon.

Expected result

This animation by itself should not use a significant amount of resources.

Actual result

Firefox uses 40% ~ 80% CPU even though it is doing nothing else. This makes loading actual pages slower.

Environment

@SimonSapin
Copy link
Author

Thank you for porting Tree Style Tabs to a WebExtension! I had not realized how much I missed it until installing it again :)

Thank you as well for including the "Extra style rules for sidebar contents" config, it’s a nice escape hatch for small hacks. I’ve used it to temporarily replace the throbber animation with something much cheaper (if uglier):

.tab.loading:not(.collapsed) .throbber::before,
:root.blocking-throbber #blocking-screen .throbber::before {
  animation: throbber 1s frames(2) infinite;
}

.throbber::before {
  box-shadow: none;
  border-radius: 0;
  border-width: 3px
}

@keyframes throbber {
  from { transform: translateX(-6px) }
}

@piroor
Copy link
Owner

piroor commented Sep 28, 2017

There are two points:

  • SVG or HTML?
  • transform or left/width?

After I read your descriptions, I thought that migration to transform seems the most important. Is it correct?

@piroor
Copy link
Owner

piroor commented Sep 28, 2017

I experimentally rewrite the animation with CSS transforms. For more natural animation it should be improved more, but It actually reduces CPU usage for me. How about this?

@keyframes throbber {
  0% { transform: translateX(0); }
  3% { transform: translateX(0); }
  20% { transform: translateX(calc((var(--favicon-size) - var(--throbber-size)) / 2)) scaleX(2); }
  47% { transform: translateX(calc(var(--favicon-size) - var(--throbber-size))); }
  53% { transform: translateX(calc(var(--favicon-size) - var(--throbber-size))); }
  70% { transform: translateX(calc((var(--favicon-size) - var(--throbber-size)) / 2)) scaleX(2); }
  97% { transform: translateX(0); }
  100% { transform: translateX(0); }
}

.throbber::before {
  left: 0;
}

.tab.loading:not(.collapsed) .throbber::before,
:root.blocking-throbber #blocking-screen .throbber::before {
  animation: throbber 1.05s steps(60) infinite;
}

@piroor
Copy link
Owner

piroor commented Sep 28, 2017

It still uses CPU 10% or more for me. Hmm...

@asamuzaK
Copy link
Contributor

FYI this is how I animate the throbber.
sidebarTabs/loading.svg

@piroor
Copy link
Owner

piroor commented Sep 28, 2017

Finally I've replace the implementation totally. d56d1e6

@asamuzaK
Copy link
Contributor

Your implementing throbber with ::before pseudo, right?
Then I think you should check with RTL too (change intl.uidirection pref in about:config to 1).
See 1392622#c19

@piroor
Copy link
Owner

piroor commented Sep 28, 2017

I forgot to set svg.context-properties.content.enabled to true by myself... With default configuration of Firefox, SVG throbber become invisible on dark-colored themes. I've reverted changes for now.

@piroor
Copy link
Owner

piroor commented Sep 28, 2017

@asamuzaK Thank you for advises! Updated version based on your code:

@keyframes throbber {
  0%    { transform: translateX(0) scale(1, 1); }
  17.5% { transform: translateX(calc((var(--favicon-size) - var(--throbber-size)) * 0.625)) scale(1.6, 0.9); }
  50%   { transform: translateX(calc(var(--favicon-size) - var(--throbber-size))) scale(1,1); }
  67.5% { transform: translateX(calc((var(--favicon-size) - var(--throbber-size)) * 0.625)) scale(1.6, 0.9); }
  100%  { transform: translateX(0) scale(1, 1); }
}

.throbber::before {
  left: 0;
}

.tab.loading:not(.collapsed) .throbber::before,
:root.blocking-throbber #blocking-screen .throbber::before {
  animation: throbber 1.05s ease infinite;
}

@SimonSapin
Copy link
Author

SimonSapin commented Sep 30, 2017

The throbber is still using significant CPU on 2.0.1, at least for me. I suppose that using scale() still forces it to be re-painted (compared to Firefox’s throbber which only uses translateX() in steps within a sprite).

@asamuzaK
Copy link
Contributor

Hmm, are there any noticeable differences in these three examples?
test

@nh2
Copy link
Contributor

nh2 commented Oct 26, 2017

I'm using 2.0.7 and the loading symbol makes 90% CPU usage on my laptop.

Which of the above code snippets are still up-to-date that you'd recommend to mitigate this locally in the short term, no matter how ugly?

@nh2
Copy link
Contributor

nh2 commented Oct 26, 2017

Because, oddly even the solution from above (#1384 (comment)) that just makes a square hop left and right wit 2 FPS, causes my Firefox to go > 50% CPU when connecting to http://localhost:1234 after running nc -l 1234 -k (so that I can observe the animation for forever).

This is assuming it is actually the animation that causes it and not something else about loading pages that hang in Firefox.

@nh2
Copy link
Contributor

nh2 commented Oct 26, 2017

This is assuming it is actually the animation that causes it and not something else about loading pages that hang in Firefox.

OK, turns out that this problem even exists with display: none:

.tab.loading:not(.collapsed) .throbber::before,
:root.blocking-throbber #blocking-screen .throbber::before {
  display: none;
}

Not sure if this is a TST or Firefox problem.

@nh2
Copy link
Contributor

nh2 commented Oct 26, 2017

Not sure if this is a TST or Firefox problem.

OK, so with TST disabled, the CPU usage goes down to 30%, still horrible for doing nothing.

strace shows that it's throwing out poll() syscalls like crazy. After closing the forever-loading tab still some polls show up but not at high frequency "busy polling" any more.

I wonder if what I'm observing is the CPU time taken by Firefox's actual loading animation even though I have hidden the top tab bar.

@Lej77
Copy link
Contributor

Lej77 commented Nov 13, 2017

I experimented with Firefox's tab throbber animation a couple of weeks ago and from what I can remember it causes high CPU usage if it is hidden in any way. For example, it will causes high CPU usage if a throbber is placed outside of the visible area, if a throbber's opacity is set to 0 or if a throbber is set to display: none;. The only exception I found where the trobber could be invisible but still not use much CPU was if the throbber was inside the visible area but behind another element that covered it up.

Don't know if this happens anywhere in the extension but thought I should mention it anyways.

@nh2
Copy link
Contributor

nh2 commented Nov 18, 2017

Here's some more data, with a Thinkpad X220 which can report accurate power consumption information via the tp_smapi module (numbers in milliwatts; native FF tab bar is hidden, TST is shown):

while true; do cat /sys/devices/platform/smapi/BAT0/power_now; sleep 1; done
-13540
-13540
-12829
-12829
-11857
-11857
-12748
-12748
-12847
-12847
-12369
-12369
-24232 # throbber turned on here
-24232
-17086
-17086
-14579
-14579
-23677
-23677
-12429
-12429
-24202
-24202
-12623
-12623
-12600
-12600
-23664
-23664
-12047
-12047
-23512
-23512 # throbber turned off with ESC here
-11501
-11501
-11570
-11570
-16759
-16759
-16759
-11517
-11517
-14298
-14298
-11658
-11658
-11515
-11515

So the throbber doubles my laptop's power consumption, despite nothing being loaded.

(Using the blocky-jumpy throbber instead of the smooth one redures the problem only insignificantly, by ~1W.)

@nh2
Copy link
Contributor

nh2 commented Nov 18, 2017

it causes high CPU usage if it is hidden in any way

I can confirm what @Lej77 said. When NOT hiding the normal FF tab bar, then having a normal throbber in TST and the FF tab bar has this Wattage impact:

-11794
-11794
-11794
-11485
-11485
-11917
-11917
-12302 # normal throbber started here
-12302
-12305
-12305
-12876
-12876
-12260
-12260
-12952
-12952
-12470
-12470
-12832
-12832
-12405 # ESC pressed here (throbber stopped)
-12405
-11599
-11599
-11263
-11263
-11273
-11273
-11398

When hiding it with #TabsToolbar { visibility: collapse; }, I get this instead:

-11886
-11886
-11505
-11505
-11861
-11861
-11308
-11308
-11572
-11572
-24570 # Normal hrobber started here (hidden in FF tab bar, shown in TST)
-24570
-20856
-20856
-12579
-12579
-23575
-23575
-13146
-13146
-13146
-23857
-23857
-13464
-13464
-12597
-12597
-24094
-24094
-12931
-12931
-23418
-23418 # Pressed ESC to end throbber here
-13096
-13096
-16862
-16862
-11809
-11809
-11654
-11654
-11643
-11643
-11690
-11690

The alternating ~12W/24W are consistent with the alternating 20%/100% CPU usage shown in htop.

When the native FF tab bar is shown, then the CPU usage does not go over 15%.

Summary:

  • If the throber is visible in TST and the FF tab bar, it adds only 0.5 W energy use
  • If the throbber is visible only in TST but not the FF tab bar, it adds up to 12 W energy use

@Lej77
Copy link
Contributor

Lej77 commented Nov 18, 2017

@nh2 What about when hiding FF tab bar and not using the TST sidebar?

@nh2
Copy link
Contributor

nh2 commented Nov 18, 2017

What about when hiding FF tab bar and not using the TST sidebar?

  • FF tabs on, TST disabled: Low CPU usage, doesn't go over 15%
  • FF tabs off, TST disabled: constant 30% CPU usage, wattage at -13178

Summary:

Native bar on Native bar off
TST on + shown good slightly worse
TST disabled good HORRID

Edit: It also makes no difference if TST is entirely disabled or just the TST bar hidden with F1.

(As a reminder, to make a page that can't load I use nc as shown above.)

@Lej77
Copy link
Contributor

Lej77 commented Nov 18, 2017

@nh2 So this is a bug in Firefox then? Its not an actual problem with TST itself?

@Lej77
Copy link
Contributor

Lej77 commented Nov 18, 2017

@nh2 Also i wrote some more about hiding throbbers without performance issues on Issue 1543.

Don't know if that can be used to hide FF tab throbbers without performance issues though.

@nh2
Copy link
Contributor

nh2 commented Nov 18, 2017

I edited to add a bit more info:

It also makes no difference if TST is entirely disabled or just the TST bar hidden with F1.

@Lej77

So this is a bug in Firefox then? Its not an actual problem with TST itself?

I think there is at least a bug in Firefox, as even if TST is not installed, CPU usage goes up when the native tab bar is hidden.

For the HORRID entry in the table, it is unclear to me whether this is a bug in Firefox or in TST.

@Lej77
Copy link
Contributor

Lej77 commented Nov 18, 2017

@nh2 How big is the difference between "good", "slightly worse" and "HORRID"?

@nh2
Copy link
Contributor

nh2 commented Nov 18, 2017

How big is the difference between "good", "slightly worse" and "HORRID"?

@Lej77 that's the numbers I reported above; in table form:

Native bar on Native bar off
TST on + shown < 15% CPU 30% CPU
TST disabled or hidden < 15% CPU 100% CPU

@Lej77
Copy link
Contributor

Lej77 commented Nov 19, 2017

@nh2 I can't reproduce your high CPU usage for just one infinite loading tab. I get around 3-4% CPU with the sidebar open and 2-3% CPU with it closed. I am using TST v2.2.11 on Firefox 57 on Windows 10. Also firefox tab bar is hidden.

@nh2
Copy link
Contributor

nh2 commented Nov 19, 2017

@Lej77 I'm on Ubuntu 16.04, Firefox Nightly 59, TST v2.2.11.

@Lej77
Copy link
Contributor

Lej77 commented Nov 19, 2017

@nh2 Using Windows 10 I have tested with Firefox 57 and Firefox Nightly 59. Both with and without hidden firefox tab bar. I do notice lower CPU usage for both 57 and 59 without hiding firefox tab bar but its only about 1% difference and the total CPU usage is about 2-6%.

@Lej77
Copy link
Contributor

Lej77 commented Nov 19, 2017

@nh2 Also I noticed another bug when I was investigating this one. I wrote about it on a comment for Issue 1535. Don't know if you are affected but if you are then you might want to be careful not to trigger it. It should only be an issue if you have many tabs though.

@SimonSapin
Copy link
Author

(As a reminder, to make a page that can't load I use nc as shown above.)

By the way, https://httpbin.org/ also has https://httpbin.org/delay/60 (where 60 is a number of seconds).

@ssokolow
Copy link

ssokolow commented Nov 19, 2017

Depending on the state you're trying to get the browser into, you might also find the testcase I wrote for Bug 1276100 useful.

It's the only attachment and it's a Python script which holds an HTTP response incomplete at just the right stage to wedge the browser into a confusing in-between state. (Basically, Firefox malfunctions if the server leaves the response in a "document begun streaming but not enough received for an initial paint" state for long enough for the user to poke at it. There are details in the bug and the script itself will also present instructions when launched.)

@bitonic
Copy link

bitonic commented Nov 20, 2017

i have created https://bugzilla.mozilla.org/show_bug.cgi?id=1419096 to report this problem upstream, especially the fact that CPU usage increases if the throbber is hidden.

@bitonic
Copy link

bitonic commented Nov 20, 2017

also, by disabling firefox's own throbber in userChrome.css and the one in TST i'm able to keep the CPU (and thus the battery) under check. whad would be the simplest way to replace TST's throbber with just an icon?

@wlonkly
Copy link

wlonkly commented Nov 20, 2017

@bitonic Mind sharing how you disabled both in userChrome.css to hold us over for now?

@bitonic
Copy link

bitonic commented Nov 20, 2017

ah yes, apologies:

/* disable throbber in the hope for better performance */

.tab-throbber[busy] {
  display: none;
}

.tab-throbber[progress] {
  display: none;
}

@wlonkly

@msujaws
Copy link

msujaws commented Nov 21, 2017

Can you please gather a performance profile? You can use the tool at https://perf-html.io/ to find which parts of the code are taking the most time.

@ghost
Copy link

ghost commented Nov 25, 2017

maybe this is related to tab cycling slowdowns when using extension hotkeys? switching between tabs with vim-vixen shift+j/k there is some serious lag compared to the master hotkeys ctrl+pgup/pgdown.
Although I have tried completely disabling animations but it does not help

@lightweight
Copy link

I'm seeing the same problems with TST (and horizontal tab bar and top-of-TST bar successfully hidden via userChrome.css) on Linux Mint 18.3. @bitonic's CSS to hide the throbber isn't working for me... Still seeing the throbber everywhere, and massive CPU hits (with 60ish tabs open in 2 windows), including lots of blocking freezes (fairly frequently forcing a full kill -9 of Firefox from a console), especially on JS-intensive pages (e.g. tweetdeck).

@hiikezoe
Copy link

Hello @piroor!
I noticed that the throbber animation does not specify 0% keyframe value.

From throbber.css;
@Keyframes throbber {
100% { transform: translateX(-100%); }
}

If the animating element is scrolled out, the animation consumes more CPU than the situation where the animation is visible. I've been trying to fix it in bug 1419079, but if you could specify 0% value there (actually the value should not be transform:none, since we can't optimize the transform:none case), this high CPU usage will be solved. Thanks!

@piroor
Copy link
Owner

piroor commented Nov 29, 2017

@hiikezoe Thanks a lot! By d651bdf I've added the 0% frame for the animation. Does it work as expected?

@hiikezoe
Copy link

@piroor Thanks for the quick fix! Yeah, that's exactly what I've expected.
To be precise, bug 1190721 is also necessary to reduce the CPU usage, so we still need to wait for firefox 58.

@piroor
Copy link
Owner

piroor commented Nov 29, 2017

@hiikezoe Now the animation target is intentionally placed out of screen and referrenced as a mask image via mask-image: -moz-element(...). Will bug 1190721 break this method?

@hiikezoe
Copy link

@piroor Interesting. Bug 1190721 just tries to stop calculating animation styles on out-of-view elements. I am not sure such animations referenced via -moz-element work fine. Would you mind uploading a simple test case somewhere? I'd like to see what happens there.

@piroor
Copy link
Owner

piroor commented Nov 29, 2017

@hiikezoe I've created a simple testcase.

throbber.zip

It seems to work as expected on my environment: Nightly 59.0a1 (Build ID 20171127220446) on Ubuntu 16.04LTS.

@hiikezoe
Copy link

@piroor Thanks for the test case! The test case noticed me that there are two issues we need to tackle.

  1. We don't yet optimize animations on out-of-view position:absolute-ed elements (bug 1421507)
  2. We do accidentally optimize animations referenced by -moz-element (bug 1421506)

The throbber animation in the test case works fine due to 1) for now. Anyway bug 1190721 does not affect the test case at all, and I am going to do optimizations for animations with the test case in my mind.

I am not sure using a single animation and referencing the animating element from multiple elements is better for CPU usage rather than specifying an animation on each element respectively though.

@SimonSapin
Copy link
Author

Does this off-screen throbber still exist when there are no visible throbber? Could https://bugzilla.mozilla.org/show_bug.cgi?id=1417460 be related to this?

@piroor
Copy link
Owner

piroor commented Nov 29, 2017

@SimonSapin The throbber element exists always. But animation is applied only when there is one or more loading tab.

:root.have-loading-tab #master-throbber,
:root.blocking-throbber #master-throbber {
  animation: throbber 1.05s steps(60) infinite;
}

The class have-loading-tab appears only when there is any loading tab.

@Omen-of-Peace
Copy link

hiikezoe mentioned "To be precise, bug 1190721 is also necessary to reduce the CPU usage, so we still need to wait for firefox 58."

I'm on:
Firefox 58 b9
TST 2.3.0

Not sure when the relevant commit is supposed to be released, but for me disabling the throbber still results in considerably less CPU during page loads.
I also sometimes had the issue where the throbber wouldn't stop (probably a Firefox bug), which caused full CPU usage; the laptop fans would start.

(I used the following code to disable the throbber, but it might be imperfect or overkill - I'm far from a CSS expert.)
.throbber {
display: none !important;
}
.throbber::before {
animation: unset !important;
}

@hiikezoe
Copy link

hiikezoe commented Dec 6, 2017

@Omen-of-Peace I guess you might need to set display:none for #master-throbber element. It's the only one animating element for all throbbers in TST, IIUC.

@piroor
Copy link
Owner

piroor commented May 2, 2019

I close this because outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests