With access specifiers, statics, plain functions, and overriding, there are a lot of ways you can dress up a function in AS3. But how many programmers really know the performance implications of these options? Read on to find a straightforward test showing just that. EDIT: added functions defined in interfaces, getters, setters, and final functions.

In this little test app I’ve simply created empty version of a bunch of permutations of the options you have when making a function. First a little base class so we have something to override:

package
{
	import flash.display.Sprite;
 
	public class Base extends Sprite
	{
		protected function overrideProtectedFunction(): void {}
		internal function overrideInternalFunction(): void {}
		public function overridePublicFunction(): void {}
 
		protected function finalOverrideProtectedFunction(): void {}
		internal function finalOverrideInternalFunction(): void {}
		public function finalOverridePublicFunction(): void {}
	}
}

And then the main test app:

package
{
	import flash.display.*;
	import flash.events.Event;
	import flash.text.*;
	import flash.utils.*;
 
	/**
	*   An app to test function call performance
	*   @author Jackson Dunstan
	*/
	public class FunctionPerformanceTest extends Base implements IInterface
	{
		private function privateFunction(): void {}
		protected function protectedFunction(): void {}
		internal function internalFunction(): void {}
		public function publicFunction(): void {}
 
		private function get privateGetFunction(): int { return 0; }
		protected function get protectedGetFunction(): int { return 0; }
		internal function get internalGetFunction(): int { return 0; }
		public function get publicGetFunction(): int { return 0; }
 
		private function set privateSetFunction(val:int): void {}
		protected function set protectedSetFunction(val:int): void {}
		internal function set internalSetFunction(val:int): void {}
		public function set publicSetFunction(val:int): void {}
 
		private static function staticPrivateFunction(): void {}
		protected static function staticProtectedFunction(): void {}
		internal static function staticInternalFunction(): void {}
		public static function staticPublicFunction(): void {}
 
		override protected function overrideProtectedFunction(): void {}
		override internal function overrideInternalFunction(): void {}
		override public function overridePublicFunction(): void {}
 
		final private function finalPrivateFunction(): void {}
		final protected function finalProtectedFunction(): void {}
		final internal function finalInternalFunction(): void {}
		final public function finalPublicFunction(): void {}
 
		final override protected function finalOverrideProtectedFunction(): void {}
		final override internal function finalOverrideInternalFunction(): void {}
		final override public function finalOverridePublicFunction(): void {}
 
		public function interfaceFunction(): void {}
 
		private var __logger:TextField; 
 
		/**
		*   Application entry point
		*/
		public function FunctionPerformanceTest()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			__logger = new TextField();
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			addEventListener(Event.ENTER_FRAME, testDynamicFunctions);
 		}
 
 		private function testDynamicFunctions(ev:Event): void
 		{
 			var beforeTime:int;
			var i:int;
			const NUM_ITERATIONS:int = 10000000;
 
			function localFunction(): void {}
			var functionVarFunction:Function = function(): void {}
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				plainFunction();
			}
			log("Plain: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				localFunction();
			}
			log("Local: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				functionVarFunction();
			}
			log("Function var: " + (getTimer()-beforeTime));
 
			log("");
 
			removeEventListener(Event.ENTER_FRAME, testDynamicFunctions);
			addEventListener(Event.ENTER_FRAME, testMethods);
 		}
 
 		private function testMethods(ev:Event): void
 		{
			var beforeTime:int;
			var i:int;
			const NUM_ITERATIONS:int = 10000000;
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				privateFunction();
			}
			log("Private: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				protectedFunction();
			}
			log("Protected: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				internalFunction();
			}
			log("Internal: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				publicFunction();
			}
			log("Public: " + (getTimer()-beforeTime));
 
			log("");
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				this.privateFunction();
			}
			log("this.Private: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				this.protectedFunction();
			}
			log("this.Protected: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				this.internalFunction();
			}
			log("this.Internal: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				this.publicFunction();
			}
			log("this.Public: " + (getTimer()-beforeTime));
 
			log("");
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				staticPrivateFunction();
			}
			log("Static private: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				staticProtectedFunction();
			}
			log("Static protected: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				staticInternalFunction();
			}
			log("Static internal: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				staticPublicFunction();
			}
			log("Static public: " + (getTimer()-beforeTime));
 
			log("");
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				overrideProtectedFunction();
			}
			log("Override protected: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				overrideInternalFunction();
			}
			log("Override internal: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				overridePublicFunction();
			}
			log("Override public: " + (getTimer()-beforeTime));
 
			log("");
 
			removeEventListener(Event.ENTER_FRAME, testMethods);
			addEventListener(Event.ENTER_FRAME, testSupersAndInterfaces);
		}
 
		private function testSupersAndInterfaces(ev:Event): void
		{
			var beforeTime:int;
			var i:int;
			const NUM_ITERATIONS:int = 10000000;
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				super.overrideProtectedFunction();
			}
			log("Super protected: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				super.overrideInternalFunction();
			}
			log("Super internal: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				super.overridePublicFunction();
			}
			log("Super public: " + (getTimer()-beforeTime));
 
			log("");
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				interfaceFunction();
			}
			log("Interface direct: " + (getTimer()-beforeTime));
 
			var inter:IInterface = this;
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				inter.interfaceFunction();
			}
			log("Interface via interface: " + (getTimer()-beforeTime));
 
			var clazz:FunctionPerformanceTest = this;
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				clazz.interfaceFunction();
			}
			log("Interface via class: " + (getTimer()-beforeTime));
 
			log("");
 
			removeEventListener(Event.ENTER_FRAME, testSupersAndInterfaces);
			addEventListener(Event.ENTER_FRAME, testGettersAndSetters);
		}
 
		private function testGettersAndSetters(ev:Event): void
		{
			var beforeTime:int;
			var i:int;
			const NUM_ITERATIONS:int = 10000000;
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				privateGetFunction;
			}
			log("Private Get: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				protectedGetFunction;
			}
			log("Protected Get: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				internalGetFunction;
			}
			log("Internal Get: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				publicGetFunction;
			}
			log("Public Get: " + (getTimer()-beforeTime));
 
			log("");
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				privateSetFunction = 0;
			}
			log("Private Set: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				protectedSetFunction = 0;
			}
			log("Protected Set: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				internalSetFunction = 0;
			}
			log("Internal Set: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				publicSetFunction = 0;
			}
			log("Public Set: " + (getTimer()-beforeTime));
 
			log("");
 
 			removeEventListener(Event.ENTER_FRAME, testGettersAndSetters);
			addEventListener(Event.ENTER_FRAME, testFinals);
 		}
 
 		private function testFinals(ev:Event): void
 		{
			var beforeTime:int;
			var i:int;
			const NUM_ITERATIONS:int = 10000000;
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				finalPrivateFunction();
			}
			log("Final Private: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				finalProtectedFunction();
			}
			log("Final Protected: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				finalInternalFunction();
			}
			log("Final Internal: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				finalPublicFunction();
			}
			log("Final Public: " + (getTimer()-beforeTime));
 
			log("");
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				finalOverrideProtectedFunction();
			}
			log("Final Override protected: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				finalOverrideInternalFunction();
			}
			log("Final Override internal: " + (getTimer()-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < NUM_ITERATIONS; ++i)
			{
				finalOverridePublicFunction();
			}
			log("Final Override public: " + (getTimer()-beforeTime));
 
			removeEventListener(Event.ENTER_FRAME, testFinals);
		}
 
		private function log(msg:*): void
 		{
 			__logger.appendText(msg + "\n");
 		}
	}
}
function plainFunction(): void {}
interface IInterface
{
	function interfaceFunction(): void;
}

