Notes: Math Optimizations
This is some more stuff migrated from the Labs Wiki. Below is just some aggregated tips and tricks I’ve found on other blogs or websites.
Extract Colors
Not really an optimization per se, but just how to extract color values.
//24bit var color:uint = 0x336699; var r:uint = color >> 16; var g:uint = color >> 8 & 0xFF; var b:uint = color & 0xFF; //32bit var color:uint = 0xff336699; var a:uint = color >>> 24; var r:uint = color >>> 16 & 0xFF; var g:uint = color >>> 8 & 0xFF; var b:uint = color & 0xFF;
Combine Colors
Not really an optimization per se, but just how to combine color values.
//24bit var r:uint = 0x33; var g:uint = 0x66; var b:uint = 0x99; var color:uint = r << 16 | g << 8 | b; //32bit var a:uint = 0xff; var r:uint = 0x33; var g:uint = 0x66; var b:uint = 0x99; var color:uint = a << 24 | r << 16 | g << 8 | b;
Modulo
If the divisor is a power of 2, the modulo (%) operation can be done with: modulus = numerator & (divisor – 1); This is about 600% faster. Ref
//Slower x = 131 % 4; //Faster x = 131 & (4 - 1);
Check if Even
//Slower isEven = (i % 2) == 0; //Faster isEven = (i & 1) == 0;
Division
For bit shifting, use any power of two. Also keep in mind that bit shifting only works with integers, not floats/numbers.
//Slowest 152ms var n:Number = value / 2; // Divide by 2 var n:Number = value / 4; // Divide by 4 //Faster 112ms var n:Number = value * .5; // Divide by 2 var n:Number = value * .25; // Divide by 4 //Fastest 63ms var n:Number = value >> 1; // Divide by 2 var n:Number = value >> 2; // Divide by 4
Multiplication
For bit shifting, use any power of two. Also keep in mind that bit shifting only works with integers, not floats/numbers.
//Slower var n:Number = value * 2; // Multiply by 2 var n:Number = value * 4; // Multiply by 4 //Fastest var n:Number = value << 1; // Multiply by 2 var n:Number = value << 2; // Multiply by 4
Math.floor
//Slower 1733ms var test:Number = Math.floor(1.5); //Faster 157ms var test:int = int(1.5); //Fastest 145ms var test:int = 1.5 >> 0;
Math.ceil
// Slower 1624ms var test:Number = Math.ceil(1.5); // Faster 440ms var n:Number = 1.5; var test:int = int(n+1) + (n >> 31); // Even faster but inaccurate var test:int = int(n) + 1; // Also faster but inaccurate var test:int = n == int(n) ? n : int(n) + 1;
Math.abs
Keep in mind that bit shifting only works with integers, not floats/numbers. Ref
// Slower 1572ms var nn:Number = -23; nn = Math.abs(nn); // Faster 76ms var nn:Number = -23; if (nn < 0) nn = -nn; // Faster by about 20% var x:Number = -23; var nn:Number = (x ^ (x >> 31)) - (x >> 31);
Math.sqrt
This uses the Babylonian method of approximating a square root. On Flash 10 there is still about a 20% performance gain if you really need it. But be sure to use this code INLINE and not as a separate function or all the performance gain will be lost. Ref
//--Using the Babylonian Method for computing square roots efficiently requires making the initial fast
// approximation as accurate as possible, insuring that the number of iterations can be minimized while
// perserving a desired level of accuracy.
//
var apprxInit:int;
var apprxRoot:Number;
var initVal:Number;
var invVal:Boolean;
if (sqNum < 1) {
initVal = 1 / sqNum;
invVal = true;
} else {
initVal = sqNum;
invVal = false;
}
apprxInit = initVal;
//--Make first approximation of APPRXROOT: Set the starting approximation APPRXINIT to determine
// its binary degree; in this case, the location of its most significant bit to determine its length
// in bits.
// In AS3, the most efficient method for accomplishing this is by comparing it to integers equal to odd
// powers of 2, then shifting it right half the number of bits of its length. This unfortunately results
// in rather unseemly code. The final treatment is one iteration of the Babylonian Method. "Hey, Morf
// said it's fast and it works"
// Well, yes... but this MUST BE USED AS INLINE CODE FOR ANY SPEED OPTIMIZATION TO BE REALIZED.
//
if (apprxInit > 7) {
if (apprxInit < 32768) {
if (apprxInit < 128) {
if (apprxInit < 32) {
apprxInit >>= 2;
if (apprxInit < 4) apprxInit++;
} else {
apprxInit >>= 3;
}
} else {
if (apprxInit < 2048) {
if (apprxInit < 512) {
apprxInit >>= 4;
} else {
apprxInit >>= 5;
}
} else {
if (apprxInit < 8096) {
apprxInit >>= 6;
} else {
apprxInit >>= 7;
}
}
}
} else {
if (apprxInit < 8388608) {
if (apprxInit < 524288) {
if (apprxInit < 131072) {
apprxInit >>= 8;
} else {
apprxInit >>= 9;
}
} else {
if (apprxInit < 2097152) {
apprxInit >>= 10;
} else {
apprxInit >>= 11;
}
}
} else {
if (apprxInit < 134217728) {
if (apprxInit < 33554432) {
apprxInit >>= 12;
} else {
apprxInit >>= 13;
}
} else {
apprxInit >>= 14; //What are you doing with a number this big anyway? Not bothering with yet another test.
}
}
}
apprxRoot = (apprxInit + initVal / apprxInit) * 0.5;
} else if (apprxInit < 2) {
apprxRoot = initVal * 0.5 + 0.5;
} else {
apprxRoot = initVal * 0.25 + 1;
}
//
//-- First approximation will be within about 1% at twice the speed and may be sufficient for collision detection computations.
//-- Now perform as many additional approximations as desired; fourth iteration = same precision as Math.sqrt().
//
apprxRoot = (apprxRoot + initVal / apprxRoot) * 0.5; // babylonian iteration 2 -- about 20% faster.
// apprxRoot = (apprxRoot + initVal / apprxRoot) * 0.5; // babylonian iteration 3 -- seven-digit precision.
// apprxRoot = (apprxRoot + initVal / apprxRoot) * 0.5; // babylonian iteration 4 -- full precision.
if (invVal) apprxRoot = 1 / apprxRoot;
// apprxRoot is now your final value
3 Comments to Notes: Math Optimizations
When you use bitwise for multiplication and division, take care that the result is not a float. So your comments a not exact :
var value:Number = 12.5;
//Slowest 152ms
var n:Number = value / 2; // Divide by 2
trace(n);
n = value / 4; // Divide by 4
trace(n);
//Faster 112ms
n = value * .5; // Divide by 2
trace(n);
n = value * .25; // Divide by 4
trace(n);
//Fastest 63ms
n = value >> 1; // Divide by 2 -> FALSE
trace(n);
n = value >> 2; // Divide by 4 -> FALSE
trace(n);
// OUTPUT :
6.25
3.125
6.25
3.125
6
3
Bye :)
January 28, 2010
Same for Math.abs
// Slower 1572ms
var value:Number = -0.5;
var test:Number = value;
var nn:Number = Math.abs(test);
trace(nn);
// Faster 76ms
test = value;
if (test > 31)) – (test >> 31); // FALSE
trace(nn);
// OUTPUT :
0.5
0.5
0
January 28, 2010
Thanks, I’ll make that clarification.
January 28, 2010