immerjs / immer

Create the next immutable state by mutating the current one
https://immerjs.github.io/immer/
MIT License
27.5k stars 850 forks source link

Array update speed #1117

Open pistacchio opened 4 months ago

pistacchio commented 4 months ago

🐛 Bug Report

I thought that since immer updates arrays directly, it would be more or less as performant as updating the array directly, but this is not at all the case.

I simulated the pattern "remove the nth element of an array". Code pasted later. The results on my machine are:

filter: 186.852ms
immer: 15.871s
splice: 50.453ms

immer seems to be 84 slower than the standard way to remove the nth element of an array in a mutable way (which is looping throught all the elements to remove the given index). This, itself, is not an efficient method, yet in my example is only ~4 times slower then directly using Array.splice()

Link to repro

import {produce} from "immer"

const arr = Array.from({length: 10_000_000}, (i, idx) => ({n: idx}));

console.time('filter');
let remove10_000nth = arr.filter(((i, idx) => idx !== 10_000));
console.timeEnd('filter');

console.time('immer');
remove10_000nth = produce(arr, draft => {
  draft.splice(10_000, 1)
})
console.timeEnd('immer');

console.time('splice');
arr.splice(10_000, 1);
console.timeEnd('splice');

Environment

mweststrate commented 4 months ago

Note that Immer also deeply freezes the output, so that is probably a large portion of your cost here. Might want to play with disabling that, or freezing arr and the elements inside it first.

On Wed, Apr 17, 2024 at 10:23 AM pistacchio @.***> wrote:

🐛 Bug Report

I thought that since immer updates arrays directly, it would be more or less as performant as updating the array directly, but this is not at all the case.

I simulated the pattern "remove the nth element of an array". Code pasted later. The results on my machine are:

filter: 186.852ms immer: 15.871s splice: 50.453ms

immer seems to be 84 slower than the standard way to remove the nth element of an array in a mutable way (which is looping throught all the elements to remove the given index). This, itself, is not an efficient method, yet in my example is only ~4 times slower then directly using Array.splice() Link to repro

import {produce} from "immer" const arr = Array.from({length: 10_000_000}, (i, idx) => ({n: idx})); console.time('filter');let remove10_000nth = arr.filter(((i, idx) => idx !== 10_000));console.timeEnd('filter'); console.time('immer');remove10_000nth = produce(arr, draft => { draft.splice(10_000, 1)})console.timeEnd('immer'); console.time('splice');arr.splice(10_000, 1);console.timeEnd('splice');

Environment

  • Immer version: 10.0.4
  • I filed this report against the latest version of Immer
  • Occurs with setUseProxies(true)
  • Occurs with setUseProxies(false) (ES5 only)

— Reply to this email directly, view it on GitHub https://github.com/immerjs/immer/issues/1117, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAN4NBDAET3ZSVAKMIFCNEDY5YWRZAVCNFSM6AAAAABGK2G46WVHI2DSMVQWIX3LMV43ASLTON2WKOZSGI2DONRYHAZDEMY . You are receiving this because you are subscribed to this thread.Message ID: @.***>