flash.utils.describeType has been around since Flash 9 and is the standard way to find out interesting information about a Class type, including its metadata/annotations. However, there’s a hidden function called describeTypeJSON that provides an interesting alternative. Since describeType is notoriously slow, could describeTypeJSON be the speedy alternative we’ve been looking for? Today’s article puts them to the test!

First of all, you should read the excellent article by Till Schneidereit about describeTypeJSON to find out lots about its internals. Suffice to say that it’s a hidden function in the avmplus package with no public visibility. We therefore need to create a wrapper class in the avmplus package so we can get at it. Thankfully, the SwiftSuspeners project has provided just such a class.

So what do you get out of describeTypeJSON? Well, just like when you use the JSON class to parse a String, you’ll get an untyped Object. As an example, I tested this simple class:

package
{
	public class Person
	{
		public var name:String;
		public var age:int;
		public var alive:Boolean;
	}
}

And printed the Object that describeTypeJSON returned with a simple recursive function:

function printObject(obj:Object, numTabs:int=0): void
{
	var tabs:String = "";
	for (var i:int = 0; i < numTabs; ++i)
	{
		tabs += "\t";
	}
	for (var k:* in obj)
	{
		var v:* = obj[k];
		trace(tabs + k + " = " + v);
		if (v)
		{
			printObject(v, numTabs+1);
		}
	}
}

Here’s what I got:

isDynamic = false
isStatic = false
name = Person
traits = [object Object]
	interfaces = 
	methods = null
	accessors = null
	bases = Object
		0 = Object
	metadata = [object Object]
		0 = [object Object]
			value = [object Object]
				0 = [object Object]
					value = 24
					key = pos
			name = __go_to_definition_help
	constructor = null
	variables = [object Object],[object Object],[object Object]
		0 = [object Object]
			uri = null
			name = alive
			access = readwrite
			metadata = [object Object]
				0 = [object Object]
					value = [object Object]
						0 = [object Object]
							value = 95
							key = pos
					name = __go_to_definition_help
			type = Boolean
		1 = [object Object]
			uri = null
			name = name
			access = readwrite
			metadata = [object Object]
				0 = [object Object]
					value = [object Object]
						0 = [object Object]
							value = 47
							key = pos
					name = __go_to_definition_help
			type = String
		2 = [object Object]
			uri = null
			name = age
			access = readwrite
			metadata = [object Object]
				0 = [object Object]
					value = [object Object]
						0 = [object Object]
							value = 73
							key = pos
					name = __go_to_definition_help
			type = int
isFinal = false

The formatting isn’t great, but you can clearly see that the output is very similar to what we get from good old describeType:

<type name="Person" base="Class" isDynamic="true" isFinal="true" isStatic="true">
  <extendsClass type="Class"/>
  <extendsClass type="Object"/>
  <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
  <factory type="Person">
    <extendsClass type="Object"/>
    <variable name="alive" type="Boolean">
      <metadata name="__go_to_definition_help">
        <arg key="pos" value="95"/>
      </metadata>
    </variable>
    <variable name="name" type="String">
      <metadata name="__go_to_definition_help">
        <arg key="pos" value="47"/>
      </metadata>
    </variable>
    <variable name="age" type="int">
      <metadata name="__go_to_definition_help">
        <arg key="pos" value="73"/>
      </metadata>
    </variable>
    <metadata name="__go_to_definition_help">
      <arg key="pos" value="24"/>
    </metadata>
  </factory>
</type>

Now on to the main point of this article: speed. Which is faster? Let’s run each 10,000 times and see. Here’s a little test app that does just that:

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
	import avmplus.*;
 
	public class DescribeTypeSpeed extends Sprite
	{
		private var logger:TextField = new TextField();
		private function row(...cols): void
		{
			logger.appendText(cols.join(",") + "\n");
		}
 
		public function DescribeTypeSpeed()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			init();
		}
 
		private function init(): void
		{
			const REPS:int = 10000;
			var i:int;
			var beforeTime:int;
			var afterTime:int;
			var describer:DescribeTypeJSON = new DescribeTypeJSON();
 
			row("Operation", "Time");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				flash.utils.describeType(Person);
			}
			afterTime = getTimer();
			row("describeType", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				describer.getInstanceDescription(Person);
			}
			afterTime = getTimer();
			row("describeTypeJSON", (afterTime-beforeTime));
		}
	}
}

Run the test

I tested this app using the following environment:

  • Release version of Flash Player 14.0.0.125
  • 2.3 Ghz Intel Core i7-3615QM
  • Mac OS X 10.9.2
  • Google Chrome 35.0.1916.153
  • ASC 2.0.0 build 354130 (-debug=false -verbose-stacktraces=false -inline -optimize=true)

And got these results:

Operation Time
describeType 643
describeTypeJSON 135

describeType vs. describeTypeJSON Graph

It turns out that describeTypeJSON is about 5x faster than describeType. This means it’s probably worth jumping through the hoops necessary to get access to the hidden functionality. The access is still pretty slow (0.135 ms each on this relatively-fast machine), so don’t call the function more than you need to.

One tried-and-true method of optimizing this is to cache the results in a static Dictionary since the types don’t change at runtime. This makes subsequent calls to either describeType or describeTypeJSON almost free. If you do this, the speedup gained from describeTypeJSON will only apply to the first call to describe a Class, but even that can be quite significant.

Spot a bug? Have a question or suggestion? Post a comment!