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

modify immer metrics to scan 5000 index #1

Merged
merged 2 commits into from
Jan 11, 2025

Conversation

robstax
Copy link

@robstax robstax commented Jan 11, 2025

this is super interesting and appreciate all the work. i am seeing slow behavior with immer 9 and freeze.
i'm not necessarily trying to merge anything, but just wanted to open this PR to show some benchmark changes that highlight some interesting results
in immerjs/immer#1152 it's mentioned that

Overall, it does appear that Immer is significantly slower than both hand-written reducers and mutative. It looks like the majority of that time is due to freezing, but it also appears that Immer's perf has gotten worse over time.

i'm not familiar enough with the internals of immer to know guess what is causing things to slowdown, but from my testing, there are cases were freeze: false significantly worsens performance.
from my testing, performance gets worse when "scanning" an array.
please let me know if i've misunderstood the code or the benchmarks.

if i'm understanding these benchmarks correctly, there is an array of 10000 items, and then we call various actions/operations.
add -> push
remove -> splice
updateItem -> find
concat -> concat

in the original benchmarks

reducers[version](initialState, actions[action](i))      

the i which is only 0, so the find and splice don't do much. in this PR, i switched i to 5000 (so find the element with item.id === action.payload.id and splice(5000, 1)) and things get much worse, especially with freeze: false.

i'm aware freeze is the default and highly recommended for immutability and safety, but it also seems like it is basically required for performance reasons.
i've attached my benchmarks below.

clk: ~3.11 GHz
cpu: Apple M1 Pro
runtime: node 18.20.5 (arm64-darwin)

benchmark                             avg (min … max) p75   p99    (min … top 1%)
----------------------------------------------------- -------------------------------
add: vanilla (freeze: false)            15.91 µs/iter  16.05 µs █ █ █     █          
                                (15.52 µs … 17.10 µs)  16.61 µs ███▁██▁▁▁▁█▁▁▁▁▁▁▁▁▁█
add: immer9 (freeze: false)              2.24 ms/iter   2.22 ms ▅█                   
                                  (2.19 ms … 3.21 ms)   2.64 ms ██▄▃▁▁▁▁▂▂▁▁▁▁▁▁▁▁▁▁▁
add: immer10 (freeze: false)             2.19 ms/iter   2.18 ms █▇                   
                                  (2.17 ms … 2.55 ms)   2.45 ms ██▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
add: mutative (freeze: false)           14.09 µs/iter  12.17 µs █                    
                               (10.92 µs … 402.29 µs) 103.08 µs █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
add: mutativeCompat (freeze: false)     14.85 µs/iter  15.02 µs ▃             ▃  █   
                                (13.52 µs … 17.43 µs)  15.23 µs █▁▁▁▁▆▁▁▁▁▁▁▁▁█▁▁█▆▆▆
add: vanilla (freeze: true)             16.74 µs/iter  16.82 µs                █     
                                (16.27 µs … 17.61 µs)  17.00 µs ▆▆▆▁▆▁▁▁▁▁▁▁▁▆▆█▁▆▁▁▆
add: immer9 (freeze: true)               2.33 ms/iter   2.34 ms  █                   
                                  (2.26 ms … 3.07 ms)   2.68 ms ██▄▂▇▃▂▁▁▁▁▂▁▁▁▁▁▁▁▁▁
add: immer10 (freeze: true)              2.27 ms/iter   2.26 ms  █                   
                                  (2.24 ms … 2.70 ms)   2.50 ms ██▄▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
add: mutative (freeze: true)            14.24 µs/iter  14.54 µs                  ██  
                                (13.23 µs … 15.07 µs)  14.66 µs █▁██▁▁▁▁▁▁▁▁▁█▁▁█████
add: mutativeCompat (freeze: true)     405.53 µs/iter 397.62 µs █▆                   
                                (371.50 µs … 1.40 ms) 668.33 µs ███▃▂▁▁▁▁▁▁▁▂▂▁▁▁▁▁▁▁

summary
  add: mutative (freeze: false)
   1.01x faster than add: mutative (freeze: true)
   1.05x faster than add: mutativeCompat (freeze: false)
   1.13x faster than add: vanilla (freeze: false)
   1.19x faster than add: vanilla (freeze: true)
   28.78x faster than add: mutativeCompat (freeze: true)
   155.46x faster than add: immer10 (freeze: false)
   158.86x faster than add: immer9 (freeze: false)
   160.8x faster than add: immer10 (freeze: true)
   165.08x faster than add: immer9 (freeze: true)

----------------------------------------------------- -------------------------------
remove: vanilla (freeze: false)          3.10 µs/iter   3.36 µs █▆▃▃           ▃  ▆▃ 
                                  (2.79 µs … 3.45 µs)   3.44 µs ████▄▄▁▄▁▄▁▄▁▆██▄▄██▆
