Category Archives: ActionScript

ActionScript performance test for Array, Object, Vector literals and Array.push, Vector.push methods

Yesterday a “tweet” pointed me to a nice article about ActionScript 3.0 optimization. Most of these techniques are quite common within the ActionScript developer scene but one thing caught my attention: A link to Jackson Dunstans blog post about runtime performance and the const and final keywords. Since I always tried to use at least final classes I was a little disappointed that my extra work time doesn’t have any performance benefit at runtime.

Due to that circumstance I was curious about some other common techniques like “Use Object and Array Literals Whenever Possible” or “Add Elements to the End of an Array Without Pushing”. I wanted to check performance benefits for myself just to make sure it’s worth the effort. So I wrote a little test script:

var result : String = "Test playerType: " + Capabilities.playerType + " version: " + Capabilities.version + "\n",
	i : int = 0,
	c : int = 1000000,
	array : Array,
	object : Object,
	vectorInt : Vector.<int>,
	startTime : int;
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { object = new Object(); }
result += c + " times: object = new Object()             -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { object = {}; }
result += c + " times: object = {}                       -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { array = new Array(); }
result += c + " times: array = new Array()               -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { array = []; }
result += c + " times: array = []                        -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { vectorInt = new Vector.<int>(); }
result += c + " times: vectorInt = new Vector.<int>()    -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { vectorInt = new <int>[]; }
result += c + " times: vectorInt = new <int>[]           -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { array.push( i ); }
result += c + " times: array.push( i )                   -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { array[ array.length ] = i; }
result += c + " times: array[ array.length ] = i         -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { vectorInt.push( i ); }
result += c + " times: vectorInt.push( i )               -> duration: " + String( getTimer() - startTime ) + "\n";
 
startTime = getTimer();
for ( i = 0; i < c; i++ ) { vectorInt[ vectorInt.length ] = i; }
result += c + " times: vectorInt[ vectorInt.length ] = i -> duration: " + String( getTimer() - startTime ) + "\n";
 
var textFormat : TextFormat = new TextFormat();
	textFormat.font = "Courier";
	textFormat.size = 14;
 
var textField : TextField = new TextField();
	textField.autoSize = TextFieldAutoSize.LEFT;
	textField.background = true;
	textField.backgroundColor = 0xFFFFFF;
	textField.defaultTextFormat = textFormat;
	textField.multiline = true;
	textField.text = result;
 
addChild( textField );

It turns out that the benefits differ greatly on platform and player type but basically it’s always a good idea to avoid using the .push methods and new keyword. While there’s almost no speed difference in Flash Players and Plug-ins, the debug versions are really slow. And check out the blazing fast object creation with AIR on iOS! Or is this a compiler optimization? Here my results:


Flash CS 5 publish
Test playerType: External version: MAC 10,1,52,14

1000000 times: object = new Object() -> duration: 246
1000000 times: object = {} -> duration: 427
1000000 times: array = new Array() -> duration: 1288
1000000 times: array = [] -> duration: 339
1000000 times: vectorInt = new Vector.() -> duration: 586
1000000 times: vectorInt = new [] -> duration: 596
1000000 times: array.push( i ) -> duration: 134
1000000 times: array[ array.length ] = i -> duration: 137
1000000 times: vectorInt.push( i ) -> duration: 113
1000000 times: vectorInt[ vectorInt.length ] = i -> duration: 114


Flash Player Debugger.app
Test playerType: StandAlone version: MAC 11,1,102,62

1000000 times: object = new Object() -> duration: 179
1000000 times: object = {} -> duration: 311
1000000 times: array = new Array() -> duration: 1072
1000000 times: array = [] -> duration: 313
1000000 times: vectorInt = new Vector.() -> duration: 397
1000000 times: vectorInt = new [] -> duration: 402
1000000 times: array.push( i ) -> duration: 118
1000000 times: array[ array.length ] = i -> duration: 108
1000000 times: vectorInt.push( i ) -> duration: 111
1000000 times: vectorInt[ vectorInt.length ] = i -> duration: 89


Flash Player.app
Test playerType: StandAlone version: MAC 11,1,102,62

1000000 times: object = new Object() -> duration: 127
1000000 times: object = {} -> duration: 183
1000000 times: array = new Array() -> duration: 227
1000000 times: array = [] -> duration: 189
1000000 times: vectorInt = new Vector.() -> duration: 215
1000000 times: vectorInt = new [] -> duration: 218
1000000 times: array.push( i ) -> duration: 72
1000000 times: array[ array.length ] = i -> duration: 62
1000000 times: vectorInt.push( i ) -> duration: 61
1000000 times: vectorInt[ vectorInt.length ] = i -> duration: 42


