AS3 has an interesting feature that is sometimes used to great effect: dynamic classes. These classes can have fields added and removed to them and used like any other field. You can even make your own dynamic classes with the dynamic keyword. However, all of this fancy functionality comes at a steep performance cost. How steep? Read on to see just how steep.

Firstly, it’s good to know just which classes are dynamic and which aren’t. All classes you write in AS3 are not dynamic unless you explicitly use the dynamic keyword. This is true even for classes you write that extend classes that are dynamic. For example, if you extend MovieClip, a dynamic class, and don’t use the dynamic keyword, your class will not be dynamic.

As for the classes in Flash’s AS3 API, quite a few are dynamic. At the top of any of Adobe’s documentation pages on the Flash API, you’ll see this text:

Class public dynamic class X

If “dynamic” is there, the X class is dynamic. Here is a partial list of commonly-used dynamic classes:

  • Dictionary
  • Array
  • Class
  • Object
  • RegExp
  • MovieClip
  • Error (and many derivatives)

To test the performance of dynamic access, I’ve written a small app. It tests the read and write performance of a built-in non-static class (Point) and an AS3 non-static class (MyPoint) against an AS3 dynamic class (MyDynamic) and several built-in static classes (Dictionary, Array, Class, Object). As a special case, the I test the performance of a declared x variable in MyDynamic against an undeclared z variable to see if there is any difference.

package
{
	import flash.display.*;
	import flash.events.*;
	import flash.utils.*;
	import flash.text.*;
	import flash.geom.*;
 
	public class DynamicAccess extends Sprite
	{
		public static var VAL:Number = 0;
 
		private var __logger:TextField = new TextField();
		private function log(msg:*): void { __logger.appendText(msg + "\n"); }
 
		public function DynamicAccess()
		{
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			var i:int;
			const REPS:int = 10000000;
			var beforeTime:int;
			var afterTime:int;
			var readTime:int;
			var point:Point = new Point(0,0);
			var myPoint:MyPoint = new MyPoint();
			var myDynamic:MyDynamic = new MyDynamic();
			var dictionary:Dictionary = new Dictionary();
				dictionary.x = 0;
			var array:Array = [0];
				array.x = 0;
			var clas:Class = DynamicAccess;
			var object:Object = {x:0};
 
			log("Type,Read,Write");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				point.x;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				point.x = 0;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			log("Point," + readTime + "," + (afterTime - beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				myPoint.x;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				myPoint.x = 0;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			log("MyPoint," + readTime + "," + (afterTime - beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				myDynamic.x;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				myDynamic.x = 0;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			log("MyDynamic (existing)," + readTime + "," + (afterTime - beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				myDynamic.z;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				myDynamic.z = 0;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			log("MyDynamic (added)," + readTime + "," + (afterTime - beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				dictionary.x;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				dictionary.x = 0;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			log("Dictionary," + readTime + "," + (afterTime - beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				array.x;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				array.x = 0;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			log("Array," + readTime + "," + (afterTime - beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				clas.VAL;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				clas.VAL = 0
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			log("Class," + readTime + "," + (afterTime - beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				object.x;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				object.x = 0;
			}
			afterTime = getTimer();
			readTime = afterTime - beforeTime;
			log("Object," + readTime + "," + (afterTime - beforeTime));
		}
 
		private function foo(): void {}
	}
}
class MyPoint
{
	public var x:Number = 0;
	public var y:Number = 0;
}
dynamic class MyDynamic
{
	public var x:Number = 0;
	public var y:Number = 0;
}

Here is the test environment I ran the above app on:

  • Flex SDK (MXMLC) 4.1.0.16076, compiling in release mode (no debugging or verbose stack traces)
  • Release version of Flash Player 10.2.152.26
  • 2.4 Ghz Intel Core i5
  • Mac OS X 10.6.6

And here are the results I got:

NOTE: These read times are actually copies of the write times. See the updated performance article with more tests to boot.

Type Read Write
Point 29 29
MyPoint 27 27
MyDynamic (existing) 27 27
MyDynamic (added) 402 402
Dictionary 423 423
Array 514 514
Class 114 114
Object 407 407

Here are some conclusions abut these results:

  • The non-dynamic access times are, as expected, vastly faster than the dynamic access times. On average, dynamic field access is about 13x slower than non-dynamic field access.
  • Accessing the declared fields of a dynamic class is just as fast as accessing the fields of a non-dynamic class
  • Class is about 4x faster than other dynamic classes, but still about 3x slower than non-dynamic classes
  • Read and write are just as fast as each other, regardless of the class’ dynamic status

In general, you should beware the dynamic keyword and the dynamic classes in the AS3 Flash API when writing performance-critical code. One notable exception is when accessing declared fields of dynamic classes: there’s no slowdown there.