12.13.2009

Closure on Closures, Terrible Typing, For-Each Failures

Here's an old reply I wrote to another blog's wonderings on performance problems in Flash functions, numeric types, and array access. I'm not in the mood to review, rewrite, nor make corrections (e.g. I'm sure I used "function closure" where I meant "activation object" and vice-versa, but not important), but here's the text of my replies (not entirely self-contained, but I recommend reading the original post either way):

From http://robert.zubek.net/blog/2008/01/12/actionscript-3-bytecode-performance/#comment-258
Anonymous functions/closures have tricky semantics in AS3, which is why you saw such painful numbers.

AS3 is not block-scoped, so all variable definitions are hoisted to the top-level of their scope — in this case, the function body of the surrounding function, not the anonymous function. This is why you cannot have two for loops with the same ‘var i:Number’ definition — you would get a ‘Duplicate name i’ error.

So, when you introduce an anonymous function, the cost of *every* variable (within the outer function down to the deepest variable definition) is increased considerably. You have to do a more expensive lookup. This is why the Flex SDK doesn’t use them, ever :)

Sometimes, you *need* a closure for an event listener… My solution is to create a simple factory function that takes the values you want to close over, and returns a Function object. This doesn’t get around the cost of creating a closure, and it adds the cost of the factory function call, but it isolates the lookup costs to the factory function.

In small functions, it may be more expensive to use this technique, but I never tested it.



As for for-each loops: The special form returns values from an array in unspecified order because the backing object is a hashtable (it sometimes begins life as a dense array, but often becomes a hashtable). Assuming that your for(;;) test was iterating over indexes from 0..len-1, each iteration does a hash lookup. We’d like lookup to be O(1) amortized, but there is considerable hidden overhead in doing the lookup.

The hash function is a quadratic probe, and we have a high load factor (~80%), so a large array has a high probability of collision. To calculate the address, integers indexes must be cast to Number (not terrible, but this is why the Flex SDK uses Number instead of Integer). If you index on a string or the cast fails, we stringify the index, intern it, and use that (!).

You *might* be able to tell if you have a dense array if the ‘for each’ loop returns items in ascending order, but that’s just a guess. You could try it with a 10 element array versus a 1000 element array, perhaps.

Player 10 should improve some of the casting costs and differences in performance between numerical types.



Another gotcha. Though no professional uses them, ‘with’ blocks introduce the same overhead that anonymous functions do, without any of usefulness. [Ed: It was pointed out to me that it's super-useful in graphics code -- true! though given the number of instructions you're likely to give for any non-trivial graphic, you're best off making a variable named g and being explicit about it.] You push the with object onto the scope chain, and then have to search the scope chain for every call within that block.

Too bad we didn’t implement them the way VB did, where you could tell which statements used the with object because they all started with a “.” (of course, there would be limitations).

Many equivalent expressions in AS3 are not equivalent in byte size or performance. Many of the convenience mechanisms, like with, are useless because their performance is so poor. I always meant to explore this and actually write posts in my blog, flexamphetamine.

By the way: Have you tested different kinds of variable accesses? I forget what my results were (two years ago…), but this might have been the order of expense (from least): argument variables (strange, huh?), local variables, class variables. I don’t think I tested statics.

No comments: