Operator Speed
Today’s article is about the basic operators that make up most languages, and in particular AS3. Without them there wouldn’t be much of a language. So it would seem vitally important that we know how they perform relative to each other. Is shifting faster than adding? Adding faster than multiplying? Multiplying faster than dividing? Does the type of the operands matter? Read on for the results in high detail. Update: see my comment below for an important change to the results.
For this test I ripped a list of operators straight from Adobe’s AS3 documentation. I then tested using the int
, uint
, and Number
types, which are the only types that really apply to many of them. Sure Boolean
applies in some cases—especially logical operators—but there are so many bitwise and arithmetic operators that just don’t apply. Further, I mixed the types to see how the operators change speed when dealing with operands of two different types. The result is this very long, very repetitive, yet very simple test application:
package { import flash.display.*; import flash.events.*; import flash.utils.*; import flash.text.*; /** * An app to test operator performance * @author Jackson Dunstan */ public class BitwisePerformance extends Sprite { public function BitwisePerformance() { addEventListener(Event.ENTER_FRAME, onEnterFrame); } public function onEnterFrame(ev:Event): void { removeEventListener(Event.ENTER_FRAME, onEnterFrame); var logger:TextField = new TextField(); function log(msg:*): void { logger.appendText(msg + "\n"); } logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); const REPS:int = 50000000; var i:int; var beforeTime:int; var afterTime:int; var val1int:int; var val2int:int; var val1uint:uint; var val2uint:uint; var val1number:Number; var val2number:Number; function init(): void { val1int = 1; val2int = 2; val1uint = 1; val2uint = 2; val1number = 1; val2number = 2; } log("int"); init(); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int & val2int; } afterTime = getTimer(); log("\t&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int | val2int; } afterTime = getTimer(); log("\t|: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int ^ val2int; } afterTime = getTimer(); log("\t^: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { ~val1int; } afterTime = getTimer(); log("\t~: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >> val2int; } afterTime = getTimer(); log("\t>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >>> val2int; } afterTime = getTimer(); log("\t>>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int << val2int; } afterTime = getTimer(); log("\t<<: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int + val2int; } afterTime = getTimer(); log("\t+: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int - val2int; } afterTime = getTimer(); log("\t-: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int * val2int; } afterTime = getTimer(); log("\t*: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int / val2int; } afterTime = getTimer(); log("\t/: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int % val2int; } afterTime = getTimer(); log("\t%: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int++; } afterTime = getTimer(); log("\t++: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int--; } afterTime = getTimer(); log("\t--: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int == val2int; } afterTime = getTimer(); log("\t==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int === val2int; } afterTime = getTimer(); log("\t===: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int != val2int; } afterTime = getTimer(); log("\t!=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int !== val2int; } afterTime = getTimer(); log("\t!==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int > val2int; } afterTime = getTimer(); log("\t>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >= val2int; } afterTime = getTimer(); log("\t>=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int < val2int; } afterTime = getTimer(); log("\t<=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int && val2int; } afterTime = getTimer(); log("\t&&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int || val2int; } afterTime = getTimer(); log("\t||: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { !val1int; } afterTime = getTimer(); log("\t!: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int = val2int; } afterTime = getTimer(); log("\t=: " + (afterTime-beforeTime)); log("uint"); init(); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint & val2uint; } afterTime = getTimer(); log("\t&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint | val2uint; } afterTime = getTimer(); log("\t|: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint ^ val2uint; } afterTime = getTimer(); log("\t^: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { ~val1uint; } afterTime = getTimer(); log("\t~: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint >> val2uint; } afterTime = getTimer(); log("\t>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint >>> val2uint; } afterTime = getTimer(); log("\t>>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint << val2uint; } afterTime = getTimer(); log("\t<<: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint + val2uint; } afterTime = getTimer(); log("\t+: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint - val2uint; } afterTime = getTimer(); log("\t-: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint * val2uint; } afterTime = getTimer(); log("\t*: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint / val2uint; } afterTime = getTimer(); log("\t/: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint % val2uint; } afterTime = getTimer(); log("\t%: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint++; } afterTime = getTimer(); log("\t++: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint--; } afterTime = getTimer(); log("\t--: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint == val2uint; } afterTime = getTimer(); log("\t==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint === val2uint; } afterTime = getTimer(); log("\t===: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint != val2uint; } afterTime = getTimer(); log("\t!=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint !== val2uint; } afterTime = getTimer(); log("\t!==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint > val2uint; } afterTime = getTimer(); log("\t>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint >= val2uint; } afterTime = getTimer(); log("\t>=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint < val2uint; } afterTime = getTimer(); log("\t<=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint && val2uint; } afterTime = getTimer(); log("\t&&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint || val2uint; } afterTime = getTimer(); log("\t||: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { !val1uint; } afterTime = getTimer(); log("\t!: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint = val2uint; } afterTime = getTimer(); log("\t=: " + (afterTime-beforeTime)); log("int/uint"); init(); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int & val2uint; } afterTime = getTimer(); log("\t&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int | val2uint; } afterTime = getTimer(); log("\t|: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int ^ val2uint; } afterTime = getTimer(); log("\t^: " + (afterTime-beforeTime)); log("\t~: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >> val2uint; } afterTime = getTimer(); log("\t>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >>> val2uint; } afterTime = getTimer(); log("\t>>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int << val2uint; } afterTime = getTimer(); log("\t<<: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int + val2uint; } afterTime = getTimer(); log("\t+: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int - val2uint; } afterTime = getTimer(); log("\t-: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int * val2uint; } afterTime = getTimer(); log("\t*: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int / val2uint; } afterTime = getTimer(); log("\t/: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int % val2uint; } afterTime = getTimer(); log("\t%: " + (afterTime-beforeTime)); log("\t++: n/a"); log("\t--: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int == val2uint; } afterTime = getTimer(); log("\t==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int === val2uint; } afterTime = getTimer(); log("\t===: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int != val2uint; } afterTime = getTimer(); log("\t!=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int !== val2uint; } afterTime = getTimer(); log("\t!==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int > val2uint; } afterTime = getTimer(); log("\t>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >= val2uint; } afterTime = getTimer(); log("\t>=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int < val2uint; } afterTime = getTimer(); log("\t<=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int && val2uint; } afterTime = getTimer(); log("\t&&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int || val2uint; } afterTime = getTimer(); log("\t||: " + (afterTime-beforeTime)); log("\t!: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int = val2uint; } afterTime = getTimer(); log("\t=: " + (afterTime-beforeTime)); log("number"); init(); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number & val2number; } afterTime = getTimer(); log("\t&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number | val2number; } afterTime = getTimer(); log("\t|: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number ^ val2number; } afterTime = getTimer(); log("\t^: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { ~val1number; } afterTime = getTimer(); log("\t~: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number >> val2number; } afterTime = getTimer(); log("\t>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number >>> val2number; } afterTime = getTimer(); log("\t>>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number << val2number; } afterTime = getTimer(); log("\t<<: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number + val2number; } afterTime = getTimer(); log("\t+: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number - val2number; } afterTime = getTimer(); log("\t-: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number * val2number; } afterTime = getTimer(); log("\t*: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number / val2number; } afterTime = getTimer(); log("\t/: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number % val2number; } afterTime = getTimer(); log("\t%: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number++; } afterTime = getTimer(); log("\t++: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number--; } afterTime = getTimer(); log("\t--: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number == val2number; } afterTime = getTimer(); log("\t==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number === val2number; } afterTime = getTimer(); log("\t===: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number != val2number; } afterTime = getTimer(); log("\t!=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number !== val2number; } afterTime = getTimer(); log("\t!==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number > val2number; } afterTime = getTimer(); log("\t>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number >= val2number; } afterTime = getTimer(); log("\t>=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number < val2number; } afterTime = getTimer(); log("\t<=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number && val2number; } afterTime = getTimer(); log("\t&&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number || val2number; } afterTime = getTimer(); log("\t||: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { !val1number; } afterTime = getTimer(); log("\t!: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1number = val2number; } afterTime = getTimer(); log("\t=: " + (afterTime-beforeTime)); log("int/number"); init(); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int & val2number; } afterTime = getTimer(); log("\t&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int | val2number; } afterTime = getTimer(); log("\t|: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int ^ val2number; } afterTime = getTimer(); log("\t^: " + (afterTime-beforeTime)); log("\t~: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >> val2number; } afterTime = getTimer(); log("\t>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >>> val2number; } afterTime = getTimer(); log("\t>>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int << val2number; } afterTime = getTimer(); log("\t<<: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int + val2number; } afterTime = getTimer(); log("\t+: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int - val2number; } afterTime = getTimer(); log("\t-: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int * val2number; } afterTime = getTimer(); log("\t*: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int / val2number; } afterTime = getTimer(); log("\t/: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int % val2number; } afterTime = getTimer(); log("\t%: " + (afterTime-beforeTime)); log("\t++: n/a"); log("\t--: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int == val2number; } afterTime = getTimer(); log("\t==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int === val2number; } afterTime = getTimer(); log("\t===: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int != val2number; } afterTime = getTimer(); log("\t!=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int !== val2number; } afterTime = getTimer(); log("\t!==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int > val2number; } afterTime = getTimer(); log("\t>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int >= val2number; } afterTime = getTimer(); log("\t>=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int < val2number; } afterTime = getTimer(); log("\t<=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int && val2number; } afterTime = getTimer(); log("\t&&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int || val2number; } afterTime = getTimer(); log("\t||: " + (afterTime-beforeTime)); log("\t!: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1int = val2number; } afterTime = getTimer(); log("\t=: " + (afterTime-beforeTime)); log("uint/number"); init(); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint & val2number; } afterTime = getTimer(); log("\t&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint | val2number; } afterTime = getTimer(); log("\t|: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint ^ val2number; } afterTime = getTimer(); log("\t^: " + (afterTime-beforeTime)); log("\t~: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint >> val2number; } afterTime = getTimer(); log("\t>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint >>> val2number; } afterTime = getTimer(); log("\t>>>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint << val2number; } afterTime = getTimer(); log("\t<<: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint + val2number; } afterTime = getTimer(); log("\t+: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint - val2number; } afterTime = getTimer(); log("\t-: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint * val2number; } afterTime = getTimer(); log("\t*: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint / val2number; } afterTime = getTimer(); log("\t/: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint % val2number; } afterTime = getTimer(); log("\t%: " + (afterTime-beforeTime)); log("\t++: n/a"); log("\t--: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint == val2number; } afterTime = getTimer(); log("\t==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint === val2number; } afterTime = getTimer(); log("\t===: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint != val2number; } afterTime = getTimer(); log("\t!=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint !== val2number; } afterTime = getTimer(); log("\t!==: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint > val2number; } afterTime = getTimer(); log("\t>: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint >= val2number; } afterTime = getTimer(); log("\t>=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint < val2number; } afterTime = getTimer(); log("\t<=: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint && val2number; } afterTime = getTimer(); log("\t&&: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint || val2number; } afterTime = getTimer(); log("\t||: " + (afterTime-beforeTime)); log("\t!: n/a"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { val1uint = val2number; } afterTime = getTimer(); log("\t=: " + (afterTime-beforeTime)); } } }
I compiled with MXMLC 4.1 and ran using Flash Player 10.1 on a 2.4 Ghz Intel Core i5 with Mac OS X 10.6. I also tested on a 3.0 Ghz Intel Core 2 Duo with Windows XP and got almost identical results, but for brevity I won’t list them here. Here are the results I got with the Core i5:
Operator | int | uint | int/uint | number | int/number | uint/number |
---|---|---|---|---|---|---|
& | 143 | 145 | 145 | 148 | 142 | 142 |
| | 141 | 148 | 144 | 146 | 144 | 145 |
^ | 141 | 145 | 143 | 142 | 148 | 141 |
~ | 150 | 142 | n/a | 141 | n/a | n/a |
>> | 145 | 143 | 147 | 143 | 144 | 145 |
>>> | 142 | 140 | 146 | 147 | 143 | 141 |
<< | 143 | 145 | 142 | 147 | 144 | 142 |
+ | 142 | 146 | 142 | 145 | 142 | 140 |
– | 145 | 141 | 142 | 141 | 146 | 145 |
* | 147 | 143 | 146 | 142 | 147 | 145 |
/ | 144 | 140 | 146 | 144 | 144 | 147 |
% | 144 | 142 | 142 | 146 | 142 | 139 |
++ | 157 | 159 | n/a | 174 | n/a | n/a |
— | 157 | 155 | n/a | 170 | n/a | n/a |
== | 150 | 142 | 149 | 147 | 147 | 145 |
=== | 142 | 141 | 147 | 145 | 146 | 146 |
!= | 141 | 147 | 143 | 146 | 142 | 142 |
!== | 141 | 146 | 143 | 141 | 141 | 142 |
> | 145 | 152 | 142 | 142 | 140 | 143 |
>= | 146 | 145 | 142 | 146 | 146 | 142 |
<= | 148 | 142 | 146 | 144 | 144 | 143 |
&& | 145 | 150 | 530 | 301 | 527 | 528 |
|| | 150 | 154 | 528 | 301 | 505 | 515 |
! | 146 | 142 | n/a | 142 | n/a | n/a |
= | 160 | 156 | 157 | 158 | 302 | 262 |
With a couple of exceptions, the above table is extremely uniform. Almost every operator on every type was within a 10% performance range. It’s especially noteworthy that classic optimizations like using shifts instead of multiplication (x << 1
instead of x * 2
) or multiplying by the inverse (x *= 0.1
instead of x / 10
) don't actually get you any discernable results. Is this the JIT doing optimizations? Is this modern CPU architecture at work? Is this virtual machine overhead dwarfing the benefits? It's hard to tell.
There are those couple of exceptions though:
&&
and||
-int
anduint
are 2x faster thanNumber
and 3.5x faster than mixed types (int/uint
,int/Number
,uint/Number
)=
-int/Number
anduint/Number
are 2x slower than everything else
If you can think of any performance critical code you have that's vulnerable to either of the above, you might have an optimization available to you. Otherwise, the main takeaway from this article is to not worry about which operators you're using. Don't bother making your code harder to read by using shifts instead of multiplication and division or by multiplying when dividing is clearer: you won't gain any performance in the process.
#1 by Amit Patel on October 5th, 2010 ·
I’m not too surprised. You’re measuring each operator once in a loop. The loop itself consists of an increment, a comparison, and a branch. I’d expect that branches are expensive on modern CPUs. So you’re comparing inc + cmp + br + add to inc + cmp + br + mul, and they’re similar.
I’d suggest “loop unrolling†by trying ten variables and performing the operation ten times inside the loop. That way you’re comparing inc + cmp + br + 10*add to inc + cmp + br + 10*mul. If add and mul really are different, the difference would show up better.
It’s probably even worse than this, as it’s not just the operation but also the read from the variable and the write back to the variable. I don’t have a good sense of whether Flash’s runtime stores that variable in a register.
#2 by jackson on October 6th, 2010 ·
I tried your suggestion and unrolled the loops to perform the operation ten times per loop. The results I got were essentially the same as above, but with one important difference: increment (
++
) and decrement (--
) are 10x slower than the other operations.Thanks for the suggestion!
#3 by skyboy on October 21st, 2010 ·
It’s worth noting that ++ and — are a combination of + and =, so being the combined speed is expected. the only place i can see where there might be a gain is with two increments vs += 2:
Which can be sped up if you edit the byte code by hand, but will nJIT see this and do it?
vs the vanilla
#4 by jackson on October 21st, 2010 ·
I doubt it will, but I haven’t tested it. By the way, what is nJIT? Google searches (“nJIT as3”, “nJIT tamarin”, “nJIT flash”) are turning up nothing…
#5 by skyboy on October 21st, 2010 ·
nanoJIT, the JIT compiler specific to AVM2
#6 by jackson on October 21st, 2010 ·
Ah, I’d never heard it called that before. I did a quick test where I did 1000000 loops of 100 operations of each (
++
and+= 2
) operation. That means one loop has 100+= 2
and the other loop has 200++
. This minimizes the effect of the loop overhead on the test since each operation is so cheap. The test is therefore really long, so I’m omitting it here.Anyhow, the results I get are 2 milliseconds for using
++
twice and 39 ms for using+= 2
once. So it would seem that++
is the hands-down winner, but that it’s unlikely there are many cases where it’ll make much of a difference as this is a very contrived example. Still, it’s good to know!#7 by erik on June 4th, 2011 ·
Out of curiosity, how would someone use
as the step size in a for loop?
#8 by jackson on June 4th, 2011 ·
The comma operator would work:
#9 by skyboy on October 21st, 2010 ·
Not too surprised, I’m probably the first person to ever call it nJIT.
That’s a rather dramatic speed up, even if it’s nil at a small level. It would be interesting to see the bytecode that nJIT is generating from the increments, it might be optimizing it to a single call of
+= 200
, orinclocal_i
#10 by jackson on October 21st, 2010 ·
It would be very interesting! Do you know of any way to see the machine code it generates?
#11 by skyboy on October 21st, 2010 ·
Maybe, there is a function that may be helpful: bytecode 0xEF, the debug command that traces bytecode, but i don’t know if it’s compiler’s bytecode or nJIT’s output. the only way I’ve ever triggered it is when the compiler/nJIT failed on a certain sequence of commands that caused it to return the debug command, which flash faithfully followed and left me very confused.
#12 by skyboy on February 19th, 2011 ·
There are a few missing tests from this, and the local functions are slowing the results quite massively to what they should be (~300%).
The “<=" test is actually "<" and "<=" is not in the test; nor are there tests for pre-increment vs post-increment, and there is actually a performance difference on uint/int for these.
I jumped the gun a little and reformatted the test and its output. You can see some initial results here: http://www.kongregate.com/forums/4/topics/145440#posts-3188489
I'm considering expanding it to include the various assignment operators as well.
#13 by jackson on February 19th, 2011 ·
Thanks for pointing this out. It’s a really long test with obvious copy/paste, so the possibility of error is pretty big. I’ll probably do an update article to this sometime after the Flash Player 10.2 Performance series.
#14 by dimuMurray on August 21st, 2011 ·
I noticed that you didn’t include the compound numeric operators in your tests.
Are we to assume, that being compound, the speed of these operators are roughly the sum of the speeds of their component operators?
#15 by jackson on August 22nd, 2011 ·
While not exactly those operators, check out Part Two of this article for
++
and the like. Also, this article talks about++
versus+=1
.