JavaScript's for of loops are actually fast

Published on

When it comes to iterating over arrays, for...of loops are believed to be significantly slower than traditional indexed loops. I decided to test this since JS engines are constantly improving.

The benchmark

I decided to do the benchmarks with jsbenchmark.com on Chrome 143, Windows 11 and AMD Ryzen 5000U. All the benchmark tests are run sequentially.

The core idea of the benchmark

The idea is to create 5 types of arrays: integers, floats, strings, objects and mixed values.

All the loops call a dummy function DATA.doSomethingWithValue() for each array element to make sure V8 doesn't optimize out something too much.

The setup

The setup will generate 5 types of arrays: random integers, random floats, random strings, objects and mixed values. It will also create a dummy function doSomethingWithValue (mentioned above).

		const N = 5000; // Can be 50000 and 500000 as well
const MAX_VALUE = 1000;

const integers = Array.from({ length: N }, () => Math.floor(Math.random() * MAX_VALUE));
const floats = Array.from({ length: N }, () => Math.random() * MAX_VALUE);
const strings = Array.from({ length: N }, () => Math.random().toString().replace(".", ""));
const objects = Array.from({ length: N }, () => ({
	int: Math.floor(Math.random() * MAX_VALUE),
	float: Math.random() * MAX_VALUE,
	string: Math.random().toString().replace(".", ""),
}));
const mixedValues = Array.from({ length: N }, () => {
	switch (Math.floor(Math.random() * 4)) {
		case 0: return Math.floor(Math.random() * MAX_VALUE);
		case 1: return Math.random() * MAX_VALUE;
		case 2: return Math.random().toString().replace(".", "");
		case 3: return {
			int: Math.floor(Math.random() * MAX_VALUE),
			float: Math.random() * MAX_VALUE,
			string: Math.random().toString().replace(".", ""),
		};
	}
});


return {
	integers,
	floats,
	strings,
	objects,
	mixedValues,
	doSomethingWithValue() {
		// A dummy function to make sure V8 doesn't optimize out something too much
	},
}
	

N is the size of the arrays and can be 5000, 50000, 500000.

The results

Benchmark results for N = 5000
Benchmark results for N = 5000

The best 2 are classic i++ cached length and for of (a bit surprising). Almost the same performance. Classic i++ and forEach are slightly behind. Looks like the reverse traversal in classic i-- reversed is doing more harm than good, probably has something to do with the CPU cache or V8 engine. The worst is for in, which is not that surprising as it also does an additional access from the array.

Benchmark results for N = 50000
Benchmark results for N = 50000

Almost the same pattern here, except forEach is becoming proportionally slower, closer to classic i-- reversed. for in is becoming relatively worse than other loops.

Benchmark results for N = 500000
Benchmark results for N = 500000

This one is a bit interesting. For of is now less optimal for 500000 items (especially for floats). This is probably because for of is harder to optimize, and since it was long running and too few runs were performed, V8 didn't optimize it. The same with forEach.

I decided to run a separate benchmark for classic i++ cached length, for of and forEach, by repeating the loops 200 times, in order to warm the code up:

Benchmark results for N = 500000 - 200 repeats
Benchmark results for N = 500000 - 200 repeats

Now, for of is closer to classic i++ cached length. Still slower, the bad results during the warmup phase might also drag the average down. Overall, the warmup helps a lot. However, forEach doesn't get any significant benefit from this.

Conclusion

In the recent years V8 has improved significantly in optimizing loops, including for of loops. If your code is performance sensitive, for of can be potentially as fast as classic loops. But as we see, for huge arrays the optimization is trickier and less reliable. The most consistent winner is classic i++ cached array length, since JS engines are good at recognizing and optimizing such loops. For non performance sensitive code for of still usually provides better ergonomics, so usually it should be preferred.

Happy New Year!





Read previous



UP
This site uses cookies. By continuing to use this website, you agree to their use. To find out more, including how to control cookies, see here: Privacy & cookies.