1

I just heard from co-workers that using the length property directly in the validation, has lower performance than assigning the value to a variable:

for(var i:int=0;i<array.length;i++)
   trace(String(i));

for(var i:int=array.length-1;i>-1;i--)
   trace(String(i));

They actually claim that, the second loop will iterate over the array "up to 90% faster", is any of this true??

This question can apply for any language, but im only interested on AS3 behavior for this, especially on ArrayCollections.

Donato Szilagyi
  • 4,279
  • 4
  • 36
  • 53
Ziul
  • 883
  • 1
  • 13
  • 24
  • 1
    true , because each loop check 'array.length' value . – turbosqel Sep 28 '12 at 05:55
  • It is true, but it is not so important, because you usually do many other "referencing" inside a loop. If you don't like it, just save the length into temporary variable: var l = array.length; – Ivan Kuckir Sep 28 '12 at 09:56

2 Answers2

2

The reason for this issue is a lot more interesting than you would expect.

Examine the following code, it includes seven tests:

Here are the results:

  1. 864 - compare with literal constant int
  2. 866 - compare with int
  3. 1358 - compare with vector length (size fixed)
  4. 1376 - compre with vector length (size dynamic)
  5. 3159 - compare with object member
  6. 3152 - compare with static object member
  7. 11855 - compare with array length

Why do you suppose the last one is so terribly slow when compared to the rest? It is not because the Array re-calculates the length each time, that would be silly.

Read this:

length property: A non-negative integer specifying the number of elements in the array. This property is automatically updated when new elements are added to the array. When you assign a value to an array element (for example, my_array[index] = value), if index is a number, and index+1 is greater than the length property, the length property is updated to index+1.

The reason is in the implementation

//Implementation
public function get length():uint
public function set length(value:uint):void

The other six tests use a regular public member of a class. Array's use getter and setter functions to retrieve the length value. If you continue to elaborate the test, you will see function calls cost precious time. When you need more performance, you sometimes have to rely on inline code. That is is true almost every time. That is because the processor has to 'jump' to a different area in the code, create a new scope and some additional reasons.

Why is inlining considered faster than a function call?

If you check the length implementation for vector, you will see it is just a public member unlike array (getter and setter) functions. Getters and Setters are better for extensibility, they can make your life a lot easier if you decide to inherit from a class, setters can also prevent certain errors by checking the values. Nothing beats a public property for speed.

package regression 
{
    import flash.display.Sprite;
    import flash.utils.getTimer;
    /**
     * ...
     * @author Arthur Wulf White
     */
    public class Check_Loop_Speed_1 extends Sprite
    {
        //BIG_NUMBER == 100,000,000
        public function Check_Loop_Speed_1() 
        {
            var i : int = 0, j : int = 100000000, time : int = 0;
            var vector: Vector.<Boolean> = new Vector.<Boolean>(100000000, true),
                vect2 : Vector.<Boolean> = new Vector.<Boolean>(100000000),
                obj : Object = new TestObject(),
                arr : Array = new Array();

            arr.length = 100000000;

            //test  1
            time = getTimer();
            for (i = 0; i < 100000000; i++) { }
            trace(getTimer() - time);

            //test  2
            time = getTimer();
            for (i = 0; i < j; i++) { }
            trace(getTimer() - time);

            //test  3
            time = getTimer();
            for (i = 0; i < vector.length; i++) { }
            trace(getTimer() - time);

            //test  4
            time = getTimer();
            for (i = 0; i < vect2.length; i++) { }
            trace(getTimer() - time);

            //test  5
            time = getTimer();
            for (i = 0; i < obj.val; i++) { }
            trace(getTimer() - time);

            //test  6
            time = getTimer();
            for (i = 0; i < obj.val2; i++) { }
            trace(getTimer() - time);

            //test  7
            time = getTimer();
            for (i = 0; i < arr.length; i++) { }
            trace(getTimer() - time);
        }

    }

}

class TestObject
{
    public var      val     : uint = 100000000;
    public const    val2    : uint = 100000000;
}
Community
  • 1
  • 1
AturSams
  • 7,568
  • 18
  • 64
  • 98
  • so, the main reason for the slow down are those calls in the array implementation: – Ziul Sep 28 '12 at 15:56
  • public function get length():uint public function set length(value:uint):void – Ziul Sep 28 '12 at 15:57
  • Yeah, length looks like a public variable because of syntactic sugar, you are actually calling a function which is slower than using a int variable. try looping and running an empty function 100,000,000 times vs. doing j++ 100,000,000 times. – AturSams Sep 28 '12 at 16:14
  • Also, remember vectors are still slightly slower because, using a int var that is a member of another object (see tests 5-6) is slower than using one that is a member the current object the methods belongs to.(test 2) – AturSams Sep 28 '12 at 16:16
1

Your friend is correct, but the 90% will not be consistent.

A way to test this:

import flash.utils.getTimer;

var btn:Sprite = new Sprite();
btn.graphics.beginFill(0);
btn.graphics.drawRect(0,0,100,50);
btn.addEventListener(MouseEvent.CLICK,test);
addChild(btn);

var array:Array = new Array();
var arraySize:int = 100000;  

for(var i:int=0;i < arraySize;i++){
    array.push(i);
}

function test(e:Event):void {
    var i:int = 0; //initialize before getTimer so all things are equal
    var curTime:Number = 0;

    curTimer = getTimer();
    for(i=0;i<array.length;i++){
        doSomething(i);
    }

    trace("First Took: ", (getTimer() - curTime) + "ms"); 

    curTime = getTimer();
    for(i=array.length-1;i>-1;i--){
        doSomething(i);
    }

    trace("Second Took: ", (getTimer() - curTime) + "ms");   
};


function doSomething(index:int):void {
    index = index * 2; //some arbitrary function - don't trace!!!
}
BadFeelingAboutThis
  • 14,445
  • 2
  • 33
  • 40
  • So, the performance difference will be based on what? array content? Whats the worse case scenario for the first loop to get greatly slowed down? – Ziul Sep 28 '12 at 00:22
  • Ok, imma try after work at home, and whats the deal with trace? – Ziul Sep 28 '12 at 00:25
  • trace takes a long time computationally, and isn't consistent with how long it takes (so one iteration could take trace substantially longer than the next). Go ahead and try it at some point and you'll likely see what I mean. – BadFeelingAboutThis Sep 28 '12 at 00:28