I’ve recently been seeing more and more usage of while loops by those who I presume are interested in performance. I’ve always assumed that these was not faster than for loops, but today I am finding out.
I was taught in school that for loops are syntax sugar one small step beyond while loops. That is, these are equivalent:
for (a; b; c) { d; } a; while (b) { d; c; }
I learned that while studying C and C++ though and it was thoroughly supported when I learned my first assembly language and loops were indeed not native operations of the CPU. :) But these days I’m dealing with AS3, AS2, and JavaScript and things are much murkier with a VM in the way. So I devised a test to see for myself if for loops are indeed still just syntax sugar:
package { import flash.display.*; import flash.text.*; import flash.utils.*; /** * An app to test the speed of for loops versus while loops * @author Jackson Dunstan */ public class ForVsWhile extends Sprite { public function ForVsWhile() { var logger:TextField = new TextField(); logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); function log(msg:*): void { logger.appendText(msg + "\n"); } var i:int; const NUM_ITERATIONS:int = 1000000000; var beforeTime:int; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) {} log("For (forward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); for (i = NUM_ITERATIONS; i--; ) {} log("For (backward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); i = 0; while (i < NUM_ITERATIONS) {++i;} log("While (forward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); i = NUM_ITERATIONS; while (i--) {} log("While (backward): " + (getTimer()-beforeTime)); } } }
And the results are:
| Environment | For (forward) | For (backward) | While (forward) | While (backward) |
|---|---|---|---|---|
| 3.0 Ghz Intel Core 2 Duo, 4 GB RAM, Windows XP | 2218 | 2365 | 2217 | 2365 |
Then I made a test for JavaScript:
/** * An app to test the speed of for loops versus while loops * @author Jackson Dunstan */ function log(msg) { document.write(msg + "<br/>"); } function getTimer() { return (new Date()).getTime(); } var i; var NUM_ITERATIONS = 1000000; var beforeTime; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) {} log("For (forward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); for (i = NUM_ITERATIONS; i--; ) {} log("For (backward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); i = 0; while (i < NUM_ITERATIONS) {++i;} log("While (forward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); i = NUM_ITERATIONS; while (i--) {} log("While (backward): " + (getTimer()-beforeTime));
Note the lower number of iterations due to low browser timeout lengths. The results are:
| Environment | For (forward) | For (backward) | While (forward) | While (backward) |
|---|---|---|---|---|
| 3.0 Ghz Intel Core 2 Duo, 4 GB RAM, Windows XP, Firefox 3.5 | 3 | 2 | 3 | 2 |
| 3.0 Ghz Intel Core 2 Duo, 4 GB RAM, Windows XP, IE 8 | 234 | 94 | 234 | 94 |
| 3.0 Ghz Intel Core 2 Duo, 4 GB RAM, Windows XP, Chrome 3 | 8 | 6 | 8 | 6 |
| 3.0 Ghz Intel Core 2 Duo, 4 GB RAM, Windows XP, Safari 4 | 3 | 2 | 3 | 2 |
Then I made a test for AS2:
/** * An app to test the speed of for loops versus while loops * @author Jackson Dunstan */ class ForVsWhile { function ForVsWhile() { _root.createTextField("log", 0, 0, 0, 640, 480); var log:Function = function(msg:String): Void { _root.log.text += msg + "\n"; } var i:Number; var NUM_ITERATIONS:Number = 10000000; var beforeTime:Number; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) {} log("For (forward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); for (i = NUM_ITERATIONS; i--; ) {} log("For (backward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); i = 0; while (i < NUM_ITERATIONS) {++i;} log("While (forward): " + (getTimer()-beforeTime)); beforeTime = getTimer(); i = NUM_ITERATIONS; while (i--) {} log("While (backward): " + (getTimer()-beforeTime)); } static function main(mc:MovieClip): Void { var app:ForVsWhile = new ForVsWhile(); } }
Again note the lower number of iterations due to the slowness of AS2 and the old VM. The results are:
| Environment | For (forward) | For (backward) | While (forward) | While (backward) |
|---|---|---|---|---|
| 3.0 Ghz Intel Core 2 Duo, 4 GB RAM, Windows XP | 1845 | 1832 | 1797 | 1832 |
As you can see from the results tables, there is usually no difference between for or while loops. The lone exception to this is AS2 where forward-iterating while loops actually outperform their for equivalents. I don’t know why this is, but given AS2’s diminishing usefulness in Flash, I don’t mind so much.
It gets more interesting when you consider both forward iteration and backward iteration. Forward iteration is faster in AS3 by a small margin, slower in JavaScript by a factor of more than two for all browsers tested, and mixed by a small margin in AS2 due to the strange forward iteration performance. Unfortunately, this harms code portability in the performance arena. When porting between these languages you may need to revisit any performance-critical areas of code and, if possible, change the directionality of their loops. Even worse, these inconsistent test results make it difficult to remember which direction to loop in various languages for maximum performance. I suppose now you at least have this page as a reference. :)
Enjoy your Thanksgiving and Black Friday festivities!
#1 by Tommy Kjær Andersen on November 27th, 2009 · | Quote
if i remember rigth the for loops are converted to while loops by the compiler, so the results souldt be the same.
#2 by jackson on November 27th, 2009 · | Quote
That’s the meaning of syntax sugar. Repeated testing (above) shows that this is, quite unfortunately, not the case.
#3 by Robert Penner on November 30th, 2009 · | Quote
A few points:
1. Some people say there is a speed difference between pre-increment and post-increment.
2. When I move the “forward while” code to the top, it runs slower than the “forward for” (AS3, Flash CS4). It often seems that code running first has a disadvantage.
3. I decompiled the loops; the for and while loops come back exactly the same (AS2 and AS3).
#4 by jackson on November 30th, 2009 · | Quote
Thanks for the points!
1. Yes, I think they’re wrong. :) If you look at the bytecodes available in AVM2 you’ll see instructions like “increment_i”, “inclocal_i”, “add_i” and so forth. Adobe puts out a good overview. There’s no mention of “pre” and “post”; that is only an order of bytecode generation by the compiler based on the precedence rules of AS3. In the end it is the same bytecode. Still, that’s something I could have been clearer about in the article.
2. My tests didn’t show any difference between “forward while” and “forward for” for AS3. Do you see any difference when you don’t swap the order around? How about when you use Flex instead of CS4? I’m not seeing any difference when I swap any of the tests around.
3. This is good to know! I wonder what accounts for the mysterious slowness “forward for” on AS2…