今天又有一位朋友和我在MSN上聊起了AS3中存取数据效率的话题,突然想起以前曾在blueidea论坛上讨论过。这个话题还有蛮多人感兴趣,比较实用,让它在论坛上沉下去有些可惜。因此特地整理成帖,希望对大家有用。废话少说,看蕉:
使用如下几种存取数据方式进行存取100万次运算,所花时间依次为(单位为毫秒,ms):
如下几种存取数据方式100万次存取运算效率依次为:
* 类定义中的属性(如public、private类属性) 135 毫秒
Point类对象 140 毫秒
Rectangle类对象(x等属性) 140ms
* Array类对象 270ms
* Object类对象 500 ms
动态类动态属性 550
* Rectangle类对象(left等属性) 700
* 自定义类getter/setter 1000
自定义类Function 1000
(以上测试结果是由eidiot测试的,感谢eidiot。稍微做了一点文字修改。见讨论原帖)
问题1:为什么会有速度差别?
最快的是第一类,速度是Array的两倍,Object的四倍,getter/setter的约八倍。存取坐标要用Point和Rectangle,比Array快的多。 Object和动态类效率较低,一般情况下请尽量使用密封类。 测试用的自定义类是密封类,那么,public等类定义中定义好的属性比Array和Object快是正常的。因为查询密封类的类成员(包括属性和方法)只需要查询TraitObj,加一个 prototype obj。而Array, Object都是动态类,不论是内存分配和name lookup都需要额外的开销。
同理,Point、Rectangle也是密封类,当然比Array和Object要快。
这是AS3新的内部实现机制的优点。如果在AS2.0中,则不会有差别。注,AS2的密封类和动态类的支持只存在于编译阶段,在运行时没有区别,无法区分。
有兴趣的朋友可以试试将AS3自定义类设成动态类,再使用动态属性和非动态属性存取10万次比较看看。
问题2:为什么有些类的属性效率会存在差异?比如Rectangle的x属性和left属性为何差别这么大?
Rectangle类除了x,y,width,height,其余属性全部是getter和setter,不是真正的属性。使用getter和setter时,函数调用需要一些开销,自然比真正的属性要慢一些。问题3:为何动态类和密封类的类定义中的成员效率没有差别?
这个是正常的。并不费解。之前想问有没有区分动态成员和非动态成员,就是要弄清这个问题。只要是我们在类定义中定义的成员(包括属性和方法),都在这个类对应的Trait obj中。动态类和密封类都一样。因此,密封类和动态类的属性,方法效率都会相同。具体的实现是通过Slot方式来实现,通过slot index来访问,效率高。
问题4:动态成员的效率和非动态成员的效率为何有差距?AS2中为何没有这样的区别?
因为动态类的动态属性不存在与trait obj中,而是在prototype obj中一个类似于哈希表的数据结构中。哈希表代价是name lookup效率明显低于slot方式,而且生成时系统的开销较大。因此AS3中,动态类的动态成员效率低于类定义中定义的成员。 在AS2中,动态属性和非动态属性的区别只存在于编译阶段,运行时并没有区别。因为运行时,实质上所有对象成员都是以name为索引,name都存储在一张hash table(哈希表)中。所以AS2中没有AS3效率高,也没有这些区别。问题5:private成员和public成员效率有没有差别?
访问private成员和访问public成员不会更快。实际上public, private, protected, internal等只是AS3中内嵌的四个命名空间而已,并不会对访问速度产生差异。他们在Trait obj中都是同样使用slot来存储的,访问也都是通过slot index。 理论上说,我们使用自定义的命名空间来修饰类成员,那么这个类成员的访问速度也应该和内嵌的命名空间一样。有兄弟感兴趣的话,可以继续试试使用自定义命名空间来测试一下。有趣的发现:看测试结果,似乎Rectangle实现的getter和setter速度,要比一般的getter和sette和自定义function要快300。这个真是很有趣。
解释:AS3中不少核心类的方法都是native(关键字)实现,即最终的实现是由AVM语言自然代码(native code)实现,而不是ActionScript代码实现。比如Object类,Class类等等,他们的方法都是native的,由相应的C++代码实现。因此,会比我们自定义类的效率要高不少。黑羽可以肯定的是,核心类中的实现都是native code。但是,Rectangle类的功能并不复杂,而且处于flash.geom包,非核心类。我在想,难道连这个类的方法也是native实现吗?
如果测试多次也是这样,并且测试方法无偏差,那么基本上可以肯定Rectangle类中也是使用了native code。
那么做一个推论,是否只要是Flash API的类基本上都是由native code实现呢?
建议使用其他包中的类,也测试一下他们的getter和setter。如果Flash API中类的getter和setter速度一致,且比自定义类的快,那么大概可以下结论,所有Flash Player API(包括核心类和非核心类)真的都是用native code实现的。