remove: immer9 (freeze: false)         442.53 ms/iter 433.17 ms ██ ▃                 
                              (428.74 ms … 548.04 ms) 454.21 ms ██▆█▆▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▆
remove: immer10 (freeze: false)        487.38 ms/iter 487.51 ms   █   ▃              
                              (482.99 ms … 508.55 ms) 490.80 ms ▆▁█▆▆▁█▁▁▁▁▁▆▆▁▁▁▁▁▁▆
remove: mutative (freeze: false)         4.80 ms/iter   5.25 ms  █▄        ▅▅▃       
                                  (3.91 ms … 7.24 ms)   6.00 ms ███▂▁▁▁▁▂▂▄███▆▄▅▂▂▁▂
remove: mutativeCompat (freeze: false)   5.14 ms/iter   5.56 ms  █▅         ▂▂       
                                  (4.24 ms … 8.24 ms)   6.25 ms ▄██▃▁▁▁▁▁▁▇███▇▄▇▄▂▁▂
remove: vanilla (freeze: true)           3.18 µs/iter   3.47 µs █▂▂              ▂   
                                  (2.87 µs … 3.61 µs)   3.59 µs ███▁▃▃▁▁▁▁▁▁▃▃▄▄▃██▄▃
remove: immer9 (freeze: true)            6.90 ms/iter   7.07 ms     █▂▆▅             
                                  (6.39 ms … 7.82 ms)   7.68 ms ▃▃▆▆████▆▃█▂█▆▃▃▁▁▂▄▃
remove: immer10 (freeze: true)           6.80 ms/iter   7.01 ms      ▃  █▃           
                                  (6.33 ms … 7.44 ms)   7.35 ms ▃▃▇▃▇█▄███▄▄▄▄▆█▁█▁▇▂
remove: mutative (freeze: true)          5.14 ms/iter   5.55 ms  ▅▆     █▃▇          
                                  (4.04 ms … 7.42 ms)   7.03 ms ▅██▃▂▃▁▄████▂▄▃▁▂▃▁▁▃
remove: mutativeCompat (freeze: true)    6.36 ms/iter   7.23 ms    █           ▃▂    
                                  (5.08 ms … 9.41 ms)   7.85 ms ▆███▆▃▁▁▁▁▁▁▁▆▇███▅▂▂

summary
  remove: vanilla (freeze: false)
   1.02x faster than remove: vanilla (freeze: true)
   1547.33x faster than remove: mutative (freeze: false)
   1655.43x faster than remove: mutativeCompat (freeze: false)
   1657.09x faster than remove: mutative (freeze: true)
   2050.99x faster than remove: mutativeCompat (freeze: true)
   2192.69x faster than remove: immer10 (freeze: true)
   2222.63x faster than remove: immer9 (freeze: true)
   142611.12x faster than remove: immer9 (freeze: false)
   157063.74x faster than remove: immer10 (freeze: false)

----------------------------------------------------- -------------------------------
update: vanilla (freeze: false)         55.36 µs/iter  83.50 µs █▅               ▃   
                               (32.12 µs … 553.08 µs)  91.29 µs ██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂█▇▂▁
update: immer9 (freeze: false)         436.28 ms/iter 438.95 ms                   █ █
                              (427.78 ms … 449.94 ms) 439.59 ms ██▁▁█▁▁█▁▁▁▁▁█▁█▁▁███
update: immer10 (freeze: false)        489.95 ms/iter 489.07 ms      █ █      █      
                              (485.29 ms … 513.99 ms) 490.52 ms █▁▁▁▁███▁█▁▁▁▁██▁▁▁▁█
update: mutative (freeze: false)         2.29 ms/iter   1.96 ms  █                   
                                  (1.69 ms … 5.24 ms)   4.75 ms ▅█▂▁▂▁▁▁▁▁▁▁▁▁▁▂▂▂▂▁▁
update: mutativeCompat (freeze: false)   2.54 ms/iter   2.23 ms ▂█                   
                                  (2.07 ms … 4.80 ms)   4.66 ms ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▂▂▁
update: vanilla (freeze: true)          58.28 µs/iter  81.92 µs ▅                █   
                               (34.13 µs … 448.71 µs)  90.67 µs ██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▃▁▁
update: immer9 (freeze: true)            4.71 ms/iter   4.94 ms  █▂                  
                                  (4.45 ms … 5.54 ms)   5.46 ms ▄██▄▂▂▂▃▂▂▂▄▄▂▂▂▁▁▁▂▁
