What's the Fastest Way to Code a Loop in JavaScript?
I built a loop benchmarking test suite for different ways of coding loops in JavaScript. There are a few of these out there already, but I didn't find any that acknowledged the difference between native arrays and HTML collections. Since the underlying implementations are different (HTML collections for example lack the pop() and slice() methods, etc), benchmarks that don't test against both are probably missing important information.
My suspicions were confirmed. Accessing the length property is more expensive on HTML collections than on arrays, depending on the browser. In those cases, caching it made a huge difference. However, HTML collections are live, so a cached value may fail if the underlying DOM is modified during looping. On the other hand, HTML collections will never be sparse, so the best way to loop an HTML collection might just be to ignore the length property altogether and combine the test with the item lookup, since you have to do that anyway:
// looping a dom html collection
for (var i=0, node; node = hColl[i++];) {
// do something with node
}
Another interesting result is that with HTML collections, hColl.item(i) is 2-6x slower than hColl[i], except in Safari where it's about the same. I wonder what the extra overhead is?
I've posted the results of all my benchmarks here, however my IE7 is really IE8 in IE7 emulation mode, so I mistrust these results as representing IE7's original JavaScript/DOM engine. If anybody wants to run the benchmarks in true IE7 and post the results, I'll update this post accordingly. Thanks to Kyle Simpson for the benchmarks. Also, I'm sure there are ways of looping that escaped my imagination, so if you know of any, post it in the comments and I'll add them to the test page.
[edit] To prevent overly-helpful "this script is taking too long" warnings from skewing the results, in IE you might have to edit your MaxScriptStatements registry setting. In Gecko it's dom.max_script_run_time under about:config.
Firefox 3.0.1 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 4ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 3ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 4ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 3ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 2ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 9ms |
| for ... in loop | for (var i in arr) {} | 28ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 93ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 3ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 6ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 4ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 5ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 51ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 51ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 81ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 35ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 79ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 32ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 22ms |
| for ... in loop | for (var i in sarr) {} | 30ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 96ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 178ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 769ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 119ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 3ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 119ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 3ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 2ms |
| for ... in loop | for (var i in hColl) {} | 97ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 163ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 114ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 189ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 95ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 235ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 98ms |
Firefox 2.0 / WinXP / 1.95GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 47ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 16ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 31ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 16ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 15ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 281ms |
| for ... in loop | for (var i in arr) {} | 375ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 906ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 32ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 62ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 31ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 47ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 391ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 422ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 453ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 203ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 469ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 218ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 172ms |
| for ... in loop | for (var i in sarr) {} | 328ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 875ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 750ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 6000ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 891ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 16ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 906ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 16ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 15ms |
| for ... in loop | for (var i in hColl) {} | 625ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 1188ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 453ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 797ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 437ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 1438ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 437ms |
Camino 1.6.1 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 8ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 4ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 9ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 4ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 2ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 85ms |
| for ... in loop | for (var i in arr) {} | 44ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 163ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 10ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 14ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 10ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 11ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 111ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 105ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 103ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 45ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 99ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 42ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 33ms |
| for ... in loop | for (var i in sarr) {} | 52ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 151ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 217ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 1338ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 270ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 4ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 270ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 4ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 3ms |
| for ... in loop | for (var i in hColl) {} | 106ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 224ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 119ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 220ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 115ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 397ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 114ms |
Mozilla 1.7.12 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 40ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 14ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 22ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 13ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 9ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 388ms |
| for ... in loop | for (var i in arr) {} | 263ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 1126ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 27ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 48ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 26ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 32ms |
| Array.forEach() custom implementation. | arr.forEach(function(x){}); | 666ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 148ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 264ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 157ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 255ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 180ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 103ms |
| for ... in loop | for (var i in sarr) {} | 236ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 1027ms |
| Array.forEach() custom implementation. | sarr.forEach(function(x){}); | 1076ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 1881ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 1888ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 18ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 1855ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 17ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 24ms |
| for ... in loop | for (var i in hColl) {} | 578ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 1455ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 1182ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 2072ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 1102ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 2542ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 1115ms |
Safari 3.1.2 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 5ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 3ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 6ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 4ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 2ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 18ms |
| for ... in loop | for (var i in arr) {} | 42ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 80ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 5ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 8ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 5ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 7ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 11ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 19ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 54ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 31ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 71ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 45ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 19ms |
| for ... in loop | for (var i in sarr) {} | 41ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 77ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 285ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 482ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 8ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 3ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 8ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 4ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 2ms |
| for ... in loop | for (var i in hColl) {} | 47ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 86ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 58ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 107ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 59ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 53ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 60ms |
Opera 9.51 / OS X 10.5 / 2x3GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 7ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 7ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 7ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 7ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 3ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 28ms |
| for ... in loop | for (var i in arr) {} | 58ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 299ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 9ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 18ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 9ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 13ms |
| Array.forEach() native implementation. | arr.forEach(function(x){}); | 54ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 27ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 94ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 76ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 92ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 74ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 42ms |
| for ... in loop | for (var i in sarr) {} | 63ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 286ms |
| Array.forEach() native implementation. | sarr.forEach(function(x){}); | 230ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 442ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 28ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 5ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 28ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 6ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 4ms |
| for ... in loop | for (var i in hColl) {} | 113ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 380ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 36ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 60ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 37ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 175ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 41ms |
IE 7.0.5730.11 / WinXP SP2
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 31ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 15ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 32ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 15ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 0ms |
| do ... while loop in reverse. | var i = arr.length-1; do { } while (i--); | 16ms |
| for loop in reverse. | for (var i=arr.length; i--;) { } | 0ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 2907ms |
| for ... in loop | for (var i in arr) {} | 140ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 438ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 31ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 63ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 46ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 47ms |
| Array.forEach() custom implementation. | arr.forEach(function(x){}); | 719ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 406ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 344ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 140ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 328ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 157ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 78ms |
| do ... while loop in reverse. | var i = sarr.length-1; do { } while (i--); | 94ms |
| for loop in reverse. | for (var i=sarr.length; i--;) { } | 93ms |
| for ... in loop | for (var i in sarr) {} | 110ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 359ms |
| Array.forEach() custom implementation. | sarr.forEach(function(x){}); | 2453ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 10922ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 2172ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 15ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 2125ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 16ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 15ms |
| do ... while loop in reverse. | var i = hColl.length-1; do { } while (i--); | 0ms |
| for loop in reverse. | for (var i=hColl.length; i--;) { } | 16ms |
| for ... in loop | for (var i in hColl) {} | 172ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 484ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 1219ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 2797ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 1406ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 3203ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 1453ms |
IE 8 Beta 1 / WinXP / 1.95GHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 46ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 32ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 47ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 31ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 15ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 188ms |
| for ... in loop | for (var i in arr) {} | 203ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 734ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 47ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 62ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 47ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 47ms |
| Array.forEach() custom implementation. | arr.forEach(function(x){}); | 610ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 203ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 657ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 281ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 656ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 266ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 156ms |
| for ... in loop | for (var i in sarr) {} | 156ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 641ms |
| Array.forEach() custom implementation. | sarr.forEach(function(x){}); | 4656ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 3781ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 3110ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 31ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 3109ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 16ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 31ms |
| for ... in loop | for (var i in hColl) {} | 281ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 938ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 453ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 875ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 453ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 3906ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 484ms |
IE 6.0 (JScript 5.6) / WinXP / 750MHZ
| Basic for loop. | for (var i=0; i<arr.length; i++) {} | 121ms |
| For loop, but caching the length. | for (var i=0, len=arr.length; i<len; i++) {} | 60ms |
| While loop that imitates a for loop. | var i = 0; while (i<arr.length) { i++; } | 110ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=arr.length; while (i<len) { i++; } | 60ms |
| While loop in reverse, simplifying the test condition. | var i = arr.length; while (i--) {} | 50ms |
| While looping by popping values (this fails on sparse arrays). | var x; while (x = arr.pop()) {} | 16694ms |
| for ... in loop | for (var i in arr) {} | 781ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} } | 2013ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; arr[i]; i++) {} | 141ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; arr[i]; i++) { var x = arr[i]; } | 250ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = arr[i++];) {} | 140ms |
| For reference. | for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; } | 170ms |
| Array.forEach() custom implementation. | arr.forEach(function(x){}); | 2945ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); } | 1331ms |
| Basic for loop. | for (var i=0; i<sarr.length; i++) {} | 1241ms |
| For loop, but caching the length. | for (var i=0, len=sarr.length; i<len; i++) {} | 671ms |
| While loop that imitates a for loop. | var i = 0; while (i<sarr.length) { i++; } | 1242ms |
| While loop that imitates a for loop, caching the length. | var i = 0, len = sarr.length; while (i<len) { i++; } | 671ms |
| While loop in reverse, simplifying the test condition. | var i=sarr.length; while (i--) {} | 471ms |
| for ... in loop | for (var i in sarr) {} | 611ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} } | 1762ms |
| Array.forEach() custom implementation. | sarr.forEach(function(x){}); | 11737ms |
| For reference against forEach(). | var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); } | 13359ms |
| Basic for loop. | for (var i=0; i<hColl.length; i++) {} | 6349ms |
| For loop, but caching the length. | for (var i=0, len=hColl.length; i<len; i++) {} | 71ms |
| While loop that imitates a for loop. | var i = 0; while (i<hColl.length) { i++; } | 6339ms |
| While loop that imitates a for loop, caching the length. | var i=0, len=hColl.length; while (i<len) { i++; } | 70ms |
| While loop in reverse, simplifying the test condition. | var i=hColl.length; while (i--) {} | 50ms |
| for ... in loop | for (var i in hColl) {} | 2984ms |
| for ... in loop, with integer test | var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} } | 5017ms |
| For loop, testing on existence rather than length (this fails on sparse arrays). | for (var i=0; hColl[i]; i++) {} | 4497ms |
| For loop, testing on existence rather than length, plus array lookup. | for (var i=0; hColl[i]; i++) { var x = hColl[i]; } | 8863ms |
| For loop, testing on existence rather than length, array lookup is combined with test. | for (var i=0, x; x = hColl[i++];) {} | 4516ms |
| For loop, testing on existence rather than length, array lookup is combined with test, item() instead of array brackets. | for (var i=0, x; x = hColl.item(i++);) {} | 10415ms |
| For reference. | for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; } | 4587ms |
var i = somelength; do something(); while( i-- );
Posted by matt bishop on July 25, 2008 at 07:49 AM MDT #
The end result being: reverse while loops are the fastest way to iterate a basic collection, as most of us who have optimized loops have found. ;)
Thanks for the amazingly comprehensive barrage of tests confirming it though. I think this holds true for other languages, such as Actionscript.
Posted by Michael Hoskins on July 25, 2008 at 08:06 AM MDT #
> var i = somelength; do something(); while( i-- );
var i = somelength-1; do something(); while( i-- );
Posted by Greg on July 25, 2008 at 10:51 AM MDT #
> The end result being: reverse while loops are the fastest way to iterate a basic collection, as most of us who have optimized loops have found. ;)
Yes, although not necessarily the best method in all cases.
Posted by Greg on July 25, 2008 at 11:05 AM MDT #
This was a really good article. I never thought about this because the sets I loop through are pretty small.
Posted by ernest leitch on July 25, 2008 at 11:11 AM MDT #
Added do ... while to benchmark suite, just for kicks.
Posted by Greg on July 25, 2008 at 11:58 AM MDT #
@ernest - Thanks. Yeah, making all loops into reverse while loops would only serve to obfuscate your code. I'd only use the technique to cool hot spots and/or iterate large sets.
Posted by Greg on July 25, 2008 at 12:08 PM MDT #
Why is it
// looping a dom html collection
for (var i=0, node; node = hColl[i++];) {
// do something with node
}
Instead of
for(var i=0; node; node = hColl[i++]) ?
Posted by Silenius on July 25, 2008 at 10:08 PM MDT #
Node wouldn't exist for the first test so the loop would fail.
Posted by Greg on July 25, 2008 at 10:24 PM MDT #
Thanks for putting so much work into this, it was illuminating. Occasionally I don't cache my variables, I had no idea what a significant impact it made.
Posted by Jack on July 26, 2008 at 03:36 AM MDT #
I think that the reason that the while loop is faster than a for loop is because you're effectively doing comparison against zero, which is faster than other comparisons. I think you can get the same performance from a for loop by using the following:
for(var i = arr.length - 1; i>=0; i--){}
Posted by Simon Hartley on July 28, 2008 at 03:03 AM MDT #
> sarr.forEach(function(x){});
Your test shows that this is definitly not a good solution.
While everyone wants to add closures everywhere, this shows that we have to be carreful and not use them on large collection...
Posted by Fluminis on July 28, 2008 at 06:54 AM MDT #
What about:
for(var i=arr.length; --i >= 0;){}
It does array length caching and comparison with zero...
Posted by Michael Lee on July 28, 2008 at 11:46 AM MDT #
> sarr.forEach(function(x){});
I think the performance hit probably comes from building a new scope object for the anonymous function for each item. Not sure if this is a true closure. There's also a lookup for each array item (x) that should be factored out, since most loops have to do it anyway.
Posted by Greg on July 28, 2008 at 11:47 AM MDT #
@Simon Hartley, @Michael Lee
Thanks. I added a reverse for loop to the benchmark. Except I left out the zero comparison since it's not strictly needed:
for (var i=arr.length; i--;) { }
Posted by Greg on July 28, 2008 at 12:04 PM MDT #
Here's the true IE7 (7.0.5730.11) WinXP SP2 benchmark results:
http://www.flensed.com/IE7-looping.html
**note: In IE7, it prompted me about 5 or 6 times if I wanted to stop a long running script. I clicked "no" as soon as I could for each alert (almost immediately each time), but I'm not really sure if that would sway results or not.
Posted by Kyle Simpson on July 28, 2008 at 12:04 PM MDT #
What about this for a HTML collection. I use it sometimes, but I never speed tested it.
var e, i = 0;
while (e = hColl[i++]) {
}
You have the for loop equivalent, but not the while.
Posted by Declan on July 28, 2008 at 01:19 PM MDT #
Sorry, I should have made a note of this. I had to edit a registry setting to prevent this from happening on IE. Those delays almost certainly blew out a few of the results. Google MaxScriptStatements for instructions.
Posted by Greg on July 28, 2008 at 01:19 PM MDT #
I don't think it's worth adding a while equivalent to every for loop in the benchmark. The existing ones already show the difference (which is negligible IMO).
Posted by Greg on July 28, 2008 at 01:37 PM MDT #
@Greg: Right you are. The zero comparison isn't necessary when working with array lengths, unless of course it's possible that the array's length was intentionally altered...
Looks like the reverse for loop with caching is about as fast as the reverse while loop and sometimes faster. Can you update the blog post to show this?
Posted by Michael Lee on July 28, 2008 at 01:39 PM MDT #
@Michael - I'm (apparently) approaching Roller's internal size limit for blog posts. I might do a summary post after comments trail off.
Posted by Greg on July 28, 2008 at 01:56 PM MDT #
@Greg... ok, I will re-run with that registry tweak in place and let you know when I update the link.
Posted by Kyle Simpson on July 28, 2008 at 02:59 PM MDT #
Were you using Firefox 3.0.1? (Firefox 3.1 is not released yet).
What version of JScript were you using with IE6 (it makes a huge difference)?
Posted by anon on July 28, 2008 at 03:06 PM MDT #
Whoops. Corrected. Firefox 3.0.1.
Posted by Greg on July 28, 2008 at 03:33 PM MDT #
The IE6 was JScript 5.6
Posted by Greg on July 28, 2008 at 03:52 PM MDT #
Posted by gOODiDEA on July 28, 2008 at 06:15 PM MDT #
Posted by gOODiDEA.NET on July 28, 2008 at 06:16 PM MDT #
@Greg:
Ok, the link has been updated with a pure run of the benchmark tests on IE7 (7.0.5730.11) on WinXP SP2.
http://www.flensed.com/IE7-looping.html
Posted by Kyle Simpson on July 28, 2008 at 07:16 PM MDT #
Hi, interesting post.
I made a benchmarker some time ago, I made a loop comparison (generic loops).
If you're interested, here's the link: http://benchmarker.flesler.com/
Choose the test called 'loops'.
Cheers
Posted by Ariel Flesler on July 28, 2008 at 08:41 PM MDT #
@Kyle - Thanks. Updated the tables.
Posted by Greg on July 28, 2008 at 09:25 PM MDT #
@Ariel - Interesting. I see you did some loop unrolling. Funny, they all performed virtually the same on FF3.
Posted by Greg on July 28, 2008 at 09:41 PM MDT #
For me too, but not in other browsers.
I also noticed that Firefox seems unreliable for benchmarks, even after turning firebug off (very important!).
The results always seemed to vary more than one would accept.
On the other hand IE, Opera and Safari seemed to be more or less stable.
Posted by Ariel Flesler on July 29, 2008 at 02:52 PM MDT #
Posted by 真见 on July 29, 2008 at 08:58 PM MDT #
Posted by 真见 on July 29, 2008 at 09:07 PM MDT #
In reference to the various comments about reverse counting, the reason checking against 0 is faster then <length, etc is that the ordinal operators <,<=,>, and >= are polymorphic (in the general case -- a sufficiently advanced interpreter may be able to infer types perhaps?) so these operators require type checks on both left and right sides of the operator to determine what comparison behaviour should be used.
In general you should also use the strict === rather than ==, as == will eventually fall back on toString conversion and string comparison.
Posted by Oliver on July 29, 2008 at 10:40 PM MDT #