Comments (10)
那静态和非静态的属性,哪个更快点
Posted by songhuan | September 12, 2007 9:00 AM
Posted on September 12, 2007 09:00
领教领教!!
谢谢...
Posted by 栋栋 | September 12, 2007 9:43 AM
Posted on September 12, 2007 09:43
请问下这是一个类存取100万次一共话的时间吗?
应该和CPU的运算速度有关是吧?
Posted by morning | September 12, 2007 11:04 AM
Posted on September 12, 2007 11:04
to morning:
不是类存取,而是类生成的对象存取。:)
与测试电脑有关?是的,运算时间当然和测试环境有关。不过这儿主要关心的是相对的速度,即谁比谁快,快多少,所以测试环境没有关系。大家都可以用自己电脑测一测。
to songhuan:
静态属性保存在Class Traits Obj中,理论上说存储效率应当和Traits obj差不多。但实际上,在读取Class Traits Obj要产生一些额外的消耗,它的读取效率比实例属性要低,比实例getter setter要高。
在我电脑测试,实例属性100万次消耗9毫秒,静态属性190ms左右,实例getter和setter大约400毫秒左右。
Posted by 黑羽 | September 12, 2007 1:43 PM
Posted on September 12, 2007 13:43
请教个问题,用Eclipse+FDT开发AS2.0程序时,有些在Flash IDE中正常的语法语句在Eclipse+FDT中却会提示错误,如以下这种情况错误多见:
private function result(event:Object) : Void {
trace(event.target.results);
}
提示:Variable "target" is not declared
如此情况应该怎样解决呢?
Posted by Merrycat | September 12, 2007 2:16 PM
Posted on September 12, 2007 14:16
to MerryCat:
下次不要回帖问与本帖无关的问题...
你可以用自定义事件类的方法解决这个AS2问题,参见下面链接中最后一段:
EventSender工具类
Posted by 黑羽 | September 13, 2007 7:22 AM
Posted on September 13, 2007 07:22
实在抱歉,实在是在首页上没找到留言的版块和相关的文章,只好发到评论里来了。
Posted by Merrycat | September 13, 2007 9:06 AM
Posted on September 13, 2007 09:06
to merrycat:
哦,这也是。
我尽快开通一个供大家专门问问题的地方。
Posted by 黑羽 | September 13, 2007 9:13 AM
Posted on September 13, 2007 09:13
黑羽大哥,你的书上市了么?我在上海博库书城都没看到有卖啊。可以网上买么?哪里可以买到。你在BLOG里面写的这些在书里面都有么
Posted by ioi | September 13, 2007 10:55 AM
Posted on September 13, 2007 10:55
黑羽师傅,(1)AS3.0在没有构造函数时也自动生成呀?
(2)“将 FLA 文件中Document 类设置为 ButtonExample” 如何操作呀?
(3)可不可以将第一帧上的代码理解为执行类的入口函数呀?
Posted by 潜龙在渊 | September 24, 2007 10:15 PM
Posted on September 24, 2007 22:15