Practical Performance Tweaks

I’ve had a little bit of time on my hands in my day job *cough choke laugh* and thought I would spend some time doing some low level improvements on the performance of my applications. Though there were some changes I made that might have improved the performance of a function by a few milliseconds on average there were a few things that I changed that made large improvements. These are the sorts of fixes that you commit to memory and just always keep them as time moves forward.

I’m not talking about making sure that you removeEventListeners and nullify unused objects for garbage collection, that is a given. These are common tweaks that people could use in every application but often we don’t because we suspect that the performance hit isn’t as big as it is. Here we will look at how big that hit actually is.

For all these tests I used Grant Skinner’s Performance Testing Harness. The numbers for the tests are provided along with the source code and the test available to run. I slightly modified the Testing Harness so that I could also get the times and numbers out of each test for graphing purposes.

Array vs ArrayCollection vs ICollectionView
These tests all started when I wondered how much slower iterating through an ArrayCollection was than iterating through an Array. The numbers were staggering. In some cases, iterating through the Array was 1/50th of the time or 5000% faster.

For my test I also was curious if using an interface instead of using the actual object made a difference. As you can see from the numbers that the difference wasn’t hugely significant, but actual.

1
2
3
4
5
6
7
8
9
10
11
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.ArraysTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different array tests.                            
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
findLastArray                               81    16.20
findLastArrayCollection                   4484   896.80
findLastICollectionView                   4405   881.00
findLastVector                             106    21.20
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

One rumor that I heard was that ArrayCollections are faster at finding a specific index value than Array’s due to some sort of magical optimization. So I tested that rumor also, below are the results. As you will see, this rumor is false. Arrays are faster at accessing a specific index value than ArrayCollections.

1
2
3
4
5
6
7
8
9
10
11
12
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.ArraysTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different array tests.                            
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
findObjectAtArray                           64    12.80
findObjectAtArrayCollectionOperand        4200   840.00
findObjectAtArrayCollectionGetItemAt      1700   340.00
findObjectAtICollectionView               4151   830.20
findObjectAtVector                          84    16.80
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Already there are additional tests I am thinking about running, but on the whole you can see that using the base Array over ArrayCollection will yield large performance improvements.

However, I again want to point out that whenever you set an Array as the dataProvider of a Flex component, Flex components convert Arrays to ArrayCollections. So don’t try to get a performance increase from a component specifically by setting the dataProvider as an Array, the Array will be transformed either way.

Loops
The next section that I was going to test was the age old debate about the perfect loop: For Loops vs While Loops, iterating before the loop vs after the loop, etc etc. Luckily for me, as soon as I started looking at Grant’s testing harness, he had already created most of the test cases for me (Thanks). So the only extension to the test that I had to make was to also test against iterating before or after the loop (++i vs i++). The results can be seen below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.LoopsTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different loop structures.                        
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
forIncrementAfter                          439    87.80
forDecrementAfter                          439    87.80
forIncrementBefore                         445    89.00
forDecrementBefore                         435    87.00
*                                                        
whileIncrementAfter                        647   129.40
whileDecrementAfter                        456    91.20
whileIncrementBefore                       649   129.80
whileDecrementBefore                       456    91.20
*                                                        
doWhileIncrementAfter                      465    93.00
doWhileDecrementAfter                      480    96.00
doWhileIncrementBefore                     483    96.60
doWhileDecrementBefore                     479    95.80
*                                                        
forIn                                      623   124.60
forEachIn                                  669   133.80
forEachInUntyped                           647   129.40
forEachInPosttyped                        1255   251.00
*                                                        
arrForEach                                1722   344.40
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

What you will probably notice is that no specific loop includes massive performance improvements, though the simple for loop seemed to be the fastest on average. So take your pick and run with it.

Unrolled Loops
The next tests that I came up with was to see how efficient rolled vs unrolled loops were. The reason I felt this was important is there are times that we needlessly iterate through a loop when we know there will always be X number of elements. When this is a the case I’ve found (and the tests prove) that it is faster to just do what you need with the X elements rather than write a loop to iterate through the elements.

Remember to check the source to see the implementations of these different tests.

1
2
3
4
5
6
7
8
9
10
11
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.LoopsTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different loop structures.                        
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
rolledUpLoop                              1585   317.00
unRolledLoop                               864   172.80
rolledUpLongLoop                          1134   226.80
unRolledUpLongLoop                         803   160.60
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Length Reference Variable
This is definitely a nit picky test, but I had to know the truth! When iterating through a loop is it significantly better to get a reference to the length of the loop, or is it okay just to use array.length in each iteration. As proven, using a variable to store the length is significantly better.

