Notes: Math Optimizations

Wednesday, January 27th, 2010 | Notes

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

J
January 28, 2010

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 :)

J
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

gabriel
January 28, 2010

Thanks, I’ll make that clarification.

Leave a comment

Please upgrade your Flash Player To submit a comment, you must have Flash Player 9.0.0 or higher installed. I use a flash form here to help prevent spam.

Search

Categories