Periodically, I ran into articles on JS code optimization (
one of the most popular ones ), and I caught myself thinking that there was too little information in them. Listed 2-3 designs, 1-2 browsers and everything on it.
As they say, if you want to do something well, do it yourself.
I decided to test, first of all for myself, the speed of work of various language constructs (starting with the most basic ones) in the main modern browsers and on the basis of this to draw conclusions about what and how to use in scripts that are demanding of performance.
Well, since the results are obtained, why not put them for general use?
')
Upd: added graphs of the results provided by
deerua (for those who perceive the visual presentation of information better than the table)
Introduction
So, the testing machine is P4 3GHz (dual core), 1.5RAM, Vista 32-bit.
Browser set - FF3, Opera 9.62, Chrom 1.0, IE7, IE8b2
I have to say that IE8 is not original, but launched through the
IETester program.
I do not know how much it corresponds to the original one, but at least the results differ quite strongly from IE7, and in different directions.
If someone wants to test IE6 or any other browser (actually, welcome)
Testing Methodology
All of the language constructs described below were tested by repeating 1 million times in a loop. This action was repeated several times in each browser in order to reveal a certain average result (it usually ranges from + -2-5% depending on the current system load). The average result (in milliseconds) was recorded in the table.
Everywhere (where nothing else is clearly indicated), the inverted for loop was used (var i = 1000000; i--;) as the fastest.
Getting down to business
Cycles
Tests / Browsers | FF3 | Opera | Chrome | IE7 | IE8b2 |
Cycles | | | | | |
Classic cycle | 71 | 100 | sixteen | 238 | 218 |
Inverted cycle | 34 | 34 | 7 | 70 | 118 |
while loop | thirty | 33 | 7 | 70 | 118 |

So, for starters, I decided to test the cycles themselves (just empty loops with no executable code inside). Because say the basis of our testing)
About cycles has already been written quite a lot, so that the results are quite predictable.
As you can see, the inverted for loop (var i = 1000000; i--;) is at least twice as fast as the classical for (var i = 0; i <1000000; i ++), so it was used in further testing.
It can also be noted that while the cycle practically does not differ in speed from the inverted for, apparently because it is actually the same construction (according to the logic of operation).
Work with arrays and objects
Tests / Browsers | FF3 | Opera | Chrome | IE7 | IE8b2 |
Search arrays and objects | | | | | |
Getting the values ​​of a large array (1M) by index (full search in the reverse order) | 430 | 170 | 18 | 790 | 1020 |
Getting the value of a small array (100) by index | 124 | 146 | 18 | 428 | 515 |
For-in object loop (1M) | 2020 | 2160 | 385 | 39400 | 35400 |
Brute force (1M) through inverted loop | 390 | 170 | 18 | 745 | 746 |
| | | | | |
Filling arrays and objects | | | | | |
Filling (1M) array through array.length | 190 | 485 | 82 | 2640 | 865 |
Filling (1M) an array in direct order through the cycle step value (classic cycle) | 200 | 432 | 75 | 2500 | 760 |
Filling (1M) an array in reverse order through the cycle step value (inverted cycle) | 1180 | 310 | 124 | 2270 | 2260 |
Filling (1M) array via push () | 176 | 1120 | 98 | 4450 | 1186 |
| | | | | |
Filling an object (1M) in direct order through the value of the cycle step (classic cycle) | 1080 | 368 | 74 | 2400 | 2205 |
This part is dedicated to working with arrays and substitute objects. Under the replacement object in this test is meant an object (of type Object), which is used exclusively as a replacement for an array (that is, it does not contain any properties except for elements like an array). I was interested in the speed of such substitutes.
Array (or object) 1M - meaning array (or its substitute) with the number of elements equal to 1,000,000, and of indices (keys), respectively, from zero to a million (more precisely, 999,999).
In the first test, we completely iterate over such an array in the inverted cycle, i.e. something like this:
var a = 0;
for (var i = 1000000; i--;) {a = big_array [i];}
In the second test, we also do a million iterations of the loop, but at each step we get the same value from a small array (100 elements in size). I did this test to check how much the size of the array affects the speed of getting values ​​from it.
As the test showed, in all browsers except Chrome, the search time for a value in an array can increase significantly with an increase in the number of its elements.
In the third test, a complete for-in loop is done on the array substitute object.
It is clearly seen that the for-in loop is very slow in all browsers (compared to inverted for), and VERY slow in IE for large amounts of data. Never use it to iterate through arrays or objects where you can get by with the for loop (i.e., numeric and ordered keys).
The fourth test is a complete analog of the first test, except that big_array is not an array but an object.
It is noticeable that the speed of iteration through the loop weakly depends on whether we have an array or a substitute object. There is only a tangible difference in IE8, but this is still a beta, so things can change.

It is also worth noting that if we are faced with the task of choosing a range of array elements, then using the array.slice () method in any browser is several times faster than simply iterating over the array in a loop with sampling the necessary elements according to the condition. The difference is so obvious that I did not even include it in the test.
In addition to sorting arrays, you often have to fill them in, so the next 5 tests are just about that.
In principle, everything should be clear from the table:
- Filling an array using the push method is significantly slower in all browsers except FF, compared to filling via array [array.length] = value. (The developers of firestone apparently were the only ones who understood that this is a complete disgrace when a bulky construction is used instead of the native method.)
- In some browsers, it is important whether you fill the array in direct or reverse order.
- Filling a replacement object is much slower in FF and IE8, apparently because the operation of filling the array is specially optimized, but filling the object is not (which is understandable, because the object should not be used instead of an array to store large amounts of uniform data).

Functions, Objects, Variables
Functions, Objects, Variables | | | | | |
Tests / Browsers | FF3 | Opera | Chrome | IE7 | IE8b2 |
Calling an empty function with passing it the current loop value | 129 | 270 | 17 | 3100 | 860 |
| | | | | |
Creating an object (creating 2 methods and one property through a constructor) | 2460 | 1900 | 593 | 18600 | 11,700 |
Creating an object (creating 2 methods from the prototype and one property through the constructor) | 1260 | 636 | 64 | 7830 | 4210 |
| | | | | |
Getting the property of an object (own property) | 84 | 142 | sixteen | 406 | 412 |
Getting object property (prototype property) | 90 | 147 | 29 | 474 | 474 |
Getting object properties (getter method of private var properties) | 260 | 354 | 33 | 3430 | 1160 |
| | | | | |
Calling an incremental method of an object (increases its own property through this) | 326 | 460 | 60 | 3810 | 1520 |
Calling an incremental method of an object (increases its own property by explicitly specifying an object) | 356 | 520 | 65 | 3985 | 1633 |
Calling an incremental method of an object (increases the private var property) | 412 | 370 | 38 | 3530 | 1320 |

First of all, I tested calling an empty function in a loop.
var f = function () {}
for (var i = 1000000; i--;) {f ();}
In most browsers, calling a function is a fairly cheap operation (there are only a few hundred milliseconds added per million calls, compared to a completely empty cycle).
However, IE, as always, presents unpleasant surprises. Fans of wrapping code in a lot of wrapper functions will have something to think about)
I note that the slow function call itself has a negative effect on other tests related to function calls (for example, creating an object or calling a method), so the time to execute them in IE increases even more.
The following 2 tests are devoted to the creation of a million objects, in one case all the methods are created through the constructor, in the other through the prototype.
The difference, I think, is obvious.
The following three tests call the property of an object: its own property (created through this.prop = value), the property of the prototype, and a private property (created through the closure from the constructor function). Obviously, the last option is obtained through the getter.
The result is, in general, predictable - the own property of the object can be obtained much faster.
Then follow 3 tests that call the incremental method of the object (i.e., the method that, by each call, increases the property of the object by one). Actually, the difference here again is how exactly this property was created (i.e., the access speed to the object properties from its methods is tested).
It is clearly seen that in this case the rate of change of the private property is higher everywhere except for Firefox, however, it must be remembered that this property is common to all objects of the same type and has the worst reading time outside (via a getter).
Branches
Branches | | | | | |
Tests / Browsers | FF3 | Opera | Chrome | IE7 | IE8b2 |
Select a branch of 8 possible through if | 800 | 500 | 60 | 1500 | 1460 |
The choice of a branch from 8 possible through switch | 315 | 334 | 54 | 868 | 1039 |
Selecting branches from 8 possible through hash functions | 620 | 400 | 86 | 4520 | 1820 |
| | | | | |

I think the first two tests in this table are not necessary to comment, we’ll dwell on the third one in more detail.
A hash of functions is an object that emulates switch behavior.
For example, we have this piece of code:
switch (a) {
case 0: b = 6 + 2; break;
case 1: b = 8 * 3; break;
}
Then the hash of functions will look like this:
hash = {
'0': function () {return 6 + 2;},
'1': function () {return 8 * 3;}
}
And used as b = hash [a] ();
With such simple actions, the hash shows worse results than switch (due to the need to call a function). However, if you're still going to call functions from switch, the hash will probably be faster.
General browser output
Again, everything is predictable. Chrome confidently leads with a margin of several Parsecs, firelight and opera share the second and third place with each other (in different tests in different ways), the donkey sadly weaves at the end, but the beta donkey gives some hope that the patient is more alive than is dead
Conclusion
The first thing I would like to draw attention to is that all the above tests are synthetic, and the load is much higher than usual for web applications (not so often we have to deal with arrays of a million values, for example).
Consequently, it is worth treating them exactly as synthetic tests with a specially increased load - i.e. Be aware of what is important for this particular script, and what is not important.
On the other hand, the method that is being called a hundred times today may be called ten thousand times tomorrow, so it’s not worth waving a hand on this thing. And if something started to slow down somewhere, there would be at least an understanding of which way to “dig”)