1
2
3
4
5
6
7
8
9
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.LoopsTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different loop structures.                        
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
lengthReferenceTest                        512   102.40
noLengthReferenceTest                      926   185.20
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Reference Variables
When iterating through a loop I often like to pull a reference to the current value that is being evaluated in a loop. I do this mainly to make reading the code easier, but I was curious if this creation of a new reference would be harmful to performance. On top of that I was curious if giving these temporary variables a type was helpful for performance or not. Below are the results.

1
2
3
4
5
6
7
8
9
10
11
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.LoopsTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different loop structures.                        
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
noTemporaryLoopVariableUntyped            1988   397.60
noTemporaryLoopVariableTyped              1937   387.40
temporaryLoopVariableUntyped              1849   369.80
temporaryLoopVariableTyped                1897   379.40
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

As you can see I’ve figured that there isn’t a significant performance increase or loss with using temporary variables. I think I’ll keep using my temporary variables to make reading easier.

Type, Cast and toX
The next series of tests are extremely geeky tests with some startling results. Is it faster to say (object as String) or String(object) or object.toString? This test case and many others are included below for each of the base types. May this help you decide how to type and cast your variables.

The big one to notice is the amount of time it takes to String(number) instead of saying (number as String).

Included with each test is the control case of casting and typing each type to the same base type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.TypesTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different type, casting, and manipulation tests.  
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
stringToString                              96    19.20
stringCastString                            68    13.60
stringAsString                              76    15.20
objectStringToString                       153    30.60
objectStringCastString                      70    14.00
objectStringAsString                        78    15.60
numberCastString                           888   177.60
numberAsString                             104    20.80
intCastString                              229    45.80
intAsString                                 86    17.20
uIntCastString                             229    45.80
uIntAsString                                86    17.20
*                                                        
numberCastNumber                            46     9.20
numberAsNumber                             103    20.60
objectNumberCastNumber                      50    10.00
objectNumberAsNumber                        81    16.20
intCastNumber                               46     9.20
intAsNumber                                 84    16.80
uIntCastNumber                              46     9.20
uIntAsNumber                                86    17.20
stringCastNumber                            76    15.20
stringAsNumber                              84    16.80
*                                                        
uintCastUint                                45     9.00
uintAsUint                                  86    17.20
objectUintCastUint                          48     9.60
objectUintAsUint                            82    16.40
numberCastUint                              47     9.40
numberAsUint                               113    22.60
intCastUint                                 45     9.00
intAsUint                                   84    16.80
stringCastUint                              82    16.40
stringAsUint                                90    18.00
*                                                        
intCastInt                                  46     9.20
intAsInt                                    82    16.40
objectIntCastInt                            48     9.60
objectIntAsInt                              80    16.00
numberCastInt                               47     9.40
numberAsInt                                106    21.20
uintCastInt                                 46     9.20
uintAsInt                                   86    17.20
stringCastInt                               77    15.40
stringAsInt                                 88    17.60
*                                                        
arrayAsArray                                77    15.40
objectArrayAsArray                          81    16.20
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Remember when using “as”, if the conversion fails, you will receive a null rather than the value. Unless you explicitly know what the type will be, it is always good to make this check.

The “new” operator
I’ve always been told that when creating a new base type that you should use the value rather than new because it saves the need for the VM to allocate the memory in the heap. But what was the ACTUAL performance improvements. Again, the numbers are staggering. For the next tests I created each of the simple types 3 different ways.

The first way I used the new operator as such: var s:String = new String();
The second way I used the new value as such: var s:String = “”;
And the final test was to create the variable with the new operator without the final parenthesis, such as: var s:String = new String;

For every test setting the value rather than using the new operator was almost twice as fast. The biggest difference being with creating a new Array, using the [] operand instead of the new operator was almost 3 times as fast.

I should also point out that for almost each test, using the parenthesis was slower than leaving them off when creating a new variable, though this is considered bad practice. Though the difference was marginal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.TypesTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different type, casting, and manipulation tests.  
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
newUintTest                                 75    15.00
newUintValueTest                            44     8.80
newUintTestNoParams                         76    15.20
*                                                        
newIntTest                                  75    15.00
newIntValueTest                             45     9.00
newIntTestNoParams                          74    14.80
*                                                        
newNumberTest                               72    14.40
newNumberValueTest                          45     9.00
newNumberTestNoParams                       75    15.00
*                                                        
newStringTest                               71    14.20
newStringValueTest                          45     9.00
newStringTestNoParams                       74    14.80
*                                                        
newArrayTest                               711   142.20
newArrayValueTest                          249    49.80
newArrayTestNoParams                       709   141.80
*                                                        
newObjectTest                              267    53.40
newObjectValueTest                         230    46.00
newObjectTestNoParams                      263    52.60
*                                                        
newXMLTest                                1032   206.40
newXMLValueTest                           1023   204.60
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Static Methods vs Class Methods
As a side curiosity I was wanted to know if there was any performance loss or gain based on the types of functions used within a class. I was able to see that there weren’t any significant performance improvements over one type of function or another. So stick with best practices within your classes and only make methods and properties public when they absolutely have to be.