Browser Debug Plug-in (Safari)
Test playerType: PlugIn version: MAC 11,1,102,62

1000000 times: object = new Object() -> duration: 157
1000000 times: object = {} -> duration: 249
1000000 times: array = new Array() -> duration: 1015
1000000 times: array = [] -> duration: 293
1000000 times: vectorInt = new Vector.() -> duration: 281
1000000 times: vectorInt = new [] -> duration: 282
1000000 times: array.push( i ) -> duration: 124
1000000 times: array[ array.length ] = i -> duration: 117
1000000 times: vectorInt.push( i ) -> duration: 124
1000000 times: vectorInt[ vectorInt.length ] = i -> duration: 94


Browser Plug-in (Google Chrome)
Test playerType: PlugIn version: MAC 11,2,202,235

1000000 times: object = new Object() -> duration: 147
1000000 times: object = {} -> duration: 146
1000000 times: array = new Array() -> duration: 237
1000000 times: array = [] -> duration: 224
1000000 times: vectorInt = new Vector.() -> duration: 257
1000000 times: vectorInt = new [] -> duration: 266
1000000 times: array.push( i ) -> duration: 54
1000000 times: array[ array.length ] = i -> duration: 61
1000000 times: vectorInt.push( i ) -> duration: 70
1000000 times: vectorInt[ vectorInt.length ] = i -> duration: 51


AIR SDK 3.2
Test playerType: Desktop version: MAC 11,2,202,223

1000000 times: object = new Object() -> duration: 274
1000000 times: object = {} -> duration: 243
1000000 times: array = new Array() -> duration: 1344
1000000 times: array = [] -> duration: 379
1000000 times: vectorInt = new Vector.() -> duration: 431
1000000 times: vectorInt = new [] -> duration: 426
1000000 times: array.push( i ) -> duration: 135
1000000 times: array[ array.length ] = i -> duration: 136
1000000 times: vectorInt.push( i ) -> duration: 153
1000000 times: vectorInt[ vectorInt.length ] = i -> duration: 74


iOS (iPad 3) debug interpreter
Test playerType: StandAlone version: MAC 11,1,102,62

1000000 times: object = new Object() -> duration: 4707
1000000 times: object = {} -> duration: 2033
1000000 times: array = new Array() -> duration: 22191
1000000 times: array = [] -> duration: 2993
1000000 times: vectorInt = new Vector.() -> duration: 10235
1000000 times: vectorInt = new [] -> duration: 10156
1000000 times: array.push( i ) -> duration: 1728
1000000 times: array[ array.length ] = i -> duration: 1670
1000000 times: vectorInt.push( i ) -> duration: 2056
1000000 times: vectorInt[ vectorInt.length ] = i -> duration: 1951


iOS (iPad 3) ad-hoc
Test playerType: Desktop version: IOS 11,2,202,223

1000000 times: object = new Object() -> duration: 1224
1000000 times: object = {} -> duration: 0 !!!
1000000 times: array = new Array() -> duration: 5886
1000000 times: array = [] -> duration: 1048
1000000 times: vectorInt = new Vector.() -> duration: 2737
1000000 times: vectorInt = new [] -> duration: 2784
1000000 times: array.push( i ) -> duration: 2043
1000000 times: array[ array.length ] = i -> duration: 234
1000000 times: vectorInt.push( i ) -> duration: 1524
1000000 times: vectorInt[ vectorInt.length ] = i -> duration: 643

Always worth reading: http://gskinner.com/talks/quick/

Removing comments in CSS, HTML and ECMAScript (JavaScript)

While working on an Adobe AIR based source code editor I was looking for an easy way to remove comments from different kind of source codes. What first seemed like an easy regular expression turned out to be much more complex because comment pre- and suffixes can also occur in string literals or regular expressions and our old friend Internet Explorer allows HTML conditional comments and JavaScript conditional compilation. Great.

First I had a look at YUICompressors source code and Google Page Speed but then I decided to port a script for removing JavaScript comments created by James Padolsey.

I just improved the recognition for regular expressions a little and since this code should work with all ECMAScript based source codes I named my class ECMAScriptParser instead of JavaScriptParser:

