【问题标题】:AS3 for loop performance difference on validationAS3 for 验证时的循环性能差异
【发布时间】:2012-09-28 00:09:41
【问题描述】:

我刚从同事那里听说,直接在验证中使用长度属性,比将值分配给变量的性能要低:

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。

【问题讨论】:

  • true ,因为每个循环检查'array.length'值。
  • 确实如此,但并不那么重要,因为您通常会在循环中执行许多其他“引用”。如果您不喜欢它,只需将长度保存到临时变量中: var l = array.length;

标签: actionscript-3


【解决方案1】:

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

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

结果如下:

  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 函数来检索长度值。 如果你继续详细的测试,你会发现函数调用花费了宝贵的时间。 当您需要更高的性能时,您有时不得不依赖内联代码。 几乎每次都是这样。那是因为处理器必须“跳转”到代码中的不同区域、创建新范围以及一些其他原因。

Why is inlining considered faster than a function call?

如果您检查向量的长度实现,您会发现它只是一个公共成员,与数组(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;
}

【讨论】:

  • 所以,速度变慢的主要原因是数组实现中的那些调用:
  • public function get length():uint public function set length(value:uint):void
  • 是的,由于语法糖,长度看起来像一个公共变量,你实际上是在调用一个比使用 int 变量慢的函数。尝试循环并运行一个空函数 100,000,000 次与执行 j++ 100,000,000 次。
  • 另外,记住向量仍然稍微慢一些,因为使用一个 int var 作为另一个对象的成员(参见测试 5-6)比使用一个作为当前对象成员的方法慢属于。(测试2)
【解决方案2】:

你的朋友是对的,但那 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!!!
}

【讨论】:

  • 那么,性能差异将基于什么?数组内容?第一个循环大大减慢的最坏情况是什么?
  • 好的,我下班后在家试试,trace 有什么问题?
  • trace 在计算上需要很长时间,并且与它花费的时间不一致(因此一次迭代可能需要比下一次迭代更长的时间)。在某个时候继续尝试,您可能会明白我的意思。
猜你喜欢
  • 2010-11-13
  • 2010-11-08
  • 2020-09-04
  • 2010-09-20
  • 2016-06-02
  • 2013-04-02
  • 2014-01-11
  • 2016-09-07
相关资源
最近更新 更多