patternjavascriptMinor
Custom marquee consumes a lot of CPU power
Viewed 0 times
marqueeconsumeslotpowercustomcpu
Problem
I've a code which applies marquee to certain elements by using the
Also, there are cases where even 3 or 4 elements are being targeted by the marquee. There is also a case in my application, not so common, that there is another animation that scrolls text from the right to the left.
My development environment is a lot different, I'm using a 3.8GHz 8-cores CPU with a strong GPU so my CPU usage is no more than 11%.
I'm not sure if it matters but locally I'm using xampp and on the tested PC I've been using wamp.
The code for my marquee is splitted into few functions:
```
/**
* Applies the marquee function to the elements who are overflowing
*/
function tryMarquee(/**/) {
var args = arguments;
for(var i=0; i containerHeight && containerHeight > 10) {
marquee(args[i], speed, spacer, spacerHeight);
}
}
}
/**
* Calculates the content height of an element by it's children's height
* @param elem
* @returns {number}
*/
function calculateContentHeight(elem) {
var total = 0;
elem.children().not('.clone').each(function() {
if(parseInt($(this).css('margin-top')) >= 0)
total += $(this).height() + parseInt($(this).css('margin-top'));
else
total += $(this).height();
});
total -= 10;
return total;
}
/**
* Generates a spacer.
* @param marginTop
* @param height
@returns {|jQuery|HTMLElement}
*/
function generateSpacer(marginTop, height) {
var spacer = $('');
height = height || 60;
spacer.css({
height: height
});
return spacer;
}
/**
* returns a clone of an element's children
* @param elem
@returns {|jQuery|HTMLElement}
*/
function duplicateContent(elem) {
retur
requestAnimFrame method. However, when I test my application on a lower spec PC (Intel Celeron 2.13GHz dou core) the CPU usage is skyrocketing and gets to a minimum of around 80%! (I have to mentioned that I have 2 elements that the custom maruqee is applied to). Also, there are cases where even 3 or 4 elements are being targeted by the marquee. There is also a case in my application, not so common, that there is another animation that scrolls text from the right to the left.
My development environment is a lot different, I'm using a 3.8GHz 8-cores CPU with a strong GPU so my CPU usage is no more than 11%.
I'm not sure if it matters but locally I'm using xampp and on the tested PC I've been using wamp.
The code for my marquee is splitted into few functions:
```
/**
* Applies the marquee function to the elements who are overflowing
*/
function tryMarquee(/**/) {
var args = arguments;
for(var i=0; i containerHeight && containerHeight > 10) {
marquee(args[i], speed, spacer, spacerHeight);
}
}
}
/**
* Calculates the content height of an element by it's children's height
* @param elem
* @returns {number}
*/
function calculateContentHeight(elem) {
var total = 0;
elem.children().not('.clone').each(function() {
if(parseInt($(this).css('margin-top')) >= 0)
total += $(this).height() + parseInt($(this).css('margin-top'));
else
total += $(this).height();
});
total -= 10;
return total;
}
/**
* Generates a spacer.
* @param marginTop
* @param height
@returns {|jQuery|HTMLElement}
*/
function generateSpacer(marginTop, height) {
var spacer = $('');
height = height || 60;
spacer.css({
height: height
});
return spacer;
}
/**
* returns a clone of an element's children
* @param elem
@returns {|jQuery|HTMLElement}
*/
function duplicateContent(elem) {
retur
Solution
Profiling in Opera suggests that it is mainly the time taken to draw the text which is consuming CPU (I do this in Opera simply because Opera is the only browser I know of that has a comprehensive profiler which profiles all aspects of page generation). Rendering fonts is one of the slowest things a web browser can do, and it appears that the browser re-renders the text on each frame, and that is basically what is consuming the cpu like crazy. And it appears that regardless of what mechanism is used to scroll the text the repaint is triggered.
Some fonts are more work to render than others and you'd benefit from a font which is quick to render, unfortunately I don't know if
As an experiment I tried putting a (large) image in the marquee and the CPU usage is much less when scrolling an image. This makes sense because it's a lot less work for a computer to blit an image than to render fonts. Hence one option would be to marquee an image of text instead of text. I know this isn't really a good solution for a lot of reasons, but it would be fast.
The other solution is a really obvious and simple one - reduce the number of frames rendered per second. At the moment your code runs at 60fps which is way higher than is really needed. Actually, at a reasonable text scrolling rate for human reading speed, you could get away with 20fps or maybe even 10fps.
At the moment you use a constant scroll amount per frame. This is not a good way to do it. Instead you should use a constant scroll rate per unit time, meaning the text scrolls at the same rate even if the browser chooses to render fewer than 60fps. Essentially what you need to do is calculate the elapsed time since the last frame, and use that to determine how far to scroll. The callback for
In that code I've also added a frameskip option, which means 'skip this many frames for each one rendered'. You can set the rate per second to get the desired speed, and then adjust frameskip to get the desired performance.
Some fonts are more work to render than others and you'd benefit from a font which is quick to render, unfortunately I don't know if
As an experiment I tried putting a (large) image in the marquee and the CPU usage is much less when scrolling an image. This makes sense because it's a lot less work for a computer to blit an image than to render fonts. Hence one option would be to marquee an image of text instead of text. I know this isn't really a good solution for a lot of reasons, but it would be fast.
The other solution is a really obvious and simple one - reduce the number of frames rendered per second. At the moment your code runs at 60fps which is way higher than is really needed. Actually, at a reasonable text scrolling rate for human reading speed, you could get away with 20fps or maybe even 10fps.
At the moment you use a constant scroll amount per frame. This is not a good way to do it. Instead you should use a constant scroll rate per unit time, meaning the text scrolls at the same rate even if the browser chooses to render fewer than 60fps. Essentially what you need to do is calculate the elapsed time since the last frame, and use that to determine how far to scroll. The callback for
requestAnimationFrame is passed the current time (in milliseconds) so this is quite easy, here is a minimalistic example:function gogogo(scrollRatePerSecond, frame_skip) {
var lastFrameTime = null,
i = 0;
function loop(now) {
var delta = lastFrameTime ? now - lastFrameTime : 0;
if (++i % (frame_skip + 1) == 0) {
lastFrameTime = now;
var scrollAmount = delta * scrollRatePerSecond / 1000;
// Do something with scrollAmount
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}In that code I've also added a frameskip option, which means 'skip this many frames for each one rendered'. You can set the rate per second to get the desired speed, and then adjust frameskip to get the desired performance.
Code Snippets
function gogogo(scrollRatePerSecond, frame_skip) {
var lastFrameTime = null,
i = 0;
function loop(now) {
var delta = lastFrameTime ? now - lastFrameTime : 0;
if (++i % (frame_skip + 1) == 0) {
lastFrameTime = now;
var scrollAmount = delta * scrollRatePerSecond / 1000;
// Do something with scrollAmount
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}Context
StackExchange Code Review Q#67530, answer score: 2
Revisions (0)
No revisions yet.