package de.superclass.parser
{
	/**
	 * @author Markus Raab (superclass.de | blog.derRaab.com)
	 */
	public class ECMAScriptParser
	{
		/**
		 * Removes inline and block comments from an ECMAScript source string. 
		 * 
		 * This is a port of James Padolsey	 with an slightly improved RegExp recognition.
		 * @see: http://james.padolsey.com/javascript/removing-comments-in-javascript
		 * 
		 * @param ecmaScript		ECMAScript source string
		 * @param removeCondComp	Optional (default=false) - Whether to remove Internet Explorers JavasScript conditional compilation comments or not.   
		 * @return					ECMAScript source string without comments
		 */
		public static function removeComments( ecmaScript : String, removeCondComp : Boolean = false ) : String
		{
			var modeSingleQuote		: Boolean = false;
			var modeDoubleQuote		: Boolean = false;
			var modeRegExp			: Boolean = false;
			var modeBlockComment	: Boolean = false;
			var modeLineComment		: Boolean = false;
			var modeCondComp		: Boolean = false; 
 
			var vector : Vector.<String> = Vector.<String>( ( '__' + ecmaScript + '__' ).split( '' ) );
				vector.fixed = true;
 
			var c : int = vector.length;
 
			for ( var i : int = 0; i < c; i++ )
			{
				var string : String = vector[ i ];
 
				if ( modeRegExp )
				{
					if ( string === '/' && vector[ i - 1 ] !== '\\' )
					{
						modeRegExp = false;
					}
					continue;
				}
 
				if ( modeSingleQuote )
				{
					if ( string === "'" && vector[ i - 1 ] !== '\\' )
					{
						modeSingleQuote = false;
					}
					continue;
				}
 
				if ( modeDoubleQuote )
				{
					if ( string === '"' && vector[ i - 1 ] !== '\\' )
					{
						modeDoubleQuote = false;
					}
					continue;
				}
 
				if ( modeBlockComment )
				{
					if ( string === '*' && vector[ i + 1 ] === '/' )
					{
						vector[ i + 1 ] = '';
						modeBlockComment = false;
					}
					vector[ i ] = '';
					continue;
				}
 
				if ( modeLineComment)
				{
					string = vector[ i + 1 ];
					if ( string === '\n' || string === '\r' )
					{
						modeLineComment = false;
					}
					vector[ i ] = '';
					continue;
				}
 
				if ( modeCondComp )
				{
					if ( vector[ i - 2 ] === '@' && vector[ i - 1 ] === '*' && string === '/' )
					{
						modeCondComp = false;
					}
					continue;
				}
 
				if ( string === '"' )
				{
					modeDoubleQuote = true;
					continue;
				}
 
				if ( string === "'" )
				{
					modeSingleQuote = true;
					continue;
				}
 
				if ( string === '/' )
				{
					if ( ! removeCondComp && vector[ i + 1 ] === '*' && vector[ i + 2 ] === '@' )
					{
						modeCondComp = true;
						continue;
					}
 
					if ( vector[ i + 1 ] === '*' )
					{
						vector[ i ] = '';
 
						modeBlockComment = true;
						continue;
					}
 
					if ( vector[ i + 1 ] === '/' )
					{
						vector[ i ] = '';
						modeLineComment = true;
						continue;
					}
 
					for ( var k : int = i - 1; true; k-- )
					{
						string = vector[ k ];
						if ( string !== ' ' )
						{
							if ( string === '=' )
							{
								modeRegExp = true;
							}
							break;
						}
					}
				}
			}
			return vector.join( '' ).slice( 2, -2 );
		}
	}
}

Allright – CSS doesn’t allow inline or conditional comments so I removed this script functionality and created a CSSParser class:

package de.superclass.parser
{
	/**
	 * @author Markus Raab (superclass.de | blog.derRaab.com)
	 */
	public class CSSParser
	{
		/**
		 * Removes comments from a CSS string. 
		 * 
		 * This is a shrinked port of James Padolseys script.
		 * @see: http://james.padolsey.com/javascript/removing-comments-in-javascript
		 * 
		 * @param css				CSS string
		 * @return					CSS string without comments
		 */
		public static function removeComments( css : String) : String
		{
			var modeSingleQuote		: Boolean = false;
			var modeDoubleQuote		: Boolean = false;
			var modeBlockComment	: Boolean = false;
 
			var vector : Vector.<String> = Vector.<String>( ( '__' + css + '__' ).split( '' ) );
				vector.fixed = true;
 
			var c : int = vector.length;
 
			for ( var i : int = 0; i < c; i++ )
			{
				var string : String = vector[ i ];
 
				if ( modeSingleQuote )
				{
					if ( string === "'" && vector[ i - 1 ] !== '\\' )
					{
						modeSingleQuote = false;
					}
					continue;
				}
 
				if ( modeDoubleQuote )
				{
					if ( string === '"' && vector[ i - 1 ] !== '\\' )
					{
						modeDoubleQuote = false;
					}
					continue;
				}
 
				if ( modeBlockComment )
				{
					if ( string === '*' && vector[ i + 1 ] === '/' )
					{
						vector[ i + 1 ] = '';
						modeBlockComment = false;
					}
					vector[ i ] = '';
					continue;
				}
 
				if ( string === '"' )
				{
					modeDoubleQuote = true;
					continue;
				}
 
				if ( string === "'" )
				{
					modeSingleQuote = true;
					continue;
				}
 
				if ( string === '/' )
				{
					if ( vector[ i + 1 ] === '*' )
					{
						vector[ i ] = '';
 
						modeBlockComment = true;
						continue;
					}
				}
			}
			return vector.join( '' ).slice( 2, -2 );
		}
	}
}

Good. Lastly I extended the code to remove all comments within HTML source code, which can also contain JavaScript and of course CSS. This is the HTMLParser:

package de.superclass.parser
{
	/**
	 * @author Markus Raab (superclass.de | blog.derRaab.com)
	 */
	public class HTMLParser
	{
		/**
		 * Removes all HTML, CSS and ECMAScript comments from a HTML string.
		 * 
		 * This is an extended port of James Padolseys script with an improved RegExp recognition.
		 * @see: http://james.padolsey.com/javascript/removing-comments-in-javascript
		 * 
		 * @param html						HTML string
		 * @param removeHTMLCondComment		Optional (default=false) - Whether to remove Internet Explorers HTML conditional comments or not.
		 * @param removeJSCondComp			Optional (default=false) - Whether to remove Internet Explorers JavasScript conditional compilation comments or not.
		 * @return							HTML string without comments
		 */
		public static function removeComments( html : String, removeHTMLCondComment : Boolean = false, removeJSCondComp : Boolean = false ) : String
		{
			var modeSingleQuote		: Boolean = false;
			var modeDoubleQuote		: Boolean = false;
			var modeRegExp			: Boolean = false;
			var modeBlockComment	: Boolean = false;
			var modeLineComment		: Boolean = false;
 
			var modeJSCondComp		: Boolean = false;
 
			var modeHTMLComment		: Boolean = false;
			var modeHTMLCondComment	: Boolean = false;
 
			var vector : Vector.<String> = Vector.<String>( ( '__' + html + '__' ).split( '' ) );
				vector.fixed = true;
 
			var c : int = vector.length;
 
			for ( var i : int = 0; i < c; i++ )
			{
				var string : String = vector[ i ];
 
				if ( modeRegExp )
				{
					if ( string === '/' && vector[ i - 1 ] !== '\\' )
					{
						modeRegExp = false;
					}
					continue;
				}
 
				if ( modeSingleQuote )
				{
					if ( string === "'" && vector[ i - 1 ] !== '\\' )
					{
						modeSingleQuote = false;
					}
					continue;
				}
 
				if ( modeDoubleQuote )
				{
					if ( string === '"' && vector[ i - 1 ] !== '\\')
					{
						modeDoubleQuote = false;
					}
					continue;
				}
 
				if ( modeBlockComment )
				{
					if ( string === '*' && vector[ i + 1 ] === '/')
					{
						vector[ i + 1 ] = '';
						modeBlockComment = false;
					}
					vector[ i ] = '';
					continue;
				}
 
				if ( modeLineComment)
				{
					string = vector[ i + 1 ];
					if ( string === '\n' || string === '\r' )
					{
						modeLineComment = false;
					}
					vector[ i ] = '';
					continue;
				}
 
				if ( modeJSCondComp )
				{
					if ( vector[ i - 2 ] === '@' && vector[ i - 1 ] === '*' && string === '/' )
					{
						modeJSCondComp = false;
					}
					continue;
				}
 
				if ( modeHTMLComment )
				{
					// --> 
					if ( string === '-' && vector[ i + 1 ] === '-' && vector[ i + 2 ] === '>' )
					{
						vector[ i + 1 ] = '';
						vector[ i + 2 ] = '';
 
						modeHTMLComment = false;
					}
					vector[ i ] = '';
					continue;
				}
 
				if ( modeHTMLCondComment )
				{
					if ( string === 'i' && vector[ i + 1 ] === 'f' && vector[ i + 2 ] === ']' && vector[ i + 3 ] === '-' && vector[ i + 4 ] === '-' && vector[ i + 5 ] === '>' ) // if]--> 
					{
						modeHTMLCondComment = false;
						i += 5;
					}
					continue;
				}
 
				if ( string === '<' && vector[ i + 1 ] === '!' && vector[ i + 2 ] === '-' && vector[ i + 3 ] === '-' ) // <!--
				{
					if ( ! removeHTMLCondComment && vector[ i + 4 ] === '[' && vector[ i + 5 ] === 'i' && vector[ i + 6 ] === 'f' ) // <!--[if
					{
						modeHTMLCondComment = true;
					}
					else
					{
						vector[ i ] = '';
						modeHTMLComment = true;
					}
				}
 
				if ( string === '"' )
				{
					modeDoubleQuote = true;
					continue;
				}
 
				if ( string === "'" )
				{
					modeSingleQuote = true;
					continue;
				}
 
				if ( string === '/' )
				{
					if ( ! removeJSCondComp && vector[ i + 1 ] === '*' && vector[ i + 2 ] === '@' )
					{
						modeJSCondComp = true;
						continue;
					}
 
					if ( vector[ i + 1 ] === '*' )
					{
						vector[ i ] = '';
 
						modeBlockComment = true;
						continue;
					}
 
					if ( vector[ i + 1 ] === '/' )
					{
						vector[ i ] = '';
						modeLineComment = true;
						continue;
					}
 
					for ( var k : int = i - 1; true; k-- )
					{
						string = vector[ k ];
						if ( string !== ' ' )
						{
							if ( string === '=' )
							{
								modeRegExp = true;
							}
							break;
						}
					}
				}
			}
			return vector.join( '' ).slice( 2, -2 );
		}
	}
}

Let me know if I made a mistake but it seems to work quite well. Or do you know a good library that already does this kind of optimizations?

Well, that’s it for now. Have fun coding!

Linked List Performance Test using Getter or Interface

Another test regarding linked lists. Since I heavily use interfaces within the frameworks I’m working on I tried to define interfaces for my linked list items. I would love to use an abstract interface but with the full linked list speed. Seems not possible. If you create an interface you need to use at least one getter function which kills performance.

I compared execution time using this code:

package
{
	import flash.utils.getTimer;
	import flash.events.Event;
	import flash.display.Sprite;
 
	public final class LinkedListPerformanceCheck extends Sprite
	{
		private var _firstItemWithPublicVar : ItemWithPublicVar;
		private var _firstFinalItemWithPublicVar : FinalItemWithPublicVar;
		private var _firstItemWithGetter : ItemWithGetter;
		private var _firstFinalItemWithGetter : FinalItemWithGetter;
		private var _firstIItemWithGetter : IItemWithGetter;
		private var _firstIFinalItemWithGetter : IFinalItemWithGetter;
 
		private var _c : int;
		private var _i : int;
 
		public function LinkedListPerformanceCheck()
		{
			loaderInfo.addEventListener( Event.COMPLETE, run );
		}
 
		private function run( event : Event ) : void
		{
			_c = 5000000;
			_i = 0;
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}
 
		private function onEnterFrame( event : Event ) : void
		{
			switch( _i++ )
			{
				case 0 : createItemWithPublicVarTest(); break;
				case 1 : createFinalItemWithPublicVarTest(); break;
				case 2 : createItemWithGetterTest(); break;
				case 3 : createFinalItemWithGetterTest(); break;
				case 4 : createImplementedIItemWithGetterTest(); break;
				case 5 : createImplementedIFinalItemWithGetterTest(); break;
				case 6 : break; // wait
				case 7 : break; // wait
				case 8 : break; // wait
				case 9 : break; // wait
				case 10 : runItemWithPublicVarTest(); break;
				case 11 : runFinalItemWithPublicVarTest(); break;
				case 12 : runItemWithGetterTest(); break;
				case 13 : runFinalItemWithGetterTest(); break;
				case 14 : runImplementedIItemWithGetterTest(); break;
				case 15 : runImplementedIFinalItemWithGetterTest(); break;
				case 16 : runItemWithPublicVarTest(); break; // again - just to make sure...
				case 17 : runFinalItemWithPublicVarTest(); break; // again - just to make sure...
				default: removeEventListener( Event.ENTER_FRAME, onEnterFrame );
			}
		}
 
		private function createItemWithPublicVarTest() : void
		{
			var item : ItemWithPublicVar = _firstItemWithPublicVar = new ItemWithPublicVar();
			for ( var i : int = 0; i < _c; i++ ) { item = item.next = new ItemWithPublicVar(); }
		}
 
		private function runItemWithPublicVarTest() : void
		{
			var item : ItemWithPublicVar = _firstItemWithPublicVar;
			var startTime : int = getTimer();
			while( item ){ item = item.next; }
			var duration : int = getTimer() - startTime;
			trace("LinkedListPerformanceCheck.runItemWithPublicVarTest( " +duration + " )");
		}
 
		private function createFinalItemWithPublicVarTest() : void
		{
			var item : FinalItemWithPublicVar = _firstFinalItemWithPublicVar = new FinalItemWithPublicVar();
			for ( var i : int = 0; i < _c; i++ ) { item = item.next = new FinalItemWithPublicVar(); }
		}
 
		private function runFinalItemWithPublicVarTest() : void
		{
			var item : FinalItemWithPublicVar = _firstFinalItemWithPublicVar;
			var startTime : int = getTimer();
			while( item ){ item = item.next; }
			var duration : int = getTimer() - startTime;
			trace("LinkedListPerformanceCheck.runFinalItemWithPublicVarTest( " +duration + " )");
		}
 
		private function createItemWithGetterTest() : void
		{
			var item : ItemWithGetter = _firstItemWithGetter = new ItemWithGetter();
			for ( var i : int = 0; i < _c; i++ ) { item = item.next = new ItemWithGetter(); }
		}
 
		private function runItemWithGetterTest() : void
		{
			var item : ItemWithGetter = _firstItemWithGetter;
			var startTime : int = getTimer();
			while( item ){ item = item.next; }
			var duration : int = getTimer() - startTime;
			trace("LinkedListPerformanceCheck.runItemWithGetterTest( " +duration + " )");
		}
 
		private function createFinalItemWithGetterTest() : void
		{
			var item : FinalItemWithGetter = _firstFinalItemWithGetter = new FinalItemWithGetter();
			for ( var i : int = 0; i < _c; i++ ) { item = item.next = new FinalItemWithGetter(); }
		}
 
		private function runFinalItemWithGetterTest() : void
		{
			var item : FinalItemWithGetter = _firstFinalItemWithGetter;
			var startTime : int = getTimer();
			while( item ){ item = item.next; }
			var duration : int = getTimer() - startTime;
			trace("LinkedListPerformanceCheck.runFinalItemWithGetterTest( " +duration + " )");
		}
 
		private function createImplementedIItemWithGetterTest() : void
		{
			var item : IItemWithGetter = _firstIItemWithGetter = new ImplementedIItemWithGetter();
			for ( var i : int = 0; i < _c; i++ ) { item = item.next = new ImplementedIItemWithGetter(); }
		}
 
		private function runImplementedIItemWithGetterTest() : void
		{
			var item : IItemWithGetter = _firstIItemWithGetter;
			var startTime : int = getTimer();
			while( item ){ item = item.next; }
			var duration : int = getTimer() - startTime;
			trace("LinkedListPerformanceCheck.runImplementedIItemWithGetterTest( " +duration + " )");
		}
 
		private function createImplementedIFinalItemWithGetterTest() : void
		{
			var item : IFinalItemWithGetter = _firstIFinalItemWithGetter = new ImplementedIFinalItemWithGetter();
			for ( var i : int = 0; i < _c; i++ ) { item = item.next = new ImplementedIFinalItemWithGetter(); }
		}
 
		private function runImplementedIFinalItemWithGetterTest() : void
		{
			var item : IFinalItemWithGetter = _firstIFinalItemWithGetter;
			var startTime : int = getTimer();
			while( item ){ item = item.next; }
			var duration : int = getTimer() - startTime;
			trace("LinkedListPerformanceCheck.runImplementedIFinalItemWithGetterTest( " +duration + " )");
		}
	}
}
 
class ItemWithPublicVar { public var next : ItemWithPublicVar; }
 
final class FinalItemWithPublicVar { public var next : FinalItemWithPublicVar; }
 
class ItemWithGetter
{
	private var _next : ItemWithGetter;
	public function get next() : ItemWithGetter { return _next; }
	public function set next( value : ItemWithGetter ) : void { _next = value; }
}
 
final class FinalItemWithGetter
{
	private var _next : FinalItemWithGetter;
	public function get next() : FinalItemWithGetter { return _next; }
	public function set next( value : FinalItemWithGetter ) : void { _next = value; }
}
 
interface IItemWithGetter
{
	function get next() : IItemWithGetter;
	function set next( value : IItemWithGetter ) : void;
}
 
class ImplementedIItemWithGetter implements IItemWithGetter
{
	private var _next : IItemWithGetter;
	public function get next() : IItemWithGetter { return _next; }
	public function set next( value : IItemWithGetter ) : void { _next = value; }
}
 
interface IFinalItemWithGetter
{
	function get next() : IFinalItemWithGetter;
	function set next( value : IFinalItemWithGetter ) : void;
}
 
final class ImplementedIFinalItemWithGetter implements IFinalItemWithGetter
{
	private var _next : IFinalItemWithGetter;
	public function get next() : IFinalItemWithGetter { return _next; }
	public function set next( value : IFinalItemWithGetter ) : void { _next = value; }
}

Result:

LinkedListPerformanceCheck.runItemWithPublicVarTest( 86 )
LinkedListPerformanceCheck.runFinalItemWithPublicVarTest( 88 )
LinkedListPerformanceCheck.runItemWithGetterTest( 1247 )
LinkedListPerformanceCheck.runFinalItemWithGetterTest( 1148 )
LinkedListPerformanceCheck.runImplementedIItemWithGetterTest( 1161 )
LinkedListPerformanceCheck.runImplementedIFinalItemWithGetterTest( 1160 )
LinkedListPerformanceCheck.runItemWithPublicVarTest( 64 )
LinkedListPerformanceCheck.runFinalItemWithPublicVarTest( 62 )

Using public variables instead of getter functions is much faster. To make sure memory allocation doesn’t kill performance I repeated the public variables test at the end. Even faster.

Is there a way to define Interfaces for public variables?

FFK11 – beyond tellerrand notes

I’m still not sure if “beyond tellerrand” is a subtitle or will become the new brand for the one and only Flashforum conference here in germany. Sascha Wolter and Marc Thiele did a great job as always and even I never went to another conference, FFK seems still to be a special one. Again I was lucky listening to some really cool stuff.

Interestingly most of my last year notes are still up to date, which doesn’t help if you don’t keep them in mind. So I think it’s a good idea to read my own notes from time to time. ;)

That’s what I became aware of this time:

AIR 2.6

  • Lee Brimelow mentioned AIR 2.6 on iOS is faster than on Android! Yeah.
  • Adobe is working hard on increasing performance near to native code.
  • Installing a runtime seems to be annoying for Android users, so Adobe is thinking about compiling for Android like for iOS. Which might bring some extra performance as well.
  • I asked Lee Brimelow about AIR on Windows Phone 7 and he said something like “Adobe is currently not working on that and it would be a lot of work”. Well, of course Microsoft is not interested having AIR on their system, but it would be a really important platform for all of us. So in my opinion Adobe at least has a small team checking out what’s possible. What do you think about Windows Phone 7 and AIR support?

Flex Builder 4.5

  • You can get Flex SDK 4.5 already, but there’s also a way to join the private pre-release program to get early access to Flex Builder 4.5. Just visit http://www.surveymonkey.com/s/flexprerelease. They are working on some useful stuff and it might be a good idea to test some of the new features.
  • Deepa Supramaniam mentioned spark MXML skins have performance issues on mobile devices. That’s why they created pure ActionScript spark skins for mobile applications. Hopefully, within the private beta Flex SDKs, we’ll find some great improvements: http://www.riagora.com/2011/03/preview-of-flex-on-ios/.

NUI

  • Wolfgang Henseler (Talk I missed at FFK10) opened my mind according NUI.
  • Don’t think of apps on mobile devices but of services allowing you reaching your goal faster than a website.
  • Think about providing services, combining them, rethinking them.
  • Use less design and let objects provide their functionalities (e.g. tap on image to get some options).
  • My brain is very busy with that NUI thing. E. g. he mentioned the Siri iPhone app. It’s not about how it looks like (not so cool) but how it works! Combining lots of possibilities with a really easy interface (speech) . He was also talking about “in body technology” (sensors and stuff like that.) and http://www.tedmed.com/
  • After Dennis Ippels Kinect (OpenKinect / http://www.primesense.com/) introduction I finally know something about the magic behind this device as well.

Molehill

Additional useful links:

SWF meta tag

I’m always searching for it so here is a useful blogpost.

As the latest FDT version ( Beta 4 ) does not set compiler arguments concerning SWF-size, framerate etc. by default I started using SWF-Metatags to specify these settings.

I’m talking of these things:

[SWF(width="910", height="610", backgroundColor="#FFFFFF", frameRate="40")]

Just set this tag above your class opening in the one you’re going to compile. Here is a complete list of (useful) arguments you can pass in:

width
widthPercent
height
heightPercent
scriptTimeLimit
frameRate
backgroundColor

So basically this is the fastest way to set up common compile-arguments to your project. By the way there is no Flex-Framework required – these tags also work in pure AS3 projects.

Date.time vs. Date.setTime() getTime() performance

Using Date.setTime() and Date.getTime() is faster than Date.time.

My quick speed test to determine the fastest way to get and set time values on Date objects:

var date : Date = new Date();
var max : Number = date.time;
 
var start : int;
var end : int;
var time : Number;
var times : Vector.<Number> = new Vector.<Number>(); 
 
var c : int = 1000000;
for ( var i : int = 0; i < c; i++ ) { times.push( Math.round( max * Math.random() ) ); }
 
start = getTimer();
for ( i = 0; i < c; i++ ) { date.time = times[ i ]; };
end = getTimer();
trace( "date.time = times[ i ];", end - start );
 
start = getTimer();
for ( i = 0; i < c; i++ ) { date.setTime( times[ i ] ); };
end = getTimer();
trace( "date.setTime( times[ i ] );", end - start );
 
start = getTimer();
for ( i = 0; i < c; i++ ) { time = date.time; };
end = getTimer();
trace( "time = date.time;", end - start );
 
start = getTimer();
for ( i = 0; i < c; i++ ) { time = date.getTime(); };
end = getTimer();
trace( "time = date.getTime();", end - start );
 
// Result:
//
// date.time = times[ i ];		381
// date.setTime( times[ i ] );	270
// time = date.time;			184
// time = date.getTime();		65

Linked List Performance Test

I was thinking about not creating linked lists for every data type I use but using one abstract data structure containing my specific data types.

So I wrote this short performance check:

package de.superclass 
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.utils.getTimer;
 
	public final class LinkedListTest extends Sprite 
	{
		private var _abstractItemLinkedList : AbstractItem;
		private var _dataItemLinkedList : DataItem;
		private var _vector : Vector.<DataItem>;
 
		public function LinkedListTest()
		{
			loaderInfo.addEventListener( Event.COMPLETE, onLoaderInfoComplete );
		}
 
		private function onLoaderInfoComplete( event : Event ) : void 
		{
			createData( );
 
			var timer : Timer = new Timer( 3000, 1 );
				timer.addEventListener( TimerEvent.TIMER_COMPLETE, onTimerComplete );
				timer.start();
		}
 
		private function onTimerComplete(event : TimerEvent) : void 
		{
			runTest( );
		}
 
		private function createData() : void 
		{
			var vector : Vector.<DataItem> = _vector = new Vector.<DataItem>();
			for ( var i : int = 0; i < 5000000; i++ )
			{
				var dataItem : DataItem = new DataItem();
					dataItem.index = i;
 
				vector.push( dataItem );
 
				var abstractItem : AbstractItem = new AbstractItem();
					abstractItem.data = dataItem;
 
				if ( i == 0 )
				{
					_dataItemLinkedList = dataItem;
					_abstractItemLinkedList = abstractItem;
				}
				else
				{
					previousDataItem.next = dataItem;
					previousAbstractItem.next = abstractItem;
				}
 
				var previousDataItem : DataItem = dataItem;
				var previousAbstractItem : AbstractItem = abstractItem;
			}
		}
 
		private function runTest() : void 
		{
			var dataItem : DataItem;
			var startTime : int;
 
			var vector : Vector.<DataItem> = _vector;
			var c : int = vector.length;
 
			startTime = getTimer();
			for ( var i : int = 0; i < c; i++ )
			{
				dataItem = vector[ i ];
				dataItem.index = dataItem.index;
			}
 
			var durationVector : int = getTimer() - startTime;
 
			startTime = getTimer();
			dataItem = _dataItemLinkedList;
			while ( dataItem )
			{
				dataItem.index = dataItem.index;
				dataItem = dataItem.next;
			}
 
			var durationDataItemLinkedList : int = getTimer() - startTime;
 
			startTime = getTimer( );
			var abstractItem : AbstractItem = _abstractItemLinkedList;
			while ( abstractItem )
			{
				dataItem = abstractItem.data;
				dataItem.index = dataItem.index;
				abstractItem = abstractItem.next;
			}
 
			var durationAbstractItemLinkedList : int = getTimer() - startTime;
 
			startTime = getTimer( );
			abstractItem = _abstractItemLinkedList;
			while ( abstractItem )
			{
				dataItem = DataItem( abstractItem.data );
				dataItem.index = dataItem.index;
				abstractItem = abstractItem.next;
			}
 
			var durationAbstractItemLinkedListWithCast : int = getTimer() - startTime;
 
			startTime = getTimer( );
			abstractItem = _abstractItemLinkedList;
			while ( abstractItem )
			{
				dataItem = abstractItem.data as DataItem;
				dataItem.index = dataItem.index;
				abstractItem = abstractItem.next;
			}
 
			var durationAbstractItemLinkedListWithAs : int = getTimer() - startTime;
 
			trace( "Durations:" );
			trace( "Vector", durationVector );
			trace( "DataItem LinkedList", durationDataItemLinkedList );
			trace( "AbstractItem LinkedList", durationAbstractItemLinkedList );
			trace( "AbstractItemLinkedList Using Cast", durationAbstractItemLinkedListWithCast );
			trace( "AbstractItemLinkedList Using As", durationAbstractItemLinkedListWithAs );
		}
	}
 
}
 
class AbstractItem
{
	public var data : *;
	public var next : AbstractItem;
}
 
class DataItem
{
	public var index : int;
	public var next : DataItem;
}

Results on my MacBook Pro i7:

Durations:
Vector 91
DataItem LinkedList 53
AbstractItem LinkedList 76
AbstractItemLinkedList Using Cast 108
AbstractItemLinkedList Using As 121

Alsways use explicit implemented linked lists!