A performance test of the JavaScript Array.some function in Chrome browser

Previously, I performance-tested the Array.some function in Node.js version 8.16. Node.js is usually used for running microservices as a backend on a server. It’s not identical to JavaScript running client-side, in the browser. So it’s also interesting to run the same kind of performance test in a browser to see if the results apply there, too.

The source code for this test is similar to what it was in my previous test using Node.js. There are some necessary differences. I wanted to use the same word list, but I didn’t want to fiddle with trying to get the browser to read from a file, which is complex at best and impossible at worst (for security reasons).

Instead, I decided to use XMLHttpRequest to grab the word list I’d used previously, which was handily available on a web page.

However, you can’t just open Chrome browser and run the code below. If you try it (and you might want to), you’ll see this error message:

Access to XMLHttpRequest at 'https://norvig.com/ngrams/count_1w100k.txt' from origin 'chrome-search://local-ntp' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Oops! The solution is to first surf to Norvig’s page, then open the JavaScript console with Ctrl-Shift-J, paste in the code below, and call runTest().

/* Displays results */
var finishAndPrint = function(words) {
    console.log("words length: " + words.length);
    console.log("First word: " + words[0]);
    console.log("Last word: " + words[words.length-1]);
};

/* This test function is guaranteed to never match a word in the Norvig list of words */
var testFunction = function(item) {
    return item === "gsdjkoeu";
};

/* A performance test using the Array.some method: */
var performanceTestSome = function(words) {
    let t0 = performance.now();
    let result = words.some(function(el, i) {
        return testFunction(el);
    });
    let t1 = performance.now();
    console.log("The some method took " + (t1 - t0) + " milliseconds and the result was " + result + ".");
};

/* A performance test using a simple for loop: */
var performanceTestForLoop = function(words) {
    let len = words.length;
    var result = false;
    let t0 = performance.now();
    for (var i = 0; i < len; i++) {
        if (testFunction(words[i])) {
            // This code is necessary so the function does the same thing as the "some" method
            result = true;
            break;
        }
    }
    let t1 = performance.now();
    console.log("The for loop method took " + (t1 - t0) + " milliseconds and the result was " + result + ".");
};

/* Read words from a URL and return the list of words */
var readTheWordsFromURL = function() {
    let myURL = "https://norvig.com/ngrams/count_1w100k.txt";
    var xmlHttp = null;
    xmlHttp = new XMLHttpRequest();
    xmlHttp.open( "GET", myURL, false );
    xmlHttp.send( null );
    return xmlHttp.responseText;
};

var runTest = function() {
    var data = readTheWordsFromURL();
    var words = [];
    var lines = data.split('\n');
    lines.forEach(function(row, i) {
        var items = row.split('\t');
        if (items[0].length > 0) {
            words.push(items[0]);
        }
    });
    finishAndPrint(words);
    performanceTestForLoop(words);
    performanceTestForLoop(words);
    performanceTestSome(words);
    performanceTestSome(words);
};

runTest();

I did this in Chrome browser Version 75.0.3770.90 (Official Build) (64-bit), on my Ubuntu 16.04 desktop. I used private mode to make sure no other tabs, plugins, etc. would interfere with the test.

These were the results the first time I ran the code:

words length: 100000
VM18:4 First word: THE
VM18:5 Last word: PGY
VM18:36 The for loop method took 2.8300000121816993 milliseconds and the result was false.
VM18:36 The for loop method took 0.5550000059884042 milliseconds and the result was false.
VM18:20 The some method took 3.064999997150153 milliseconds and the result was false.
VM18:20 The some method took 2.6000000070780516 milliseconds and the result was false.

I repeated the test (closed the browser, reopened it, and ran the test), but this time changed the order of the tests. I ran the some test first:

...
VM18:20 The some method took 3.5149999894201756 milliseconds and the result was false.
VM18:20 The some method took 2.5599999935366213 milliseconds and the result was false.
VM18:36 The for loop method took 2.599999977974221 milliseconds and the result was false.
VM18:36 The for loop method took 0.670000008540228 milliseconds and the result was false.

Huh! I tried it again:

...
VM18:20 The some method took 3.5499999939929694 milliseconds and the result was false.
VM18:20 The some method took 2.634999982547015 milliseconds and the result was false.
VM18:36 The for loop method took 2.449999999953434 milliseconds and the result was false.
VM18:36 The for loop method took 0.5299999902490526 milliseconds and the result was false.

And again:

...
VM18:36 The for loop method took 2.9750000103376806 milliseconds and the result was false.
VM18:36 The for loop method took 0.5549999768845737 milliseconds and the result was false.
VM18:20 The some method took 3.939999995054677 milliseconds and the result was false.
VM18:20 The some method took 2.789999998640269 milliseconds and the result was false.

I ran another test case, just running the some method only. The result took 3.429999982472509 milliseconds. A test with just the for loop running once took 2.905000001192093 milliseconds.

The only conclusion I can draw is that Chrome doesn’t quite perform as well when running some instead of a simple for loop. The difference is not huge, and there’s some variation. This test runs over 100K words, and it still only takes a few milliseconds either way.

The results are similar to those in my previous post, which described a similar performance test using Node.js. This comes as no surprise, because Node.js runs on V8.

On the first run, it looks like a for loop performs a bit better than the some method. There’s some optimization on the second run. This is all déjà vu, if you read my last post.

Is it worth using a for loop everywhere to take advantage of that optimization? I think you’d really want to go deeper in your performance tests to understand the optimization, and make sure that it will be used before deciding to go with for loops everywhere. You will also want to be sure that it’s worth sacrificing code maintainability and readability in pursuit of some possibly minuscule performance boost.

It’s important to be aware of the potential for any performance impacts of special Array methods when architecting a new project or looking for bottlenecks in an existing one. But deciding never to use Array iterators just because you heard a vague rumor that they were not performant is unwise.

If you found this interesting, click the subscribe button below! I write a new post about once a week.

Leave a Reply

Your email address will not be published. Required fields are marked *