I remember when I started messing with this Flash/AS3 business a while ago, I read about these “new” uint and int types that would supposedly perform better than the “Number” type. THEN I read a bunch of articles by “reputable” people posting about how the performance gains didn’t exist, and which gave seemingly random benchmarks on some tests run using them.
So I looked into it and, barring some crazy patching by Adobe since those original articles were written (over a year ago as of this posting), I’m left to believe that they were just plain wrong. I think that this was due to a misunderstanding of the implicit conversions that happen, even though the authors of these articles *seemed* to be aware of the issue. Who knows.
But here are my results:
UINT time: 90
INT time: 92
NUMBER time: 303
WOW, seems pretty much like what you would expect, huh? Isn’t that just CRAZY?
AS3 has a weak typing system, so you often don’t realize when conversions are being done. Converting takes time. The best you can do is just make sure your types are all homogeneous for a given operation. For example, arrays return their length as “uint”s, so use uints to store array lengths, compare with those lengths, etc. getTimer(), for some reason I don’t understand, returns a regular ‘int’… so operations on values returned from getTimer() should be ints whenever possible (or converted to uint or whatever, and then operated on with other uints/whatever).
Here’s a made up example of how implicit converting can eat your lunch. Please DONT quote me on this as I could, and am likely, wrong about some of this stuff… but to give you an idea of how it MIGHT work, consider this:
var x:uint = 5;
var y:uint = x * 3.5;
Fairly innocuous, huh? Here’s what might happen in this situation:
* The compiler probably thinks ‘3.5′ is a Number type (because of the decimal), so it might decide it needs to promote x to a Number type as well, since the types need to be the same to perform a multiplication (and it’ll promote to Number before it demotes to int, for somewhat obvious reasons).
* It multiplies the promoted x by the 3.5 and gets some value. This value is a Number, of course, since it’s the product of two other Numbers.
* Next, it has to assign this product to our ‘y’ variable, which is a uint. This means that the value obtained by the multiplication has to be converted *again* back to a uint.
So the unaware developer then replaces the x and y values with Number variables and totally eliminates the implicit converting, which increases overall performance. The conclusion reached, then, is that ‘uints are slow and Numbers are fast’, which is completely wrong. Some more might even decide that ‘multiplication is just faster with Numbers’, which is ALSO wrong. Changing our 3.5 to a 3 removes the need for any sort of converting, so the two integers can be multiplied safely and no runtime penalty is incurred.
You could PROBABLY figure out exactly what’s going on with a decompiler, if you feel like doing that. I’m gonna take the fat and sassy route at the moment, though, and just not do it. I’ll take the cop-out “exercise for the reader” excuse for this one
My test results for the example above where like this for the 3.5 value:
Integer (Implicit Conversions): 4192
Number (Implicit Conversions): 3507
And the same exact thing using 3.0 as a value:
Integer (Implicit Conversions): 3422
Number (Implicit Conversions): 3564
It’s easy to just make everything a Number and see performance gains because implicit casting will never happen with Numbers (they’re as high as you can promote in Flash). If you’re careful with making sure your types are used properly, you could see some significant performance gains (look again at the for loop test at the beginning of the post, for example) and, at worst, no performance loss.
The code I used for these tests is:
package
{
import flash.display.Sprite;
import flash.utils.getTimer;
public class TypeBenchmarker extends Sprite
{
private static const TOTAL_ITERATIONS_UINT:uint = 10000000;
private static const TOTAL_ITERATIONS_INT:int = 10000000;
private static const TOTAL_ITERATIONS_NUMBER:Number = 10000000;
private static const TOTAL_TESTS:uint = 3;
public function TypeBenchmarker()
{
var unsigned_int:uint;
var signed_int:int;
var number:Number;
var start_time:int;
var time_elapsed:int;
for (var i:uint = 0; i < TOTAL_TESTS; i++)
{
start_time = getTimer();
for (unsigned_int = 0; unsigned_int < TOTAL_ITERATIONS_UINT; unsigned_int++) { }
time_elapsed = getTimer() - start_time;
trace("UINT time: " + time_elapsed); // Implicit promotion to Number
start_time = getTimer();
for (signed_int = 0; signed_int < TOTAL_ITERATIONS_INT; signed_int++) { }
time_elapsed = getTimer() - start_time;
trace("INT time: " + time_elapsed); // Implicit promotion to Number
start_time = getTimer();
for (number = 0.0; number < TOTAL_ITERATIONS_NUMBER; number++) { }
time_elapsed = getTimer() - start_time;
trace("NUMBER time: " + time_elapsed); // Implicit promotion to Number
/// --- Implicit Casting Example Below ---
var x:uint;
var y:uint;
start_time = getTimer();
for (unsigned_int = 0; unsigned_int < TOTAL_ITERATIONS_UINT; unsigned_int++)
{
x = 5;
y = x * 3.5;
}
time_elapsed = getTimer() - start_time;
trace("Integer (Implicit Conversions): " + time_elapsed);
var o:Number;
var p:Number;
start_time = getTimer();
for (unsigned_int = 0; unsigned_int < TOTAL_ITERATIONS_UINT; unsigned_int++)
{
o = 5;
p = o * 3.5;
}
time_elapsed = getTimer() - start_time;
trace("Number (Implicit Conversions): " + time_elapsed);
}
}
}
}
If anyone is bored enough to try this code out, and finds different results, feel free to slap me with ‘em!
James | 06-Apr-08 at 4:08 pm | Permalink
I totally dig this kind of research. Getting things to work faster has always been a top priority with me. Especially since my latest projects are games. If it’s as easy as horsing around with (somewhat) primitive data types in Actionscript 3.0, then I’d better keep this in mind as I develop my future games. Thanks!
Zack Jordan | 24-Apr-08 at 8:38 pm | Permalink
I read those same articles on Numbers vs. ints/uints, but it seemed ridiculous to me that Adobe would introduce something to the language for no reason. Thus, I kept using ints in spite of the “benchmarks.” So, in the end, I’m glad to see I was proven right… sort of.
Douglas Thompson | 24-Apr-08 at 9:27 pm | Permalink
James - Yup, it’s that easy. It’s a little bit easy to mess up here and there and allow the conversion to take place as well, though. One thing you can do is download ABCDump (or compile it.. a long, difficult process in my experience) to decompile your AS3 stuff into ASM-like bytecode to see what’s going on “for sure”. This is a technique used in other languages all the time–not really sure why it’s basically non-existant in the AS world… but the AS world was always a little bit weird.
Zack - I’m glad to hear that! Too many members of the Flash community seem to read one thing in one place, then adhere to it without question, spreading it around to others who do the same. Ah well, what can ya do…
Rezmason | 09-May-08 at 10:21 pm | Permalink
Geez, without resorting to ABCDumping all my complex AS3, isn’t there some way of avoiding conversions in a statement by just looking at it? If it came down to it, I’d rather leave ABCDump on the shelf.
I think that hesitation, by the way, is largely due to Flash developers’ tendency to stick with graphical development environments whenever possible. ABCDump runs in a shell, right? That can be intimidating.