Topic: JS animation performance: reflow/redraw cost, CPU use? (Page 1 of 1) Pages that link to <a href="https://ozoneasylum.com/backlink?for=30631" title="Pages that link to Topic: JS animation performance: reflow/redraw cost, CPU use? (Page 1 of 1)" rel="nofollow" >Topic: JS animation performance: reflow/redraw cost, CPU use? <span class="small">(Page 1 of 1)</span>\

 
Scott
Paranoid (IV) Inmate

From: schillmania.com
Insane since: Jul 2002

posted posted 11-01-2008 19:56

OK, this is a rather open-ended JS performance question, but one I imagine many people have wondered about. I've been considering redoing a "snow" script which I wrote many years ago, my main gripe with it (and others) is the CPU use; perhaps it's simply expensive to redraw many elements, but the performance numbers seem to vary widely between browsers.

The current implementation varies in CPU use depending on browser, from what I've seen - eg. 40% on Safari and Opera, perhaps using better redraw/reflow methods or hardware acceleration (?) vs. 90% in Firefox 3, on Mac OS X. I wrote this in 2003 and the animation has always been fairly smooth, but it is at the expense of CPU. I'm unsure if this is simply a browser DOM/reflow/layout efficiency thing we'll eventually see improvements in (eg. as with JS engines recently), or if there are things that can be done in the code which will make it noticeably faster.

I'm currently creating a bunch of <img> elements which I append to a document fragment, and then finally append to the document. They're absolutely-positioned, and the script runs through something like..
for (var i=this.snow.length; i-- {
this.snow[i].move();
}
.. Which simply increases the individual objects' x and y properties, sets the left/top position etc. This is obviously convenient for me, but expensive in terms of CPU given I'm moving so many elements at once. The number of elements being moved changes as flakes hit the bottom of the screen and "stick", etc. The core animation loop is a setInterval(), 20 msec.

I was thinking perhaps putting the snowflakes into a few common parent containers and then moving those around would improve things - eg., 4 abs-positioned <div> elements with a number of absolutely-positioned snowflakes, thus I'm only actually moving four elements via script - but that also seems to be fairly expensive CPU-wise as presumably the browser has to redraw all of those absolute child elements.

Even moving a single absolutely-positioned <img> element seems to have a fairly high CPU cost (setInterval() demo, 20% CPU in Firefox, 9% in Safari, 5% in Opera?)

Perhaps explicitly specifiying width and height, providing background colors and/or setting overflow:hidden might help any of these things, I haven't done any serious experimenting specifically with large amounts of animated elements such as this.

Anyone else have some good stories/findings?

(Edited by Scott on 11-01-2008 19:57)

kwei
Nervous Wreck (II) Inmate

From: Germany
Insane since: Jul 2008

posted posted 11-02-2008 00:50

I don't know whether this will increase speed a lot... But the bottleneck seems to lie in this.move. Here are 4 (random?) ideas, which just happen to pop into my head.

1. Cache the images' style.

code:
this.o.style.left = ...
->
this.S.left = ...



2. Call this.move() directly, not through this.animate()

3. Cache this.flakes[i] (in this.snow()):

code:
for (var i=this.flakes.length-1; i>0; i--) {
      if (this.flakes[i].active == 1) {
        this.flakes[i].animate();
        active++;
      } else if (this.flakes[i].active == 0) {
        used++;
      } else {
        waiting++;
      }
    }

->

    for (var f, i=0; f = this.flakes[i++];) {
      if (f.active == 1) {
        f.animate();
        active++;
      } else if (f.active == 0) {
        used++;
      } else {
        waiting++;
      }
    }



4:

code:
setInterval("snowStorm.snow()",75)
->
setInterval(snowStorm.snow,75)


Just out of curiosity (and because I am tired...), why are you calling one every 25ms and one every 75ms fpr windows 9x?
Increasing the timeout of 20ms just a little bit should not affect the snow effect that much, should it? It would certainly decrease CPU load. Probably a lot more than any of the above ideas!

Cheers,
kwei

_______________
http://weibell.de/

(Edited by kwei on 11-02-2008 00:55)

poi
Paranoid (IV) Inmate

From: Norway
Insane since: Jun 2002

posted posted 11-02-2008 16:43

In my experience JavaScript is rarely, if ever, the bottleneck when doing animations.
But that doesn't mean we shouldn't optimize things where we can.

It is impossible to know on which device your code will run, therefore prefer setTimeout( arguments.calee, delay ) over setInterval( foo, delay ). Usually you don't want to burn out the CPU and see the CPU load sky rocket.

About reflows, I can't think of a case where moving an absolutely positionned element should trigger one.


Whenever possible, things should be grouped to limit the number of HTML elements, and DOM manipulations : A few months ago, I tried a dots effect with thousands of dots, many of them moving together. I generated biiig CSS sprites and ended up just moving ~50 background images. The framerate never dropped below 50fps and there was ( visually ) several thousands of dots moving.


Regarding kwei's 2nd point, I'd go one step further and avoid the function call altogether. Functions calls are expensive ( try to limit them as with certain types of animations it's easy to reach the hundreds of thousand computations/calls per second ), so unless this makes the code really ugly favor inlining the heavy duty.



I ran a redraw perfomance test ( which is basically a simpler version of Christian 'aleto' Krebbs tests so that JavaScript has a lesser impact on the end results ). In a nutshell it simply moves some elements horizontally and add some until the framerate is stable around 25fps.

code:
Number of elements processed @ 25 fps:

                  +---------+---------+-------+-------+
                  | JS only | bg only |  GIF  | PNG32 |
+-----------------+---------+---------+-------+-------+
| Op 9.62 10467   |  80,225 |   1,470 |   700 |   755 |
+-----------------+---------+---------+-------+-------+
| Fx 3.1b1        |  42,000 |     715 |   470 |   420 |
+-----------------+---------+---------+-------+-------+
| Sf 4dp2 528.1.1 |  53,250 |   1,425 |   900 |   860 |
+-----------------+---------+---------+-------+-------+
| IE 8b2          |  28,250 |     370 |   230 |   230 |
+-----------------+---------+---------+-------+-------+
| IE 8b2*         |  28,200 |     945 |   375 |   360 |
+-----------------+---------+---------+-------+-------+
| Cr 0.3.154.9    |  66,750 |   2,350 | 2,030 | 1,940 |
+-----------------+---------+---------+-------+-------+

* in Compatibility mode

For the JS only runs, the elements were not appended to the DOM. This very test only served to show how JS alone doesn't cost much.

Each browser was open individually with a single tab, first running a test with no sprite then one with a sprite. Closing the browser and doing the 2 other tests.


Overall, the perfomance do vary greatly across browsers. But it's nice to see that PNG32 are as fast/slow as GIF images.


Scott: About the stacking of the snow at the bottom of the page, you could use a Canvas ( or SVG ) element to do just that and keep your CSS sprites for the snow flakes.


Hope this gives a few hints.



(Edited by poi on 11-02-2008 18:35)

poi
Paranoid (IV) Inmate

From: Norway
Insane since: Jun 2002

posted posted 11-03-2008 12:02

Note:

  • on the redraw perfomance test above, Opera does a single reflow after executing the update() function.
  • I will add an argument to test with text instead of a background color/image.
  • I will check if I can enable Tracemonkey in Fx 3.1b1 but that should only effect the JS only results.



Feel free to tweak the CSS to stress test the impact of various CSS properties.

poi
Paranoid (IV) Inmate

From: Norway
Insane since: Jun 2002

posted posted 11-03-2008 19:41
code:
Number of elements processed @ 25 fps:

                  +---------+---------+-------+-------+-------+
                  | JS only | bg only |  GIF  | PNG32 |  text |
+-----------------+---------+---------+-------+-------+-------+
| Op 9.62 10467   |  80,225 |   1,470 |   700 |   755 |   975 |
+-----------------+---------+---------+-------+-------+-------+
| Fx 3.1b1        |  42,000 |     715 |   470 |   420 |   360 |
+-----------------+---------+---------+-------+-------+-------+
| Fx 3.1b1 jit    |  40,575 |     725 |   475 |   410 |   385 |
+-----------------+---------+---------+-------+-------+-------+
| Sf 4dp2 528.1.1 |  53,250 |   1,425 |   900 |   860 |   965 |
+-----------------+---------+---------+-------+-------+-------+
| IE 8b2          |  28,250 |     370 |   230 |   230 |   225 |
+-----------------+---------+---------+-------+-------+-------+
| IE 8b2*         |  28,200 |     945 |   375 |   360 |   525 |
+-----------------+---------+---------+-------+-------+-------+
| Cr 0.3.154.9    |  66,750 |   2,350 | 2,030 | 1,940 | 1,250 |
+-----------------+---------+---------+-------+-------+-------+



Scott
Paranoid (IV) Inmate

From: schillmania.com
Insane since: Jul 2002

posted posted 11-03-2008 19:55

P01: Fancy stats! Thanks for digging in, I'll take some time later to check this out. I'm interested in general animation performance in particular, of course, though the snow script is a decent real-world example which could be optimized further.

kwei: For Win9X, I found back in the day that having two setInterval() calls running at slightly different intervals will produce a much smoother animation framerate than one. Windows 2000 and WinXP+ don't seem to benefit from this. It shouldn't be in quotes either as you noted, that code was written back in 2002/03.

(Edited by Scott on 11-03-2008 19:56)

liorean
Paranoid (IV) Inmate

From: Umeå, Sweden
Insane since: Sep 2004

posted posted 11-03-2008 23:28

I blogged a bit about timers a while back. http://web-graphics.com/2007/03/06/timer-resolutions-and-browsers/

Might shed some insight into why that might work better.

--
var Liorean = {
abode: "http://liorean.web-graphics.com/",
profile: "http://codingforums.com/member.php?u=5798"};

Scott
Paranoid (IV) Inmate

From: schillmania.com
Insane since: Jul 2002

posted posted 11-07-2008 23:56

John Resig has also written about JS timers (how they work), and performance. The latter has some interesting graphs showing timing data between browsers. It looks like Safari nails timings quite wonderfully. These tests are nearly a year old, so things may be fairly different in nightly builds of Opera, Firefox etc.

I've gone back and forth between setTimeout vs. setInterval over the years for JS animation loops, and generally I've found that setInterval(animateStuff,20) tends to provide pretty good overall performance. The demos po1 linked use setTimeout() with 0 or 1, but I presume that is done to encourage the highest execution speed possible rather than a value that provides a fast, but not necessarily CPU-pegging framerate.

(Edited by Scott on 11-07-2008 23:56)

poi
Paranoid (IV) Inmate

From: Norway
Insane since: Jun 2002

posted posted 11-10-2008 17:57

Robert Kieffer has announced JSLitmus a tool to allow to quickly and easily write performance tests. It comes with a set of tests incrementing a variable in various scopes and shows, if need be, that local variables are much faster than global variables. But what surprises me is the cost of closures in Fx 3.0.3.

One thing about the results showed on the announcement page : the footnote says the test were run in either MacOS X or Windows Vista without saying which was done on what.

coach
Bipolar (III) Inmate

From:
Insane since: May 2011

posted posted 05-31-2011 11:10
Edit TP: spam removed


Post Reply
 
Your User Name:
Your Password:
Login Options:
 
Your Text:
Loading...
Options:


« BackwardsOnwards »

Show Forum Drop Down Menu