Optimization. Validate my findings.

I usually code like this to begin with:

[as]for(var i:int = 0; i < whatever; i++) { var n:Number = someCalculation goes here; somethingElse = n * whatever; }[/as] Then when I decide to optimize, and I'm looking to squeeze that one last millisecond out of my code, I start doing stuff like this: [as]var i:int; var n:Number; for(i = 0; i < whatever; i++) { n = someCalculation goes here; somethingElse = n * whatever; }[/as] The thinking being that you declare the local vars up front, rather than in the loop. This wasn't one of those things I ever really thought about in much depth, just one of those fixed ideas you get in your head and you operate on until you actually step back and think about it for two seconds. Today, I thought about it for two seconds and realized that for the i at least, this was pretty silly. It still only gets declared once, even if you declare it in the loop like the first example. Duh. Then I started thinking about the n. Sure, it's declared in the loop, so theoretically, that declaration line gets run every time through the loop. But since AS3 does not have block level local vars, it's still scoped to the function, and probably only actually gets created once. Doing some simple speed tests confirmed that there really is no difference between the two. Now I need to go back and refactor a bunch of "highly optimized" code so I don't look like an ass. 🙂 But, I'd really feel even better about it if one of you people who read AS3 byte code over lunch could step in and validate my findings.

This entry was posted in Flash. Bookmark the permalink.

15 Responses to Optimization. Validate my findings.

  1. kathryn says:

    fascinating… despite using it every day, i didn’t realize AS3 doesn’t have block-level scoping. is there a reason it was designed that way?

  2. 5566 says:

    When Flash compile for loops, it converts it into a while loop. I found out this by decompiling some swf files. It’s something like this.

    for(var i:int=0;i0){
    doIt();
    }

  3. 5566 says:

    doh! Something wrong… my comment didn’t come out correctly. the while loop looks something like this.

    var _local1:int=100;
    while(_local1–>0){
    doIt();
    }

  4. 5566 says:

    still wrong… It’s the “minus minus > 0 ” causing the problem.

  5. kp says:

    5566, that’s cool. i get the idea. and yeah, i knew for loops get compiled to whiles, so that makes sense.

  6. There are obviously subtle cases, but in general the ‘initization’ of a storage variable is a compile time step only. All it has to do is decide what local (to the function) register to use. It doesn’t have to do anything special with that register, as it is just used for that object. Of course if you are creating an object there, like x = new Point(0,0), then that will run each time (there could be side affects) and you may want it outside — but that is wrt the right side rather than the left.

    When optimized compilers (not flash though) decide on register allocation they generally look at all assignments as separate variables, even if ‘n’ is reused, and then optimize from there based on a graph. Usually (not flash) they can pick up redundant things like:

    var z = 5;
    for(var i = 0; i < 100; i++)
    {
    var n = 5 * z;
    }

    …and just get rid of them this way. I think though that is more useful with sample code than real code, like a lot of compiler optimizations ; ).

    At the end of the day I never optimize like that with Java or C# etc, but with Flash I think you still have to take out the blatant stuff.

    (woah, after testing, the flash ide (optimizations on) will even leave in things like x = 5 * 4; rather than convert to x = 20; Not sure about Flex. That is pretty lame, I prefer to leave some things like that just for clarity. Maybe my sample code wasn’t pure as3 enough…)

  7. Danny Miller says:

    To Robin:
    I totally agree about the 5*4 example. Someone should develop a compiler extension (or at least a “pre-compiler”) that will convert these things.

    For example, what about a Flex Builder extension which will convert the source code into an optimized source code then compile it. Sort of like #define with C.

    Anyway, to Keith, basically, it all depends on the type of variable you create. If you just create a Flash primitive like an int or String then you won’t notice a performance difference. I’m not 100% familiar with how Flash does the byte code, but I would assume the var keyword initializes the variable into memory IF it hasn’t been stored already. If it has it just references it. At least that is my impression on how it would work, but I’m not sure.

    AS3’s scope has always bothered me. How annoying is it that these variables in a for loop last so long…

    // Start
    for (var j:int = 0 ; j < 5; j++)
    {
    }
    trace(j); // I don’t like how j shows as 5 instead of null or a compiler error
    for (var j:int = 11; j < 14; j++) // I hate how this will throw a warning/error of “j has been redeclared”
    {
    }

    I really hope they change that with AS4 and ECMAScript4

  8. Stas says:

    Why do you use int, maybe it’s better uint in this case?

  9. var n:Number = someCalculation goes here;
    i declare n usually outside the loop if i know that it is written at the beginning of a block.

    but I leave the other declaration inside the for.
    # for(var i:int = 0; i <whatever; i++)

  10. Mike says:

    There was method to improve performance in AS2 – create reference to functions:

    var sin:Function = Math.sin;
    var s:Number = sin(i); //works faster

    var s:Number = Math.sin(i); //works slower

    It doesn’t work in AS3 anymore. References work few times slower.

  11. freddy says:

    to Danny MIller:

    AS3’s scope has always bothered me. <— works the same on AS2, keep reading…
    // Start
    for (var j:int = 0 ; j < 5; j++){
    }
    trace(j); // I don’t like how j shows as 5 instead of null or a compiler error

    Why it should throw an error or null?, the loop runs and j reach 5 because of j++ part of the loop (try the trace inside the loop)

    Now, if you declare j inside a function it remains inside the function scope and doing a trace outside the function shows undefined, since AS1 age

    for (var j:int = 0 ; j < 5; j++){
    }
    trace(j);
    for (var j:int = 11; j < 14; j++) // I hate how this will throw a warning/error of “j has been redeclared”

    Perfectly normal, as you redeclared j on the same scope: var j:int, if you are going to reuse j on the same scope take out the “var” and “:int” part or just use another variable, can’t get easier than that

    to Keith:

    I declare i in the for loop statement

    for (var i:int = 0; etc

    and the rest of the variables outside the loop, unless I’m creating new objects inside the loop. in this case I would expect things to be a little slower.

  12. Erik Olson says:

    If you use uint instead of int you’ll get a bit more speed.

  13. Tim Kindberg says:

    The real optimization, I would think, would come from optimizing any calls within the loop on arrays:

    for(var i:int = 0; i < myArray.length; i++){}

    TO

    var loopTotal:int = myArray.length;
    for(var i:int = 0; i < loopTotal; i++){}

  14. Badger says:

    If the for loop is in a function that is called more than once, then it IS more efficient to declare the variables before hand outside up top.

    Perhaps you should account for both scenarios (for loop within initialisation section of code and for loop within a function that is called more than once).

Leave a Reply