We all know that code optimization is important, especially when processing large quantities of data. It is also common knowledge that adding layers of code to loops reduces performance. Except this maxim doesn’t always hold when it comes to JavaScript in browsers and even native methods can vary wildly in performance. In fact, in almost all cases, Underscore, jQuery and YUI outperformed any of the native methods. Interestingly, the newest implementation for native looping, forEach, consistently trumped a traditional for (var i = 0; i < 2500; i++) loop.
I set up a testing framework that looped through a normal array instantiated like so:
var data = [];
var v = {}; //This value doesn't seem to affect performance at the data size I chose
for (var i = 0; i < loops; i++) { //loops is defined elsewhere
data.push(v);
}
I looped through this array using the following methods:
- for loop
- for … in … loop
- forEach loop (not supported in IE9 Quirks Mode)
- Prototype 1.7: Enumerable.each
- jQuery 1.7.1: $.each
- Underscore 1.2.3: _.each
- YUI 3.1.4: YUI.each
Each loop was passed the value of the array element and returned immediately, like so:
//From the native for and for ... in ... tests
(function(el){return;})(data[i]);
I eventually settled upon an array of 2500 elements and running the test 200 times to collect a reasonable number of samples. These numbers were not chosen arbitrarily. Prototype would time out on Firefox at 5000 array elements and Safari seemed to have wildly varying results on each run of the suite with fewer than 200 samples. With 200 samples, the variance in mean time between runs became a few hundredths of a millisecond, not enough to be of consequence. Disturbingly, while Prototype froze the browser with 5000 array elements and 100 samples, it had no issue with 2500 elements and 200 samples. Even so, with some tests averaging less than a hundredth of a millisecond, we will soon require much larger data sets to get meaningful results at all, which may preclude the inclusion of Prototype (and even native methods!) in future tests.
All tests were run in a freshly launched browser on an updated operating system (either MacOS 10.7.2 or Windows 7 Home Premium) running on a MacBook Pro with a 2.53GHz Intel Core 2 Duo processor and 4GB of 1067MHz DDR3 RAM. Firefox had all add-ons disabled and Internet Explorer had the Developer Tools open (to switch between Standards and Quirks mode).
So let’s look at some charts!

This was certainly an interesting start to the process. YUI, Underscore and jQuery all clocked in at 0.005 ms. Prototype would prove to be faster than native methods, with the exception of forEach, on all browsers. Underscore was only be outperformed on IE 9 (32-bit, Standards Mode).

Chrome was exceptional as well, with the traditional for loop actually running slower than for ... in .... This behavior was also seen on Chrome for Windows and Safari for Windows.
Since all of the major browsers are clearly pre-optimized for these libraries (as they themselves wrap the native looping language structures), let’s look at just the libraries:

Here we can begin to see that Prototype really is behind its peers in loop performance. While we are talking fractions of a millisecond, our apps are growing larger by the day and this could become a serious issue.

Hey! It looks like Firefox really is slow. Curiously, IE 9 is significantly faster, which flies in the face of most generalized JavaScript performance metrics.
Still, something looks odd about IE:

Your mileage varies wildly depending on whether you choose standards or quirks mode, and not always in obvious ways. In general, 64-bit is faster, but only marginally, and most people are not using it. (You have to explicitly launch it, as it is not the default, even on 64-bit Windows). Meanwhile, quirks mode apparently embeds some performance hacks. As a note to benchmark developers, this should be evidence enough that not all IE’s are the same and we need to start breaking down by rendering mode and compilation.
So far, we’ve seen the average times and most browsers are quite acceptable. What are the worst case scenarios?
|
Platform
|
Browser
|
Method
|
Max (ms)
|
Mean (ms)
|
|
Windows 7
|
IE 9.0.8112.16421 (64-bit IE 9 Quirks)
|
for
|
93
|
5.29
|
|
Windows 7
|
IE 9.0.8112.16421 (32-bit IE 9 Standards)
|
forEach
|
81
|
4.04
|
|
Windows 7
|
IE 9.0.8112.16421 (64-bit IE 9 Quirks)
|
for in
|
81
|
6.27
|
|
Windows 7
|
IE 9.0.8112.16421 (32-bit IE 9 Standards)
|
for in
|
76
|
9.87
|
|
Windows 7
|
IE 9.0.8112.16421 (64-bit IE 9 Standards)
|
for in
|
71
|
4.89
|
|
Windows 7
|
IE 9.0.8112.16421 (32-bit IE 9 Quirks)
|
for in
|
67
|
5.995
|
|
MacOS 10.7.2
|
Firefox 9.0.1
|
for in
|
55
|
1.885
|
|
Windows 7
|
Firefox 9.01
|
for in
|
52
|
6.055
|
|
Windows 7
|
IE 9.0.8112.16421 (32-bit IE 9 Quirks)
|
for
|
49
|
4.66
|
|
Windows 7
|
IE 9.0.8112.16421 (64-bit IE 9 Standards)
|
for
|
48
|
4.76
|
|
Windows 7
|
IE 9.0.8112.16421 (32-bit IE 9 Standards)
|
for
|
39
|
7.845
|
|
Windows 7
|
Chrome 16.0.912.75 m
|
for in
|
30
|
1.13
|
|
Windows 7
|
Chrome 16.0.912.75 m
|
for
|
26
|
1.315
|
|
MacOS 10.7.2
|
Chrome 16.0.912.75
|
for
|
23
|
1.175
|
|
MacOS 10.7.2
|
Chrome 16.0.912.75
|
for in
|
22
|
1.085
|
|
MacOS 10.7.2
|
Safari 5.1.2
|
for in
|
16
|
1
|
|
Windows 7
|
Firefox 9.01
|
jQuery
|
6
|
0.185
|
|
MacOS 10.7.2
|
Safari 5.1.2
|
for
|
5
|
0.52
|
|
MacOS 10.7.2
|
Firefox 9.0.1
|
for
|
5
|
0.7
|
Those averages hid some extreme variations within Internet Explorer. Fortunately, most of these are found using native language structures, which we know better than to use.