This test does not include testing whether creating the class and then accessing the function is better than a static function which does not need to instantiate a class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.ClassTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different class method tests.                    
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
publicMethod                               141    28.20
privateMethod                              143    28.60
protectedMethod                            144    28.80
publicStaticMethod                         141    28.20
privateStaticMethod                        142    28.40
protectedStaticMethod                      141    28.20
publicInternalMethod                       142    28.40
publicFinalMethod                          144    28.80
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Using “this” and Other Class Curiosities
This next section includes many different tests to improve my class creation.

Setting the default values
In this test I was curious if it is better to initially set a variable in the constructor or at the variable, and also if setting the variable defaults inside the class vs outside the class was better. What I found is that it is faster to initially just set the variables to their default value rather than making this change in the constructor.

1
2
3
4
5
6
7
8
9
10
11
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.VariablesTest (5 iterations)  
Player version: MAC 10,0,32,18 (debug)
Test different variables and vo based tests.          
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
defaultVarsSetVO                           400    80.00
varsSetInConstructorVO                     490    98.00
varsSetOutsideConstructorThisVO            499    99.80
varsSetOutsideConstructorNoThisVO          496    99.20
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Is using “this” wrong?
Checking whether it was faster to use the “this” identifier was harmful to performance I concluded that there was no significant performance loss or gain in using the “this” identifier.

1
2
3
4
5
6
7
8
9
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.VariablesTest (5 iterations)  
Player version: MAC 10,0,32,18 (debug)
Test different variables and vo based tests.          
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
thisToString                              2395   479.00
noThisToString                            2441   488.20
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Getters and Setters vs Direct Accessors
Lots of good ole’ boy Java developers will tell you that every property has to have a getter and setter. Other programmers will say that this is only necessary if there is some logic that needs to run in the get/set process. Now you can see that by using a getter/setter when a direct accessor can be used is wasteful for performance and typing. Therefore, only create getters/setters when it is important to run some logic in the getting/setting process.

1
2
3
4
5
6
7
8
9
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.VariablesTest (5 iterations)  
Player version: MAC 10,0,32,18 (debug)
Test different variables and vo based tests.          
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
noGetSetAccessors                         2382   476.40
getSetAccessors                           2786   557.20
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Size really doesn’t matter… in variables.
Finally, the stigma from writing Javascript applications always left me feeling guilty about using full words when a letter can do. In Javascript this was a performance issue and caused many programmers to use single incomprehensible letters instead of easy to read words. To elevate my guilt I decided to test if using long variable names made any difference at runtime. The answer is no. No savings, no loss. Now, guilt free, I will always use well written variable names instead of short single letter descriptors.

1
2
3
4
5
6
7
8
9
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.VariablesTest (5 iterations)  
Player version: MAC 10,0,32,18 (debug)
Test different variables and vo based tests.          
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
shortVarNames                               45     9.00
longVarNames                                45     9.00
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

If..Then vs Switch..Case
Like loops, developers argue which logical test is better: if..then vs switch..case. Pitting the two against each other, apples vs apples, I’ve found that if..then statements are slightly faster than switch..case statements. I will still use each statement where appropriate though, but it is good to know in the situations that I have the choice that I should use if..then statements.

1
2
3
4
5
6
7
8
9
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.LogicalTest (5 iterations)    
Player version: MAC 10,0,32,18 (debug)
Test different logical structure tests.                
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
switchCaseStatement                        411    82.20
ifThenElseStatement                        378    75.60
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Math.max() vs Math.max.apply()
One little known secret to AS3 is the apply method. Most classes have more than one way to skin a cat, and the apply method is that function. In the case of Math.max (or Math.min) I was curious how much faster the apply method was vs logically finding the largest (or smallest) value. The numbers were staggering. From now on I will be using the apply method whenever possible as it is a very low level and fast function.

1
2
3
4
5
6
7
8
9
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
com.unitedmindset.tests.ApplyTest (5 iterations)      
Player version: MAC 10,0,32,18 (debug)
Test different apply method tests.                    
–––––––––––––––––––––––––––––––––––––––––––––––––––––––
method..................................ttl ms...avg ms
findMaxLogically                          2093   418.60
findMaxApply                               131    26.20
–––––––––––––––––––––––––––––––––––––––––––––––––––––––

Final Thoughts
Obviously the things in this list I see as some of the greatest offenders and also the easiest to fix. It all comes down to you and which changes you feel comfortable making in your daily coding practices. Some of the more “nit picky” performance changes I feel can effect code readability and reusability, and which you implement into your code is up to you and your coding practices. Hopefully this ends the debate between some techniques as being the end all be all technique.

If there are other coding techniques that have left you wondering which is better, let me know and I’ll add it in. I’m sure over time I’ll revisit these test cases.

The Test, Run It For Yourself Or View Source
If you are having a hard time viewing this test app go get flash. Or try the direct link, <a href="http://unitedmindset.com/jonbcampos/wp-content/flex_content/performance/PerformanceApp.html">Performance Test App</a>
View Source (or right click swf)

  • Share/Bookmark

Comments (15)

[...] This post was mentioned on Twitter by Grant Skinner, Joseph Burchett. Joseph Burchett said: Practical Performance Tweaks in Flex http://bit.ly/5KEANi [...]

uberVU - social commentsDecember 1st, 2009 at 12:31 am

Social comments and analytics for this post…

This post was mentioned on Twitter by retrogamer4ever: Practical Performance Tweaks in Flex http://bit.ly/5KEANi...

krdrDecember 1st, 2009 at 1:59 am

Interesting results, especially for apply().

I have a question about if..else and switch comparison. I think that speed of if..else structure largely depends on the place of correct condition (condition that gives a “true”). Can you confirm and test that? When I’m in question whether to use if or switch, I examine a conditions. If probability for some conditions to be true is greater than for rest, I’ll go with if and that conditions would be on the top of if structure. If probability for large numbers of conditions is same (or similar), I would go for switch.

So can you check on this

Devin ReimerDecember 1st, 2009 at 3:11 am

The Math.max.apply() results are shocking, I will definitely have to remember that one.

Jonathan CamposDecember 1st, 2009 at 6:39 am

@krdr That would be interesting tests. Finding in first position and finding in last position. You will notice from my tests that I put the if..then and switch..case statements in the same order and randomized the order that the true result would be found so that it would equalize the found first, second, or last. But that would be an interesting set of tests to add, just to see the results.

Twitted by nothingagencyDecember 2nd, 2009 at 6:42 am

[...] This post was Twitted by nothingagency [...]

Germain LECOURTOISDecember 2nd, 2009 at 7:19 am

Function.apply … I’m not the first who is surprised…
How did you find this tips ?

Thanks for this benchmark !

sascha/hdrsDecember 2nd, 2009 at 7:39 am

Some very interesting results here, thanks for the tests! According to this site http://wiki.joa-ebert.com/index.php/Casting wrapped cast is faster than cast with ‘as’.

One case that would still interest me is if a Math function stored in a class property is faster than using it direclty. E.g.

private var _mathRandom:Function = Math.random;

somewhere down the line:
var n:Numer = _mathRandom();

instead of:
var n:Numer = Math.random();

sascha/hdrsDecember 2nd, 2009 at 8:53 am

Nevermind. After some quick tests it turns out that calling Math functions directly is much faster than storing them in a var or even const.

Jonathan CamposDecember 2nd, 2009 at 9:11 am

@Germain It wasn’t that I found these stats. I created these tests to run because I wanted to see not what people said / assumed was faster, but instead what actually was faster. You can view source and see the tests and run them for yourself. I will be releasing another set of tests at some point as performance testing has been interesting and fun.

Jonathan CamposDecember 2nd, 2009 at 9:12 am

@sascha That was my guess too. Making the additional reference to a static function seemed more like another hoop to jump through rather than a point of improvement.

Most Tweeted Articles by Flash ExpertsDecember 26th, 2009 at 6:26 am

[...] 2010 (25/03/2010) 4 Likes Adobe: Flex 4 beta in a Week 4 Likes Practical Performance Tweaks | The World In A State of Flex I've had a little bit of time on my hands in my day job *cough choke laugh* and thought I would [...]

georgiFebruary 12th, 2010 at 8:16 am

The NumberAsString is wrong — it should be excluded from the tests.
That’s because:
41.5 as String is clearly null and not “41.5″.

‘as’ does not convert. It just checks the RTTI and returns non-null only if the object is an instance of the class argument.

Jonathan CamposFebruary 12th, 2010 at 10:52 am

I agree that you receive Null. I did make sure to include that warning when using “as”. But I left it in there so I didn’t get people saying I forgot NumberAsString as one of the permutations. One of those situations where you’re damned if you do, damned if you don’t.

Leave a comment

Your comment