Brenden
Brenden

Reputation: 329

Why is for-of loop faster than standard for loop for small arrays and slower for larger arrays?

In JavaScript, I noticed that the ES6 for ... of loop has a much different performance than the traditional for (start; stop; step) loop.

Benchmark

const n = 10000;
const arr = Array(n).fill().map((e, i) => i); // [0, n)

console.log('n =', n);

let sum1 = 0;
console.time('for let i');
for (let i = 0; i < arr.length; i++) {
  sum1 += arr[i];
}
console.timeEnd('for let i');

let sum2 = 0;
console.time('for of');
for (let v of arr) {
  sum2 += v;
}
console.timeEnd('for of');

Results

n = 10
for let i: 0.350ms
for of: 0.015ms
-----
n = 100
for let i: 0.354ms
for of: 0.023ms
-----
n = 1000
for let i: 0.429ms
for of: 0.111ms
-----
n = 10000
for let i: 1.048ms
for of: 2.138ms
-----
n = 100000
for let i: 9.452ms
for of: 13.644ms

(Tested using Node.js v10.11.0)

As you can see, as n increases, the speed of the for-of loop decreases at a faster rate than the standard for loop. Why is the for-of loop faster for smaller arrays and slower for larger ones?

Upvotes: 16

Views: 1517

Answers (4)

Whatatimetobealive
Whatatimetobealive

Reputation: 1353

Let me try to explain, in this case for is faster than for...of because operation is much simpler, detailed explanation is can be found here:

for(var VariableDeclarationList; Expression; Expression) Statement

for (let i = 0; i < arr.length; i++) {
  sum1 += arr[i];
}

So what happens is here:

  • Check i < arr.lenght
  • i plus one

But things are a bit different for...of

for(var ForBinding in Expression) Statement

let sum2 = 0;
console.time('for of');
for (let v of arr) {
  sum2 += v;
}

As I know, for..of use object-specific iterator and loops over the values generated by that so this will take a bit more time than for so things are little interesting here. If you want to learn more about this please check difference on runtime semantics:

for runtime semantics: here

for...of runtime semantics: here

This case is only valid user defined Symbol.iterators, but the built-in iterators should be optimized to work better in a for...of instead of a for "thanks for @Aryter"

So why your test is not accurate? Because your generating for...of after for loop so V8 engine is doing some optimization there.

My assumption of that optimization is related with JavaScript properties and make significant speed difference V8 engine when object is small which more detailed can be found here V8 Engine fast properties

Hope is more clear now...

Upvotes: 0

Kostiantyn
Kostiantyn

Reputation: 1906

I'd suggest taking a look into microbenchmarking concept, and also getting familiar with this great answer for microbenchmarking in JS.

In short, when testing something small like for loops for timings, you can easily get your test logic interfered with other ongoing processes under the hood. For example, if you swap the order of execution in your test case so that for of is executed before for let, you may notice surprising timings change for smaller n values (spoiler: in this case for let wins the race for n=10 and n=100)

So, the answer to your why question: for let is slower in your listing, because it is closer to the program start and executes on more 'cold' vm, also warming it up for the consequent for let statement. The bigger the n is, the lesser this side effect contributes to the measured execution time.

That's why microbenchmarks imply execution of the series of identical tests instead of just a single one - to make small side effects not that significant on the higher scale

Additionally, note that canonical unit of measurement of performance of statements is "total operations per unit of time", instead of absolute time per single set of operations

Example microbenchmark for your topic can be found here

Upvotes: 5

Compiler v2
Compiler v2

Reputation: 3605

The for-of loops only over the data (like the cells of an array), not the objects themselves. Increasing performance greatly.

The other loops loop over objects. The for-of loop makes the internal code simpler.

Instead of accessing the memory addresses (a hex number) of the array, which takes longer for larger arrays of n size. Those types of loops are more suitable for smaller quantities.

The for-of loop iterates over the numerical data and NOT the objects, which is much more efficient at looping through a ton of elements. You apply the necessary loop type to the specific situation of your code.

It's like a horse and a car. The horse is good for a journey less than 100 miles and not a 100+ mile one. When a car can do that better.


Refer to this link for more info. - https://hacks.mozilla.org/2015/04/es6-in-depth-iterators-and-the-for-of-loop/

Upvotes: 3

Russell
Russell

Reputation: 17719

When benchmarking smaller values, the overhead operations can have a bigger impact on the test.

For example, if variable initialization and memory allocation takes 0.1 ms, this is negligible with n > 1000, however significant with n = 10.

In this case, the for/of operator allows the V8 engine to optimize the loop operation (reducing the overheads like above). For example it may pre-load the array items onto the stack or similar.

The for/let operation will process each item independent of the whole array, and is more explicit in variable usage (reducing the amount of optimization the engine can do).

Upvotes: 8

Related Questions