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:

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,

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:

(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:

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: