JavaScript Array.push Performance

I was recently reading Google's JavaScript Style Guide when I came across the following claim:

"Note that since assigning values to an array is faster than using push() you should use assignment where possible."

I had always used push to assign a value to the end of an array, so thought I'd investigate this a little bit further to determine the performance of each of the methods. There are 3 distinct ways to do this:

  1. Array.push(element) - Defined on the native Array object, this is the method challenged by Google.

  2. array[i] = element - Assigning an element to a specific index of the array. The requirement of this method is that you must have a pointer to the last location. Other than that it's a simple assignment.

  3. array[array.length] = element - Similar to the previous, but this involves a lookup for situations where you don't have access to a pointer.

  4. I also defined a fourth function to test. I put a function on the Array prototype called mypush, which carried out step 3 above. It was defined as such:

      Array.prototype.mypush = function(element) {
        this[this.length] = element;
      };
    

This short article will document my testing of these different methods on several browsers.

The Tests

I put together a small HTML page to execute the different methods of adding an element to the end of an array alongside some method of timing how long each took.

The tests consisted of adding 400,000 elements to an empty Array using the different methods described above. I performed the test 10 times for each method, and took the average. For example, here's a look at the test for Array.push:

  function testPush() {
    var result = 0;
    var a = [];

    for (var i = 0; i<10; i++) { // run the test 10 times
      a = []; // we start with an empty array each time
      var start = new Date().getTime(); // get the start time of the test

      for (var j = 0; j<400000; j++) {
        a.push(i);
      }

      var end = new Date().getTime();
      result += end - start;  // and the result is the end time minus the start time
    }

    alert('Result for Array.push is ' + (result / 10)); // take the average
  }

I then repeated the same logic for the other methods.

The Results

The tests yielded the following results in the following browsers:

Google Chrome 6.0.472.63 Mozilla Firefox 3.6.10 Apple Safari 5.0 (7533.16) Internet Explorer 8 Internet Explorer 7 Opera 10.62
Array.push 0.4 ms 5 ms 2 ms 21.7 ms 66.7 ms 2.7 ms
array[i] 1 ms 0.8 ms 0.9 ms 4.6 ms 29.4 ms 0.7 ms
array[array.length] 1.2 ms 0.9 ms 0.9 ms 10.9 ms 32.6 ms 1 ms
Array.mypush 1.2 ms 7.1 ms 1.2 ms 31 ms 86.8 ms 1.2 ms

Conclusion

The results speak for themselves: using an index outperforms using Push in every browser with the exception of Google's own. If cross-compatibility is a big concern for you, the utilitarian approach would be to use an index wherever possible.

To look at the situation a little deeper, we could consider steps the Closure Compiler takes to handle these situations. If we run the following code in the Compiler:

var a = [];

function push(i) {
  a.push(i);
}

for(var i = 0; i<10; i++) {
  a.push(i);
}

We observe the following output:

  for(var a=[],b=0;b<10;b++)a.push(b);

Showing that the Compiler doesn't do anything about push statements in pre-compiled code. However, with an improved performance in most browsers, you might expect it to.

However, it wouldn't be as simple as converting all pushes to index assignments as there is a subtle difference between the two; Array.push returns the length of the array after the element has been added (something you don't get with array[array.length]). Converting all Array.push statements would cause semantic problems if the user has assigned that statement to a variable. For example:

var a = [];
var b = [];

var i = a.push('test'); // i is 1

var j = b[b.length] = 'test'; // j is 'test'

However, we could examine the case where the result of Array.push is not assigned to anything. In this scenario, the Compiler should be able to replace the push with an array.length index without side effects.

The problem then lies in the fact that the performance varies between browser. Although, on the whole, indexing performs better than push, that is not the case in Google Chrome. Unlike GWT, where you can deploy a certain condition for a certain browser, Closure Compiler just generates one JavaScript file for every browser.

Given the differences between Chrome's results, and the extremely poor performance in some other browsers, it may be worth sacrificing some performance in Chrome for much better performance in other browsers.

blog comments powered by Disqus