1

我刚刚从同事那里听说,直接在验证中使用 length 属性,其性能低于将值分配给变量:

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));

他们实际上声称,第二个循环将迭代数组“快 90%”,这是真的吗?

这个问题适用于任何语言,但我只对 AS3 行为感兴趣,尤其是 ArrayCollections。

4

2 回答 2

2

这个问题的原因比你想象的要有趣得多。

检查以下代码,它包括七个测试:

结果如下:

  1. 864 - 与文字常量 int 进行比较
  2. 866 - 与 int 比较
  3. 1358 - 与向量长度比较(大小固定)
  4. 1376 - 与向量长度比较(大小动态)
  5. 3159 - 与对象成员比较
  6. 3152 - 与静态对象成员比较
  7. 11855 - 与数组长度比较

为什么你认为最后一个比其他的慢得多?这并不是因为 Array 每次都重新计算长度,那会很愚蠢。

读这个:

length 属性:一个非负整数,指定数组中的元素个数。当新元素添加到数组中时,此属性会自动更新。当您为数组元素赋值时(例如,my_array[index] = value),如果 index 是一个数字,并且 index+1 大于 length 属性,则 length 属性会更新为 index+1。

原因在于执行

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

其他六个测试使用类的常规公共成员。Array 使用 getter 和 setter 函数来检索长度值。如果你继续详细的测试,你会发现函数调用花费了宝贵的时间。当您需要更高的性能时,您有时不得不依赖内联代码。几乎每次都是这样。那是因为处理器必须“跳转”到代码中的不同区域、创建新范围和一些其他原因。

为什么内联被认为比函数调用更快?

如果您检查向量的长度实现,您会发现它只是一个公共成员,与数组(getter 和 setter)函数不同。Getter 和 Setter 具有更好的可扩展性,如果您决定从类继承,它们可以让您的生活更轻松,setter 还可以通过检查值来防止某些错误。在速度方面,没有什么比公共财产更好的了。

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;
}
于 2012-09-28T09:37:52.450 回答
1

你的朋友是正确的,但 90% 不会是一致的。

一种测试方法:

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!!!
}
于 2012-09-28T00:15:59.590 回答