Adobe, MAKE SOME NOISE

Loops!

October 3rd, 2008

I've been thinking about AS3 optimization lately so I dusted off the old benchmark code and started speed testing some things. I meant to put together a post filled with exciting optimizations and tricks, but I got bogged down almost immediately in loop code.

When I started getting into it, I was somewhat surprised by how many ways there are to write a basic for loop. Combined with the different number types, that grows into a huge amount of variation in the way people can write code, so it follows that some methods must be better than others. I broke it down into four different structures:

  1. Undefined variable, count up to ten.
  2. Variable set to 10, negative increment to zero.
  3. Variable set to 0, increment to ten.
  4. Variable set to 10, decrement to zero.
From there I also tested the three number types (Number, int, uint), as well as an untyped variable. And in the interest of full disclosure, I ran these loops exactly as they appear in a test harness set to iterate one million times. I compiled each test separately instead of running them in sequence just to make sure there was no weirdness from the garbage collector, and I ran each test at least three times (averaging them roughly in my head). The longest test clocked 660ms, the shortest was around 5. Here's the nitty-gritty:

These results are a little odd…let's look at what's going on. First of all, the obvious one: int is fast, uint is slow, and untyped is downright painful. So int it is, now on to technique.

The first and third test appear to be the same code, but there's a pretty large discrepancy between them at runtime. In fact the first test always wins by a visible margin. The difference between the first technique and the other three, is that the first test doesn't need an extra trip to memory to manually assign value to the newly instanced variable.

You may also be wondering about the negative increment in the second test – this is actually two tricks in one (and for a long time, I believed this to be the fastest execution). Even using a negative value, the addition operator takes about ¾ the time of the decrement (j--) operator. On top of that, looping backwards from a high value to zero, means you don't need to run a comparison, you can just cast the variable directly to a boolean.

All the rest of the code should be pretty pedestrian. There is one little snag in the untyped results but I suspect that's coming from weirdness in the JIT when it has to continually recast those values, and it's worth noting that uint really doesn't like the increment/decrement operators. And lastly, if you're wondering, I didn't test while loops here because both while and for loops are compiled into the same code – as far as the player is concerned, there is no difference, so use whichever you prefer.

UPDATE: Ok this is borked. In the middle of the night it came to me (in a vision?) – since the variable is never defined in the first test, maybe the JIT is just jumping the rails and never going through that loop at all? That would certainly explain the speed increase (better than my feeble explanation earlier).
 
So testing should have been an easy iterator variable - testing a loop that counts to 10, over 1 million iterations should trace out 10 million. Somehow the undefined loop code is so extremely broken that it's actually leaking out into the scope above! When I tested I got back 10...implying that the test code actually broke my benchmark harness. I'm still trying to get to the bottom of this but in the meantime, just go with method #2.

4 Responses to “Loops!”

  1. Nathan Ostgard Says:

    I’d be curious to see how a while loop for the same thing would hold up:

    var j:int = 11;
    while (–j) {}

  2. Jackson Dunstan Says:

    Very interesting results. I did an article on inlining Math.ceil() just yesterday. I didn’t use a mod like you did, but instead compared:

    int(value) == value

    To check if value is a whole number. I’m seeing speedups of over 6x.

  3. Brandon Says:

    I’ve seen an increase in speed in my for loops by doing the following:

    increment before the i not after (e.g. ++i faster than i++)

    go backwards:

    for ( var i:int = 10 – 1; i >= 0; –i )

    is faster than

    for ( var i:int = 0; i < 10; ++i )

  4. Valentin Radu Says:

    Mmm….how about:

    var i:int=0;
    for (i; i<10; i++)

    for what I know (actual never test it) this is the fastest way.

Leave a Reply