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来用,读写效率能有所提升