自FlashPlayer10伴随着FlashCS4发布时,AS3中就多了一种新的数据类型:Vector
这里先给不大熟悉Vector的同学们做一下简介(节选并改编自官方文档):
Vector是一种特殊的数组
- 首先,Vector的定义和C语言中“数组”更为接近,即数组的每一个元素都有相同的数据类型。(这就意味着对Vector中元素的访问要经过类型检查)
- 其次,Vector是一种密集数组,即每一个索引中必须包含一个值。(可以是null)
- 最后,Vector可以随时指定为固定长度的数组。
由于这些特殊性,Vector便拥有以下几点优势:
- 性能:使用 Vector 实例时的组元素的访问和迭代速度比使用 Array 时的要快很多。
- 类型安全性:在严格模式下,编译器可以识别数据类型错误。(但当使用 push() 方法或 unshift() 方法向 Vector 添加值时,编译器不会检查参数的数据类型,而是在运行时检查)
- 可靠性:相对于 Array,运行时范围检查(或定长检查)极大地提高了可靠性。
今天我们主要关注的是使用Vector数据类型来优化程序效率。
对于追求效率的RIA应用,使用Vector来提升效率是一种简单却行之有效的办法,
然而很多开发者对Vector的了解不够深入,以致未能充分发挥Vector在速度的优势。
接下来我们便来剖析一下Vector的最佳使用方法。
注:下文中所有代码的运行环境:FlashPlayer 10.1 & Mac OS X 10.6.4,所有耗时均经过多次统计平均并取整。一、Vector数组的长度应当固定
Vector类型在AVM中的实现使用的是定长数组,虽然在AS3中其fixed属性默认为false,但仍不建议对Vector数组进行能够更改长度的操作。
请看以下测试:
- var a:Vector.<uint>=new Vector.<uint>(1000000);
- var b:Vector.<uint>=new Vector.<uint>(10000000);
- var t:Number=getTimer();
- for(var k:uint=0;k<9;k+=1){
- a.concat(new Vector.<uint>(1000000));
- }
- for(var i:uint=0;i<10000000;i+=1){
- a[i]=i;
- i=a[i];
- }
- trace(getTimer()-t);
- //////////////////////////////////
- t=getTimer();
- for(var j:uint=0;j<10000000;j+=1){
- b[j]=j;
- j=b[j];
- }
- trace(getTimer()-t);
同样是对长度为1千万的数组进行操作,前者的耗时在850ms,后者则保持在350ms。
如果将上述代码中的Vector a使用1千万次push操作来达到1千万的长度,则耗时变为1800ms。
(事实上,用上述代码对Array进行测试,有同样的耗时比率,所以无论使用哪种数组,都不建议频繁改变其长度)
至于splice操作的耗时,请参见gskinner的Quick As A Flash: Optimization Strategies for AS3 and Flash可以看到,Vector的splice操作要比Array慢出10倍。二、正向/逆向遍历的速度无差异
对于Array类型,正向/逆向遍历其元素的速度大约是3:2,即逆向遍历要慢一些,比如以下代码:
- var a:Array=new Array(10000000);
- var b:Array=new Array(10000000);
- var t:Number=getTimer();
- for(var i:uint=0;i<10000000;i+=1){
- a[i]=i;
- i=a[i];
- }
- trace(getTimer()-t);
- //////////////////////////////////
- t=getTimer();
- for(var j:uint=9999999;j>0;j-=1){
- b[j]=j;
- j=b[j];
- }
- trace(getTimer()-t);
二者的耗时基本一样,经过多次平均,Array耗时要比Vector多3%~8%,速度上仍然不及Vector,如非对效率要求极高,基本可以忽略。
最后对使用String类型的情况进行了单独测试,与引用类型下的表现结果一致。
四、注意索引的数据类型
很多开发者会忽略一个细节,那就是在AS3中“看起来是整数的”不一定就是整数类型。
请看以下的代码段:
- var a:Vector.<uint>=new Vector.<uint>(10000000);
- var t:Number=getTimer();
- for(var i:uint=0;i<10000000;i+=1){
- a[i*2/2]=i;
- i=a[i*2/2];
- }
- trace(getTimer()-t);
- //////////////////////////////////
- t=getTimer();
- for(var j:uint=0;j<10000000;j+=1){
- a[uint(j*2/2)]=j;
- j=a[uint(j*2/2)];
- }
- trace(getTimer()-t);
- //////////////////////////////////
- t=getTimer();
- for(var k:uint=0;k<10000000;k+=1){
- a[(k*2/2)>>0]=k;
- k=a[(k*2/2)>>0];
- }
- trace(getTimer()-t);
- //////////////////////////////////
- t=getTimer();
- var n:uint;
- for(var q:uint=0;q<10000000;q+=1){
- n=(q*2/2);
- a[n]=q;
- q=a[n];
- }
- trace(getTimer()-t);
四种方式的耗时分别是1100ms,750ms,650ms,550ms,当然,最后一种由于只计算了一次,不具可比性。(如果同样计算两次,则是750ms)
这里测试用的是乘法和除法,另经测试,+1/-1的结果也是一样。
所以这里特地提一下,如果一定要把算式直接写在索引的位置,要使用位运算将其转化为整数。
关于AS3效率优化中Vector的使用,目前我能想到的就是这四点。(不排除以后想起来了再更新)
希望能对各位有所帮助。