Subscribe on changes!

Relatively poor performance when prepending to a large array

avatar
Dec 10th 2021

Version

3.2.24

Reproduction link

codesandbox.io

Steps to reproduce

  • Click on the "add 50" button

What is expected?

The action should be reflected immediately

What is actually happening?

The action's result is delayed, and we can observe "Jank - event processing delay" when capturing the performance with Firefox


A similar problem can be observed with splice if the insertion point is close to the beginning of the array. But if we replace unshift with push, there does not seem to be any performance penalty; this seems to point towards something that is run for every existing element in the array.

This problem was not existing in the latest vue2, cf: https://codesandbox.io/s/mystifying-monad-8n0h4?file=/src/App.vue

Might be related to: https://github.com/vuejs/vue-next/issues/4318

avatar
Dec 10th 2021

If that helps, here is a performance capture of the action described above:

Screenshot_20211210_165732

avatar
Dec 10th 2021

Let's keep one issue with https://github.com/vuejs/vue-next/issues/4318

In your case, you can use a few tricks like Object.freeze() or markRaw() to improve performance. There are a few other tricks in the docs too

avatar
Dec 10th 2021

Just as a preliminary explanation:

  • calling push() on an array with 2000 items results in 1 set operation.
  • calling unshift() on an array with 2000 items results in 2001 set operations, as all array items have to be reassigned to index+1.

The latter is still very fast with plain arrays as the Browser JS engine likely has a highly optimized codepath for this. But when a reactivity proxy is involved, that optimization is lost.

avatar
Dec 12th 2021

note that calling unshift in a loop, invokes reactivity overhead per each call. the lost of optimization mentioned is actually the overhead of the reactivity system implementation.

Adding 50 items to a 2000 items array will iterate the full list 50 times performing different check to maintain the reactivity system ( ~2000 x 50 )

a possible user land optimization is for you to batch the data to a single prepend call.

SFC playground- slow prepend on large list , get faster with batching (10x faster)

can vue batch all prepend mutation calls within the "tick" and invoke logic as if it was an atomic operation, that I do not know.

avatar
Dec 13th 2021

If you do not intend to mutate the items in the list (e.g. they are server-fetched data), another way to improve performance with large lists is going immutable - mark the list with markRaw to prevent Vue from making it reactive, and trigger list updates by re-assigning the list: example