Multiple requestAnimationFrame performance
I don't think any of these answers really explained what I was looking for: "do n calls to requestAnimationFrame" get debounced (i.e. dequeued 1 per frame) or all get invoked in the next frame.
When callbacks queued by requestAnimationFrame() begin to fire multiple callbacks in a single frame (mdn)
This suggests the latter, multiple callbacks can be invoked in the same frame.
I confirmed with the following test. A 60 hz refresh rate translates to a 17ms period. If it were the former, no 2 timestamps would be within 17ms of each other, but that was not the case.
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));let update = async timestamp => { console.log('update called', timestamp) await sleep(10); requestAnimationFrame(update);}requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);requestAnimationFrame(update);
You should be using only one requestAnimationFrame
call as calls to requestAnimationFrame
do stack. The single callback version is thus more performant.
Someone benchmarked this. Let's talk...
https://jsperf.com/single-raf-draw-calls-vs-multiple-raf-draw-calls
I looked at the performance comparison (you should too). You're welcome to disagree. These are drawing primitives on a canvas element.
function timeStamp() { return window.performance && window.performance.now ? window.performance.now() : new Date().getTime(); } function frame() { drawCircle(); drawLines(); drawRect(); } function render() { if (timeStamp() >= (time || timeStamp())) { time = timeStamp() + delayDraw; frame(); } requestAnimationFrame(render); } function render1() { if (timeStamp() >= (time || timeStamp())) { time = timeStamp() + delayDraw; drawCircle(); } requestAnimationFrame(render1); } function render2() { if (timeStamp() >= (time || timeStamp())) { time = timeStamp() + delayDraw; drawRect(); } requestAnimationFrame(render2); } function render3() { if (timeStamp() >= (time || timeStamp())) { time = timeStamp() + delayDraw; drawLines(); } requestAnimationFrame(render3); }
I think this code is really benchmarking 7 calls to timestamp() vs 2 calls to timestamp(). Look at the difference between Chrome 46 and 47.
- Chrome 46: 12k/sec (one call) vs 12k/sec (3 calls)
- Chrome 47: 270k/sec (one call) vs 810k/sec (3 calls)
I think this is so well optimized that it doesn't make a difference. This is just measuring noise at this point.
My takeaway is this doesn't need to be hand-optimized for my application.
If you're worried about performance look at the difference between Chrome 59 (1.8m ops/sec) vs Chrome 71 (506k ops/sec).