Performance of Array.prototype.every(), Array.prototype.some()
I was writing a userscript a couple of weeks ago and had the opportunity to use Array.prototype.every()
/some()
for the first time. (I only needed one of them — I ended up going with some()
for semantics reasons.)
This wasn’t a performance-critical context, but nevertheless I started to wonder if every()
would immediately return false as soon as it found an element that didn’t meet the condition, and some()
would immediately return true as soon as it found an element that did meet the condition.
MDN states that they do, but I wanted to see some numbers. So I ran a handful of quick n’ dirty tests in the Firefox console to check it out. For the sake of organization, here’s the setup:
function bench(label, test, loops) {
loops = +loops || 10;
let t = [];
for (let i = 0; i < loops; ++i) {
let tt = performance.now();
test();
t.push(performance.now() - tt);
}
let avg = Math.round(t.reduce((a, b) => a + b) / loops);
console.log(`${label}: ${avg}ms avg of ${loops}`);
return t.map(Math.round);
}
const a = [1, 1, 1, 1, 1, 1, 1, 1], b = [0, 0, 0, 0, 0, 0, 0, 0];
const isEqualToOne = n => n === 1;
Here are the tests:
bench('every() is true', () => {
for (let i = 0; i < 10_000_000; ++i)
a.every(isEqualToOne);
});
bench('every() is false', () => {
for (let i = 0; i < 10_000_000; ++i)
b.every(isEqualToOne);
});
bench('some() is true', () => {
for (let i = 0; i < 10_000_000; ++i)
a.some(isEqualToOne);
});
bench('some() is false', () => {
for (let i = 0; i < 10_000_000; ++i)
b.some(isEqualToOne);
});
Here are the relevant computer specs:
- Windows 10, version 1909 (build 18363.720)
- AMD Ryzen 7 2700X (8 cores, 16 threads, tests running at 4.0 GHz via Precision Boost 2)
- 16 GB DDR4-3200 RAM
And here are the results in Firefox 74.0:
every() is true: 1501ms avg of 10 every() is false: 1382ms avg of 10 some() is true: 1339ms avg of 10 some() is false: 1459ms avg of 10
It looks like there is indeed a difference, albeit a small one. Specifically,
Array.prototype.every()
returns false 7.9% faster when the first of 8 elements doesn’t meet its condition, than it returns true for all elements; andArray.prototype.some()
returns true 8.2% faster than it returns false, in similar circumstances.
I wanted to see how the results compared in other browsers, so I attempted to run these tests in both the new and legacy versions of Microsoft Edge, as well as Chrome. In doing so, I discovered that V8 and Chakra appear to cache function calls after the first couple of iterations, causing subsequent iterations to clock far shorter times and invalidating the output of bench()
.
Frustratingly, there does not appear to be a way to disable this behavior, so I had to significantly decrease the iteration count using the loops
parameter I just happened to have had the foresight to include. I also had to decrease the number of calls to every()
and some()
in Edge Legacy and increase it in the new Edge and in Chrome to get reasonable times.
Here are the results:
- Edge Legacy 44.18362.449.0:
every() is true: 2451ms avg of 3 every() is false: 1897ms avg of 3 some() is true: 1811ms avg of 3 some() is false: 2488ms avg of 3
- Edge 80.0.361.69:
every() is true: 467ms avg of 2 every() is false: 153ms avg of 2 some() is true: 154ms avg of 2 some() is false: 424ms avg of 2
- Chrome 80.0.3987.149:
every() is true: 465ms avg of 2 every() is false: 153ms avg of 2 some() is true: 156ms avg of 2 some() is false: 428ms avg of 2
(Yes, I really did have to limit it to just 2 iterations in V8 before it started caching.)
The difference appears significantly larger in Chakra at 22.6%/27.2% faster, and is just ridiculous in V8 at a whopping 67%/64% faster. It’s not immediately clear to me why when I compare the source implementations, so perhaps someone more familiar with the code might be able to shed some light? It doesn’t help that the SpiderMonkey and ChakraCore implementations are literally just JavaScript, so it’s got to be something way under the hood. But here are the links anyway:
- SpiderMonkey: https://github.com/mozilla/gecko-dev/blob/master/js/src/builtin/Array.js#L84
- ChakraCore: https://github.com/microsoft/ChakraCore/blob/master/lib/Runtime/Library/JsBuiltIn/JsBuiltIn.js#L591
- V8: https://github.com/v8/v8/blob/master/src/builtins/array-every.tq and https://github.com/v8/v8/blob/master/src/builtins/array-some.tq
All told, since I was just writing a userscript for Firefox, which is my daily driver, for personal use, I’m not too worried. But I did learn a thing or two about Array.prototype.every()
and Array.prototype.some()
, more than I bargained for, and I thought it might interest other JS performance junkies, which is why I’ve blogged about it. Hope you learned from it, too.
Add a comment
Things to keep in mind:
* denotes a required field.
Please keep comments civil and on-topic.
Markdown is supported; examples include
**bold**
for bold,*italics*
for italics, and[hyperlinks](http://example.com)
for hyperlinks. Learn more.You may choose to have your browser remember your name, email and website for the next time you comment. Your email address will not be shared either way. See the privacy policy.