motiondivision / motion

A modern animation library for React and JavaScript
https://motion.dev
MIT License
25.85k stars 849 forks source link

Optimize batcher hot-loop #2720

Closed kurtextrem closed 4 months ago

kurtextrem commented 4 months ago

While profiling hydration performance, @iamakulov found this to be a hot-loop: image

  1. First thought: .reduce is quite slow compared to a native for loop, so what I did is:

    • use for instead of reduce
    • make sure the object type is known before assigning props
  2. Second thought: but also since we use TS, we can guarantee that Steps and Batcher contain every key, so:

    • to avoid the loop during object creation, we manually unroll the loop

Bench: https://jsperf.app/misawo

image

Note

Just from the code alone I'm not sure how this can get hot, as we call it only on export (twice in total?). This might tell us not the reduce is hot but rather the function inside -> (runNextFrame = true). But since I was at it, I figured why not do it for the sake of comparing how slow for/reduce/object creation really is.

mattgperry commented 4 months ago

I’ll have a look next week but my first thought is that I’m curious about the benefits of unrolling the reduce. I think hoisting the callback is a good idea as it gets called a lot, not need to make 6 of them. But the reduce is the way it is to save bundlesize - we’re talking about a loop that gets called twice ever, probably responsible for nanoseconds of overhead.

kurtextrem commented 4 months ago

Agreed, this was probably a profiler inaccuracy (the screenshot shows the full FN is hot but it's more like the inner FN). let's hoist only the inner FN in a follow up pr?