zoukankan      html  css  js  c++  java
  • AS3 代码效率优化

    AS3代码效率优化中以帧频为重要部分。帧速的调节是很灵活的。帧速较高意味着每秒钟执行的循环、代码操作会比较多,帧速低则是减小每秒钟相应的循环、代码的执行次数。有时候,降低帧速有助于提高回放视频的流畅度。就算并没有很多的代码,帧速的高低依然会直接影响到CPU使用率,这在Mac上尤其明显。帧速可以动态设置,如下:stage.frameRate = 20;

    优化你的优化行为。优化通常都需要时间,并且大多会牺牲代码的清晰度。请将注意力集中在loops、enterFrames和timers当中去。局部优化相对于整体优化,需要更少的优化计划,并且难度较低。一个优化例子是Adobe kuler。原始版本执行代码需要14s,局部优化后需要4s,整体优化之后只需要0.5s。

    下面大于号表示时间左比右更加耗时

    变量优化部分:
    #1:将变量声明在一行中,要比声明多行更好,效率更高
    var a:int = 0, b:int = 0, c:int = 0; 
    a = b = c = 0;

    var a:int=0;
    var b:int=0;
    var c:int=0;

    #2:如果你想去交换变量,但是又不想创建新的变量的时候,可以用xor 如:
    a = a^b;
    b = a^b;
    a = a^b;

    #3:强制转换类型对比
    建议使用对应的类型的变量进行比较
    同类型的比较效率高的多

    #4:尽量用短的变量名

    #get set 类变量 > 类变量(无关访问标志) > 局部变量

    # 变量类型运算速度
    一般运算情况下 uint > Number > int
    i += 1自加情况下 Number > int >= uint
    最终保存类型为Number的情况下: uint > Number >= int
    最终保存类型为int的情况下: uint > Number >= int
    最终保存类型为uint的情况下: uint > Number >= int
    最终结论:除非万不得已千万别拿uint做参数做运算对象,即便保存类型也是uint。
    还有一个结论就是:
    for循环Number数据类型++和+=1速度相当
    for循环int和uint数据类型 += 1比++快很多。

    #5:变量数据类型转换
    用unit() 或int() 代替取整运算Math.floor() 和Math.ceil()。
    比如var test:uint = uint(1.5); 
    要比var test:Number = Math.floor(1.5); 快;
    而var test:uint = uint(1.5) + 1; 
    要比var test:Number = Math.ceil(1.5); 也快。
    如果是Math.floor(),还可以用位运算( >> 0) 来代替。
    比如var test:uint = 1.5 >> 0,比unit() 或int() 更快。
    用乘 - 1来代替Math.abs() 方法。
    比如
    var nn:Number = -23;
    var test:Number = nn < 0 ? nn * -1 : nn;
    要比
    var nn:Number = -23;
    var test:Number = Math.abs(nn); 快。
    当然还有更多的优化计算的方法。
    一般来说,低级运算要比高级运算速度;
    内部方法比调用其他方法速度快。
    另外要注意的是,这些方法有的时候可能并一定适用。

    在定义局部变量的时候,一定要用关键字var来定义,因为在Flash播放器中,局部变量的运行速度更快,而且在他们的作用域外是不耗占系统资源的。
    aw附:var变量仅仅在花括号对中才有“生命”,个人认为没有系统学过编程的人容易出错的一个地方:
    awMC.onLoad = function(){
     var aw = 1;
    }
    awMC.onEnterFrame = function(){
     //不存在aw这个变量
    }

    * 类定义中的属性(如public、private类属性) 135 毫秒
    Point类对象 140 毫秒
    Rectangle类对象(x等属性) 140ms
    * Array类对象 270ms
    * Object类对象 500 ms
    动态类动态属性 550
    * Rectangle类对象(left等属性) 700
    * 自定义类getter/setter 1000 
    自定义类Function 1000

    字符串优化部分:

    #1:那怎么能让字符串连接的更快呢?看一看下面这段代码:
    time1 = getTimer();
    var tmpA = new Array();
    for(var i=0;i < 10000;i++){
     tmpA[i] = str;
    }
    var tmp1 = tmpA.join("");
    trace(getTimer() - time1);
    trace(tmp1 == tmp);
    这段代码是接在上面的代码后面的,这里思路有所不同的是,在这里先把字符串一个一个的付给数组,然后利用数组的join方面一下子将整个数组转换为字符串,你也许会想,先付给数组,然后再有数组转换,肯定会更慢.但实际上不是这样的,因为赋值语句在as里面是很快的,所以将10000个字符串付给数组花不了多长时间,而最后调用的数组的方法是as内部函数,完全由c实现的,因此肯定要比as实现快很多倍,因此整体速度跟上面的代码就相差很多了,而且随字符串增长,效率也不会明显降低,下面是执行结果:
    同样为10000个字符串连接,5个字符第二种方法只需要140ms,比直接连接字符串快10倍左右,而后面的比较输出true说明两个字符串是一样的
    而且当字符串加置10个,15个甚至更多的时候,效率并没有降低,而仍然用时140ms左右,而当字符串长度达到40的时候,用时才超过了200ms.


    数组与对象优化部分:

    #1:构造数组和对象的时候:
    new Array() > []
    new Object() > { }
    差3倍的时间
    如果你需要设置一个空数组,有一个方便的办法去选择,就是通过设置它的length属性为0
    或者你会认为这么做是不错的选择,原因是它能节省内存,但是事实上这样做的执行速度不如直接new array的效率高
    在循环语句中避免多次创建数组或对象,最好创建一次用多次更新内容替换

    #2:Array和Vector的三种插入方法
    arrOrVec.push(val);
    arrOrVec[arrOrVec.length] = val;
    arrOrVec[i] = val;//fixed length
    应使用第三种方式arr.push(i) > arr[arr.length] = i > arr[i] = i

    #3:数组的数字索引类型
    list[0] > list[int(0)]

    #4:数组排序查询等方法使用
    对于庞大的数组而言就行splice操作是比较耗成本的,要尽量避免

    #5:清空或删除数组/对象/Vector
    有时候在array或vector里跳过元素,或者将元素设为null,比delete删除这个元素更高效。
    清空array或vector里元素:new Array() > [] > arr.length = 0;

    循环语句优化部分:
    #1:嵌套循环
    多次嵌套循环效率差,所以最好保证循环在2层以内

    #2:帧事件循环函数
    如果在时间帧上的函数很长而且执行时间长,最好,把该函数分成多个小的函数执行。这样可以缩短执行时间提高效率

    读取数组
    for > while > for (in) > for each (in)
    写入数组或其他操作
    for > for (in) > while > for each (in)
    遍历数组的效率问题
    var arr:Array = new Array()
    for(var i:int=0;i<1000000;i++){
            arr.push(Math.random())
    }
    var t = getTimer();
    for(var j:String in arr){
            arr[j] = Math.random();
    }
    trace(getTimer()-t)
    t = getTimer();
    for(var k:int = 0;k<arr.length;k++){
            arr[k] = Math.random();
    }
    trace(getTimer()-t)
    t = getTimer();
    for each(var item in arr){
            item = Math.random()
    }
    trace(getTimer()-t)
    测试结果for...in... 用时6060,for用时517,for...each..用时402
    数字类型和循环运算的效率测试,大家可以自己测试一下结果。
    基本上for和while效率差不多;++和--运算比 += 和 -= 快;--又比++快。
    比较倒的是int类型和uint类型在某些计算上居然比Number类型差那么多。

    下面是一个简单的过程

    private function method1() : void { 
    var tmpVar:int; 
     for(var i:Number=0; i<testArray.length; i++) { 
      tmpVar = testArray[i]; 
     } 
    } 
    i定义为Number ,运行时间是: 53.34 毫秒

    for(var i:int=0; i<testArray.length; i++)

    i定义为int,效率提高了,35.58 毫秒

    var l:int = testArray.length;

    for(var i:int=0; i<l; i++)

    时间: 21.6毫秒!
    类中的常量
    var tmpVar:int;

    for(var i:Number=0; i<100000; i++) {
     tmpVar = SomeClass.SOME_CONSTANT;
    }

    需要34.08毫秒,如果把赋值放在循环外了?
    var tmpVar:int;
    var myConstant:int = SomeClass.SOME_CONSTANT;

    for(var i:Number=0; i<100000; i++) {
     tmpVar = myConstant;
    }

    只需要15.8毫秒
    变量
    for(var i:int=0; i<100000; i++) {
     var v1:Number=10;
     var v2:Number=10;
     var v3:Number=10;
     var v4:Number=10;
     var v5:Number=10;
    }
    时间 46.52 毫秒
    如果把变量定义在一起,就像这样:
    for(var i:int=0; i<100000; i++) {
     var v1:Number=10, v2:Number=10, v3:Number=10, v4:Number=10, v5:Number=10;
    }
    平均时间19.74毫秒
    位操作
    位运算也可以提高效率,
    例如乘除
    for(var i:int=0; i<100000; i++) {
     var val1:int = 4 * 2;
     var val2:int = 4 * 4;
     var val3:int = 4 / 2;
     var val4:int = 4 / 4;
    }
    平均时间: 49.12毫秒,使用位运算符下降到35.56毫秒
    for(var i:int=0; i<100000; i++) {
     var val1:int = 4 << 1;
     var val2:int = 4 << 2;
     var val3:int = 4 >> 1;
     var val4:int = 4 >> 2;
    }

    #循环体内变量定义较快
    一段非优化代码:
    function doSomething() {
     mx = 100;
     my = 100;
     arr = new Array();
     for (y=0; y < my; y++) {
      for (x=0; x < mx; x++) {
         i = (y * mx) + x;
         arr[i] = i;
      }
     }
     return arr;
    }
    这段代码中,并未声明函数体内的那些变量(那些仅仅在函数内使用的变量)为局部变量,这使得这些变量被播放器调用的速度更慢,并且在函数执行完毕的时候仍然耗占系统资源。
    下面列出的是经过改进的同样功能的代码:
    function doSomething() {
     var mx = 100
     var my = 100
     var arr = new Array();
     for (var y = 0; y < my; y++) {
      for (var x=0; x < mx; x++) {
         var i = (y * mx) + x
         arr[i] = i
      }
     }
     return arr;
    }
    这样一来所有的变量均被定义为了局部变量,他们能够更快地被播放器调用。这一点在函数大量(10,000次)循环运行时显得尤为重要!当一个函数调用结束的时候,相应的局部变量都会被销毁,并且释放出他们占有的系统资源。

    变量名寻址
    这个测试反映了变量名的预寻址是非常重要的,尤其是在循环的时候,一定要先给丁一个指向。这样大大节约了寻址时间。
    比如:
    var num = null
    t = getTimer()
    for (var i=0; i < MAX; i++)
    {
    num = Math.floor(MAX) - Math.ceil(MAX)
    }
    t1.text = "Always lookup: " + (getTimer() - t)
    就不如:
    t = getTimer()
    var floor = Math.floor
    var ceil = Math.ceil
    for (var i=0; i < MAX; i++)
    {
    num = floor(MAX) - ceil(MAX)
    }


    函数语句优化部分:
    #1:函数参数部分
    尽量最小化函数的参数个数

     

    数据运算优化部分:

    #1:减少常数出现的次数
    var a:uint = b+(1024-200)/2;   
    var a:uint = b+412; 
    应取后者。
    a += b 要比 a = a + b 快,同样,自增a++也比a = a + 1快,不过自减a–不是比a=a-1快
    n + n比n * 2快。
    ;++和--运算比+=和-=快;--又比++快。比较倒的是int类型和uint类型在某些计算上居然比Number类型差那么多。

    #2:乘法胜于除法
    result = num / 4;   
    result = num * 0.25;
    应取后者。
    用位运算代替除2或乘2。比如10>>1要比10*2快,而10<<1要比10*2快。从测试来看位运算几乎比乘除快一 倍,但是一般情况下,我们不能选择位运算,比如我们就不能用13>>1来代替13/2,尽管前者比后者运算速度更快,但2者的运算结果却不一 样。所以还是要看具体情况。
    在做除以2操作时,乘法比除法快,位运算更快. 但是不要位运算来操作Number类型的变量,因为它会将Number类型的数值转为整数类型。
    1.a += b 要比 a = a + b 快,同样,自增a++也比a = a + 1快,不过自减a–不是比a=a-1快。
    2.在做除以2操作时,乘法比除法快,位运算更快. 但是不要位运算来操作Number类型的变量,因为它会将Number类型的数值转为整数类型。

    for Int : a = b >>1 faster than a = b *.5 faster than a = b /2 ;
    for Number : a = b *.5 faster than a = b /2 ;
    3.取整操作时,用unit()或int()比用Math.floor()和Math.ceil()要快,其中用uint(n) 比Math.floor(n)要快10倍.
    比如var test:uint = uint(1.5);要比var test:Number = Math.floor(1.5);快,而 var test:uint = uint(1.5)+1;要比var test:Number = Math.ceil(1.5);也快。
    如果是 Math.floor(),用位运算(>>0) 比unit() 或int()更快。
    4.取绝对值时,*-1 比 Math.abs要快.如var test:Number = n < 0 ? n * -1 : n;快于var test:Number = Math.abs(n);
    5.n+n比n*2快。
    6。Math.sqrt()的替代算法.

    function sqrt(w:Number):Number
    {
    var thresh:Number = .00001;
    var b:Number = w * 0.25,a:Number,c:Number;
    do
    {
    c = w / b;
    b = (b + c) * 0.5;
    a = b - c;
    if (a &lt; 0)
    {
    a = -a;
    }
    }
    while (a&gt; thresh);
    return b;
    }
    使用 a ? b : c 的判断运算也比 if 稍微快上一些

    #3:选用最恰当的类型
    var pt:Object = {x:x,y:y};   
    var pt:Point = new Point(x,y);
    应取后者。

    #4:使用模糊的类型转换

    var pt:Point = points[i] as Point;   
    var pt:Point = Point(points[i]);   
    var pt:Point = points[i];
    应选第三个。例外是在迭代器中

    pt = points[i*2];   
    pt = points[i*2 as uint];   
    pt = points[uint(i*2)];
    应该选第三个。(ps:从#4看来,使用as来转换类型是最慢的)。

    #5:位运算符的技巧

    val = num|0;     //向下舍入正数(floor positive nums)   
    val = num+0.5|0;      //四舍五入正数(round pos nums)   
    val = num>>1;     //除以2并且向下舍入(divide by 2&~floor)   
    if(++count&1){};     //间隔(alternation)(ps:count尾数为1,此表达式为假,尾数是1时为真)
    相比Math类的方法。bitwise运算符要快得多。

    #6:比较条件的排列顺序

    if(expensiveTest() && usuallyFalse)   
    if(usuallyFalse && expensiveTest())
    很明显取后者。

    #7:推迟不必要的运算

    whlie () {   
    //min code to establish coarse   
    if(!coarseCriteria){continue;}   
    //more expensive logic   
    }

    #8:内联函数

    将严重影响到性能的代码改成内联,而不是放到一个函数中去调用。

    #9:直接调用函数,避免使用引用来调用函数,更不要用匿名函数。

    #10:循环中应该使用常量。

    for(var i=0; i<arr.length; i++)   
    for(var i=0; i<l ; i++)
    应选后者。

    #11:预先计算好经常被访问的数值或引用。(ps:用一些变量替换多次运算已经多次对象访问)

    #55:没看懂。原文:callbacks are faster than events for assignment & dispatch.bubbling events are even slower. 效率分析数据

    #12:抛出error很费资源。

    try{isNull.x = 3}catch(e:*){}   
    if(!isNull){isNull.x = 3} 
    应选后者。

    #13:变量的作用域的不同并不会构成很大的性能差异。不过变量属于class的话,访问起来会慢一些。

    #14:避免使用"with"(avoid using "with")

    #15:使用对象池(use object pools)
    function getThingInstance(param){   
     if(pool.length){o = pool.pop()}   
     else{o = new Thing()}   
     o.init(param)   
     return o;   
    }

    #16:默记法,为运算量大的运算缓存返回结果(memoization:cache return values for expensive logic)
    function calculateStuff(a) {
     if (cache[a]) { return cache[a]; }
     ... // calculate b
     cache[a] = b;
     retutn b;
    }

    #17:经常清理不用的监听器,如enterFrame、timer、mouseMove等

    #18:将超长的操作分散到多个帧里面去,用以保证UI能响应用户。

    #19:各种容器的性能对比。LinkedList的插入是最慢的。
    linkedList > arrayFixed > array > vector > vectorFixed

     

    #21:各种容器的迭代性能。所需时间比较:
    ObjHash1 > ObjHash2 > Dictionary > LinkedList > Vector = Array

    #22:各容器随机删除元素的性能。所需时间比较:
    Vector>Array>Hash>Dictionary>LinkedList

    #23:有时候在array或vector里跳过元素,或者将元素设为null,比删除这个元素更高效。
    清空array或vector里元素:new > [] > arr.length = 0;

    #24:组合不同的容器。以SPL & Gtween为例。要从整体来优化,可从下列入手:减少初始化动作,减少嵌套函数的调用,减少深层访问(例如a.b.c.d),减少运算数量,减小GC次数。

    多媒体方面的优化:

    总的来说,所有媒体都是占用CPU滴~所以,不用的时候记得关上~
    视频优化:减少播放面积,减低帧速,减小关键帧出现频率(keyframte frequency),用Spark来编码视频,去除声音。又或者试一下GPU渲染模式。
    摄像头优化:通过使用camera.setMode来减少捕捉面积,减少帧速。
    通过右键菜单→显示重绘区域来检测那些地方被重新绘制,以便优化。
    图像由以下东西组成:渲染、滤镜和合成
    提高图像渲染的效率:减少曲线数量,避免使用遮罩,减小笔触和渐变的复杂度,在动画中使用抗锯齿,将复杂的图形转换成位图(bitmap)。
    减少图像渲染的频率:使用cacheAsBitmap,激活2.5D,或者实现自定义的缓存:
    //cache for translation:   
    sprite.cacheAsBitmap = true;   
    //translation, rotation, scale;   
    sprite.z=0;  
    自定义缓存。

    将图形放到container sprite中。把图形滑到一个bitmapData对象中。在容器中将图形转换成位图。监听器等等的东西要添加到容器中(而不是图形中。)

    在大部分系统中,你可以在位图渲染到屏幕中之前,在里面添加一些包含图像内容的向量。
    技巧:

    每一帧都交换位图,以便能缓存动作(swapping bitmaps each frame to cache motion)。

    在不同的Bitmap对象中使用相同的BitmapData对象。

    优化滤镜:减少滤镜的数量和质量。减少重绘的频率。减少滤镜效果涉及的范围的大小,包括那些透明的像素。
    清空屏幕:图像的alpha=0的时候,依然会被渲染,所以应该以visible=false来代替。不过,即使是visible=false的时候,事件监听等一些事情依然会发生,为避免这些,请使用removeChild()。
    优化合成:影响合成所需时间的因素有:范围、深度(指layers和在影片中被嵌套的次数)和混合模式(blend modes)。


    可以通过cacheAsBitmap和自定义缓存来优化合成。

    优化GC:在每一帧中保留足够的时间给GC使用(ps:这应该是说GC发生在每一帧的剩余时间中)。

    通过重用对象来减小GC的需要。(例如使用对象池)

    使用图片尽量转成png来用,读写效率能有所提升

  • 相关阅读:
    Serialize&Deserialize
    Async&Await
    Generic
    Enum
    Array
    String
    Nullable
    Collection
    Class&Struct
    Interface
  • 原文地址:https://www.cnblogs.com/keng333/p/3114957.html
Copyright © 2011-2022 走看看