update: immer10 (freeze: true)           4.56 ms/iter   4.67 ms  █                   
                                  (4.39 ms … 6.04 ms)   5.19 ms ▃█▂▂▁▁▁▂▃▂▂▁▂▁▁▁▁▁▁▁▁
update: mutative (freeze: true)          2.26 ms/iter   1.92 ms  █                   
                                  (1.73 ms … 4.77 ms)   4.57 ms ▅█▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▂▂▁
update: mutativeCompat (freeze: true)    3.68 ms/iter   4.45 ms  █                   
                                  (2.90 ms … 6.23 ms)   5.98 ms ▇█▅▂▁▁▁▁▁▁▁▁▁▁▁▂▂▄▂▂▂

summary
  update: vanilla (freeze: false)
   1.05x faster than update: vanilla (freeze: true)
   40.79x faster than update: mutative (freeze: true)
   41.37x faster than update: mutative (freeze: false)
   45.93x faster than update: mutativeCompat (freeze: false)
   66.53x faster than update: mutativeCompat (freeze: true)
   82.29x faster than update: immer10 (freeze: true)
   85.09x faster than update: immer9 (freeze: true)
   7880.91x faster than update: immer9 (freeze: false)
   8850.35x faster than update: immer10 (freeze: false)

----------------------------------------------------- -------------------------------
concat: vanilla (freeze: false)         19.82 µs/iter  20.17 µs █                  █ 
                                (19.16 µs … 21.21 µs)  20.21 µs ██▁▁▁█▁▁██▁▁▁▁██▁▁▁██
concat: immer9 (freeze: false)          23.13 µs/iter  23.26 µs                  █  █
                                (22.52 µs … 24.39 µs)  23.38 µs █▁▁██▁▁▁█▁█▁█▁▁▁██▁▁█
concat: immer10 (freeze: false)         22.05 µs/iter  22.34 µs    █                 
                                (21.40 µs … 23.08 µs)  22.67 µs ██▁█▁█▁█▁▁▁▁█▁███▁▁▁█
concat: mutative (freeze: false)        21.95 µs/iter  20.08 µs █                    
                               (18.67 µs … 316.79 µs) 106.88 µs █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
concat: mutativeCompat (freeze: false)  22.38 µs/iter  22.51 µs         ▃   █ ▃      
                                (21.40 µs … 23.28 µs)  22.93 µs ▆▁▁▁▁▁▁▁█▁▁▁█▆█▁▆▁▁▁▆
concat: vanilla (freeze: true)          20.34 µs/iter  20.45 µs         █    █      █
                                (19.51 µs … 22.20 µs)  20.80 µs █▁▁▁█▁███▁▁▁▁█▁█▁▁▁▁█
concat: immer9 (freeze: true)            2.78 ms/iter   2.80 ms   █  ▃▇▃             
                                  (2.65 ms … 3.18 ms)   3.10 ms ▂▄██████▃▁▁▁▂▁▂▁▁▁▂▂▂
concat: immer10 (freeze: true)           2.68 ms/iter   2.69 ms █                    
                                  (2.58 ms … 3.55 ms)   3.41 ms █▃▂▂▂▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁
concat: mutative (freeze: true)         21.07 µs/iter  20.95 µs ██▃                  
                                (20.73 µs … 22.14 µs)  21.78 µs ███▁▆▁▁▁▁▁▁▁▁▁▁▁▁▆▁▁▆
concat: mutativeCompat (freeze: true)  457.31 µs/iter 435.04 µs █                    
                              (429.63 µs … 894.87 µs) 656.63 µs █▂▁▁▁▁▁▁▂▂▁▁▁▁▁▁▁▂▁▁▁

summary
  concat: vanilla (freeze: false)
   1.03x faster than concat: vanilla (freeze: true)
   1.06x faster than concat: mutative (freeze: true)
   1.11x faster than concat: mutative (freeze: false)
   1.11x faster than concat: immer10 (freeze: false)
   1.13x faster than concat: mutativeCompat (freeze: false)
   1.17x faster than concat: immer9 (freeze: false)
   23.08x faster than concat: mutativeCompat (freeze: true)
   135.43x faster than concat: immer10 (freeze: true)
   140.05x faster than concat: immer9 (freeze: true)

@markerikson
Copy link
Owner

Huh, nice catch!

The Immer docs do advise at https://immerjs.github.io/immer/performance#for-expensive-search-operations-read-from-the-original-state-not-the-draft to minimize reads from the draft, but it is interesting that the array methods would get that much slower.

Could you update the PR to not remove the immer10Each copy?

@robstax
Copy link
Author

robstax commented Jan 11, 2025

sure! i put the immer10Each stuff back

@markerikson markerikson merged commit 8e9e337 into markerikson:main Jan 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants