While on IRC, an argument arose on how to bring a component to the front of all other children for a specific container. I’ve done this in the past using ‘setElementIndex’ (the Flex 4 version of ‘setChildIndex’) which you just need to specify the child and the new index. Another person in the channel suggested to just use ‘addElementAt’ since it does the same thing. I decided to test which one is faster in both Flex 3 and 4.
After spending a bit of time Googling for which one is faster, nothing came up that clearly stated it. The test itself was simple enough; Create 1000 buttons on the screen, then add half of them to the front using ‘setElementIndex’ or ‘setChildIndex’ and the other using ‘addElementAt’ or ‘addChildAt’; of course measuring the time spent doing these operations.
Here’s the code used for the test. I could have used FlexUnit, but that seemed more complicated than needed.
Flex 3 Version
< ?xml version="1.0" encoding="utf-8"?>
<mx:application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="onCreationComplete()">
<mx:script>
< ![CDATA[
import flash.utils.getTimer;
import mx.controls.Button;
// Holds all the buttons currently displayed
private var _buttons:Array = [];
// Holds the timestamps for time calculations
private var _timers:Dictionary = new Dictionary();
// Holds the functions to call
private var _testFunctions:Array = [testSetIndex, testAddRemove];
private function onCreationComplete():void
{
// Create all the buttons on screen
createButtons();
}
/**
* When a test is done, this method is called to stop the timer
* and start the next test on the next frame.
*
* @param name:String the name of the timer to stop
*/
private function testDone(name:String):void
{
// Stops timer
stopTimer(name);
// Call the next test to run
if(this._testFunctions.length > 0)
{
callLater(this._testFunctions.pop() as Function);
}else{
trace(‘Test Completed’);
}
}
/**
* Creates all the buttons on the screen for testing
*/
private function createButtons():void
{
var b:Button;
startTimer(‘Creation’);
for(var i:int = 0; i<1000; i++)
{
b = new Button();
b.label = ‘Button ‘+i;
this._buttons[i] = b;
addChild(b);
}
// Start the testing process
callLater(testDone, ['Creation']);
}
/**
* Tests the ‘setChildIndex’ method on the bottom half of the buttons.
*/
private function testSetIndex():void
{
startTimer(‘testSetIndex’);
for(var i:int = int(this._buttons.length/2), len:int = this._buttons.length; i<len ; i++)
{
setChildIndex(this._buttons[i], 0);
}
// End test
callLater(testDone, ['testSetIndex']);
}
/**
* Tests the ‘addChildAt’ method on the bottom half of the buttons.
*/
private function testAddRemove():void
{
startTimer(‘testAddElementAt’);
for(var i:int = int(this._buttons.length/2), len:int = this._buttons.length; i<len; i++)
{
addChildAt(this._buttons[i], 0);
}
// End test
callLater(testDone, ['testAddElementAt']);
}
/**
* Starts a timer with a specified name
*
* @param name:String the name of the timer to start
*/
private function startTimer(name:String):void
{
this._timers[name] = getTimer();
}
/**
* Stops a timer with a specified name
*
* @param name:String the name of the timer to start
*/
private function stopTimer(name:String):void
{
if(this._timers[name])
{
trace("Timer ‘" + name + "’: "+ (getTimer() – this._timers[name]));
}else{
trace("Timer name ‘" + name + "’ cannot be found");
}
}
]]>
</len></mx:script>
</mx:application>
Flex 4 Version
< ?xml version="1.0" encoding="utf-8"?>
<s:application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="onCreationComplete()">
<fx:script>
< ![CDATA[
import flash.utils.getTimer;
import spark.components.Button;
// Holds all the buttons currently displayed
private var _buttons:Array = [];
// Holds the timestamps for time calculations
private var _timers:Dictionary = new Dictionary();
// Holds the functions to call
private var _testFunctions:Array = [testSetIndex, testAddRemove];
private function onCreationComplete():void
{
// Create all the buttons on screen
createButtons();
}
/**
* When a test is done, this method is called to stop the timer
* and start the next test on the next frame.
*
* @param name:String the name of the timer to stop
*/
private function testDone(name:String):void
{
// Stops timer
stopTimer(name);
// Call the next test to run
if(this._testFunctions.length > 0)
{
callLater(this._testFunctions.pop() as Function);
}else{
trace(‘Test Completed’);
}
}
/**
* Creates all the buttons on the screen for testing
*/
private function createButtons():void
{
var b:Button;
startTimer(‘Creation’);
for(var i:int = 0; i<1000; i++)
{
b = new Button();
b.label = ‘Button ‘+i;
this._buttons[i] = b;
addElement(b);
}
// Start the testing process
callLater(testDone, ['Creation']);
}
/**
* Tests the ‘setElementIndex’ method on the bottom half of the buttons.
*/
private function testSetIndex():void
{
startTimer(‘testSetIndex’);
for(var i:int = int(this._buttons.length/2), len:int = this._buttons.length; i<len ; i++)
{
setElementIndex(this._buttons[i], 0);
}
// End test
callLater(testDone, ['testSetIndex']);
}
/**
* Tests the ‘addElementAt’ method on the bottom half of the buttons.
*/
private function testAddRemove():void
{
startTimer(‘testAddElementAt’);
for(var i:int = int(this._buttons.length/2), len:int = this._buttons.length; i<len; i++)
{
addElementAt(this._buttons[i], 0);
}
// End test
callLater(testDone, ['testAddElementAt']);
}
/**
* Starts a timer with a specified name
*
* @param name:String the name of the timer to start
*/
private function startTimer(name:String):void
{
this._timers[name] = getTimer();
}
/**
* Stops a timer with a specified name
*
* @param name:String the name of the timer to start
*/
private function stopTimer(name:String):void
{
if(this._timers[name])
{
trace("Timer ‘" + name + "’: "+ (getTimer() – this._timers[name]));
}else{
trace("Timer name ‘" + name + "’ cannot be found");
}
}
]]>
</len></fx:script>
</s:application>
Flex 3 Version
Timer ‘Creation’: 1534ms
Timer ‘testAddElementAt’: 1153ms
Timer ‘testSetIndex’: 55ms
Test Completed
Flex 4 Version
Timer ‘Creation’: 4988ms
Timer ‘testAddElementAt’: 2246ms
Timer ‘testSetIndex’: 2241ms
Test Completed
As you can see, Flex 3′s version is faster to create. These results surprised me. I thought Flex 4 would have been faster because of the separation between component and skinning. I never done any comparison between Flex 3 and 4 performance and these results are hard to ignore; 325% reduction of speed on creating 1000 buttons. Adobe definitely needs to address this performance issue in Hero.
As for that, you’ll notice that there is no performance difference between the two methods in Flex 4 since they seem to both force an update to the buttons. However in Flex 3, setting the index is faster than re-adding the button since that seems to force an update while setting the index just changes the display depth.
Copyright © Thinking in Code. All Rights Reserved. Powered by Wordpress