Dmitry Reutov
Dmitry Reutov

Reputation: 3032

How to explain unexepctable perfomance in js-array loop?

Lets say we have an array of 200 000 elements for example... Now we want to iterate it in different ways and check the fastest one. I've heard that if we will save array.length in variable before loop we will reduce execution time, so i tried the code below

let sum = 0
for (let i = 0; i < arr.length; ++i) sum += arr[i]

against

let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) sum += arr[i]

But i got the same result as if in both cases js reads length value just once in the very beginning.

Then i decided to check, what if during loop we will change an array, removing last element.

let sum = 0
for (let i = 0; i < arr.length; ++i) {
  sum += arr[i]
  if (i === 100) arr.pop()
}

against

let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) {
  sum += arr[i]
  if (i === 100) arr.pop()
}

So i expected that second case now should work faster because in first case js inevitably should check array.length each time and i was much suprised that it is not works faster but even slower - from 10 to 15 %. For me it is unexplainable. Any ideas?

Tests: https://jsbench.me/tfkefwjuw2

Upvotes: 0

Views: 46

Answers (1)

Bergi
Bergi

Reputation: 664297

The problem is that

let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) {
  sum += arr[i]
  if (i === 100) arr.pop()
}

is now incorrect, as it loops beyond the end of the array. The sum will be NaN in the end. The correct solution would have l = arr.length - 1 so that this doesn't happen.

Now why does it becomes so slow? Because array accesses in javascript are only fast (get compiled to a fast path with pointer addressing) when the element exists. When you miss, the code will get de-optimised. Since JSbench runs the same code multiple times, so even if the deoptimisation happens only at the last of 200000 iterations, the subsequent runs will be much slower.

See this talk for details, which even explicitly spells out "Avoid out-of-bounds reads" on one slide. In general, don't use non-idiomatic code to improve performance. i < arr.length is idiomatic, and JS engines will optimise it well.

Upvotes: 1

Related Questions