The output I see is never dependent on the access specifier (private, protected, internal, public), calling through this, the final keyword, or the presence of a get or set keyword, which is good. Here are results I get with access specifiers and this calls stripped out:

Test 3.0 Ghz Intel Core 2 Duo, 4 GB RAM, Windows XP
Plain 1969
Local 625
Var 670
Method 48
Static 60
Override 48
super 87
Interface Direct 48
Interface via Interface 65
Interface via Class 46

Immediately we see the clear outliers: plain functions, local functions, and Function variables. While I’ve talked about the slowness of these before, it’s good to see some confirmation. These are between 22 and 7 times as slow as the next slowest form of function call: calls through super. These in turn are 45% slower than statics and calls through an interface, which are 25% slower than everything else: methods, methods accessed through this, and overridden methods.

If you want speed you should not be using plain functions, local functions, or Function variables. This should give some hint as to the speed of Flash’s package-level functions like getTimer(). Further, you can get big speedups by foregoing static functions, calls trough an interface, and calls through super, especially if you’re going to be using that convenience a lot. The rest of the functions are as fast as you can get. Don’t let anyone tell you that access specifiers matter, calling through this adds needless slowdown, functions should be final for speed, or overriding is handy but slow. It is handy, but it’s not slow.

Keep the above speeds in the back of your mind as you architect and implement your AS3 programs. It may save you some optimization time down the line.