本文共 10199 字,大约阅读时间需要 33 分钟。
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 < 0) { a = -a; } } while (a> 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来用,读写效率能有所提升
转载地址:http://lejsi.baihongyu.com/