比较Object/Dictionary/Array顺序读写性能
2011-07-01更新:关于字符串创建时间的计算,以及一些新发现。
一个既需要顺序读取又需要随机读取的表,采用什么进行存储比较合适呢?
我们知道,Object和Dictionary适合随机存取,而Array和Vector适合顺序存取(**关于Array和Vector的性能可以看这篇文章: **Array/Vector/AS3DS/ds/dsforas性能比较 ),但它们之间的性能差距有大呢?
分析如下:
Object的写性能低于Dictionary;- Object与Dictionary的读写性能基本相当;
- Object在使用数字键名的时候,读写速度接近Array;
- AS会优化使用过的Object的字符串键名,因此对于整个程序的运行时间来说,创建字符串的时间不是问题;但对于性能测试来说,是很大的问题;
- AS也会优化创建变量的过程(或许是在编辑器层次优化?),所以对于字符串和数字等类型,把var放在循环外部,是一种良好的习惯,但对性能或许影响不大(相比而言,new的影响更大);
- 对于Object和Dictionary的读取,使用for each循环性能最高,已经接近Array了;
- 对于Array的读取,for循环性能最高。
为什么for each循环在Object与Array上的表现正好相反呢?这正体现了这两种数据结构的特性,for在顺序存取上性能高,for each在随机存取上性能高。因此,对于Object和Dictionary的读取,应尽量使用for each,而对Array和Vector则尽量使用for。
for each和for in循环在Object与Array上的性能,取决于Object是采用字符串键名还是采用数字键名。如果Object采用的是字符串键名,则for each和for in在Object上的表现就与Array一致。否则,她们的表现就与Array正好相反。
测试结果:
Object write:7582
Object for read:2309
Object for in read:234
Object for each read:103
Dictionary write:2557
Dictionary for read:2302
Dictionary for in read:242
Dictionary for each read:94
Array write:90
Array for read:64
Array for in read:81
Array for each read:79
1package
2{
3import flash.display.Sprite;
4import flash.utils.Dictionary;
5import flash.utils.getTimer;
6
7/**
8 * 比较Object/Dictionary/Array顺序读写性能
9 * @author zrong
10 */
11public class ObjectForSpeedTest extends Sprite
12{
13 public function ObjectForSpeedTest()
14 {
15 const max:int = 500000;
16 var i:int = 0;
17 var r:* = null;
18 _obj = {};
19 _tim = getTimer();
20 for(i=0;i<max;i++)
21 {
22 _obj['a'+i] = i;
23 }
24 trace2('Object write');
25
26 _tim = getTimer();
27 for(i=0;i<max;i++)
28 {
29 r = _obj['a'+i];
30 }
31 trace2('Object for read');
32
33 _tim = getTimer();
34 for(var __objin:* in _obj)
35 {
36 r = _obj[__objin];
37 }
38 trace2('Object for in read');
39
40 _tim = getTimer();
41 for each(var __objeach:* in _obj)
42 {
43 r = __objeach;
44 }
45 trace2('Object for each read');
46
47 _dic = new Dictionary();
48 _tim = getTimer();
49 for(i=0;i<max;i++)
50 {
51 _dic['a'+i] = i;
52 }
53 trace2("Dictionary write");
54
55 _tim = getTimer();
56 for(i=0;i<max;i++)
57 {
58 r = _dic['a'+i];
59 }
60 trace2("Dictionary for read");
61
62 _tim = getTimer();
63 for(var __dicin:* in _dic)
64 {
65 r = _dic[__dicin];
66 }
67 trace2('Dictionary for in read');
68
69 _tim = getTimer();
70 for each(var __diceach:* in _dic)
71 {
72 r = __diceach;
73 }
74 trace2('Dictionary for each read');
75
76 _arr = [];
77 _tim = getTimer();
78 for(i=0;i<max;i++)
79 {
80 _arr[i] = i;
81 }
82 trace2("Array write");
83
84 _tim = getTimer();
85 for(i=0;i<max;i++)
86 {
87 r = _arr[i];
88 }
89 trace2("Array for read");
90
91 _tim = getTimer();
92 for(var __arrin:* in _arr)
93 {
94 r = _arr[__arrin];
95 }
96 trace2("Array for in read");
97
98 _tim = getTimer();
99 for each(var __arreach:* in _arr)
100 {
101 r = __arreach;
102 }
103 trace2("Array for each read");
104 }
105
106 private var _tim:int = 0;
107 private var _obj:Object = {};
108 private var _dic:Dictionary;
109 private var _arr:Array = [];
110
111 private function trace2($n:String):void
112 {
113 trace($n+':'+(getTimer() - _tim));
114 }
115}
116}
2011-07-01更新:
测试环境:
- SDK:4.5
- FlashPlayer:10.3.181.22 debug
有网友在留言中提到,这个测试没有考虑字符串的创建时间,我仔细看了代码,确实是不够严谨。
将for循环中的var **
放在了循环外部,不考虑var的创建时间,发现性能并没有什么变化。
[trace] Object write:7388 [trace] Object for read:2281 [trace] Object for in read:230 [trace] Object for each read:111 [trace] Dictionary write:2575 [trace] Dictionary for read:2294 [trace] Dictionary for in read:234 [trace] Dictionary for each read:109 [trace] Array write:91 [trace] Array for read:64 [trace] Array for in read:91 [trace] Array for each read:89
代码如下:
1package
2{
3import flash.display.Sprite;
4import flash.utils.Dictionary;
5import flash.utils.getTimer;
6/**
7 * 比较Object/Dictionary/Array顺序读写性能,改进var
8 * @author zrong
9 * @data 2011-07-01
10 */
11public class ObjectForSpeedTest extends Sprite
12{
13 public function ObjectForSpeedTest()
14 {
15 const max:int = 500000;
16 var i:int = 0;
17 var r:* = null;
18 _obj = {};
19
20 _tim = getTimer();
21 for(i=0;i<max;i++)
22 {
23 _obj['a'+i] = i;
24 }
25 trace2('Object write');
26
27 _tim = getTimer();
28 for(i=0;i<max;i++)
29 {
30 r = _obj['a'+i];
31 }
32 trace2('Object for read');
33
34 var __objin:* = null;
35 _tim = getTimer();
36 for(__objin in _obj)
37 {
38 r = _obj[__objin];
39 }
40 trace2('Object for in read');
41
42 var __objeach:* = null;
43 _tim = getTimer();
44 for each(__objeach in _obj)
45 {
46 r = __objeach;
47 }
48 trace2('Object for each read');
49
50 _dic = new Dictionary();
51 _tim = getTimer();
52 for(i=0;i<max;i++)
53 {
54 _dic['a'+i] = i;
55 }
56 trace2("Dictionary write");
57
58 _tim = getTimer();
59 for(i=0;i<max;i++)
60 {
61 r = _dic['a'+i];
62 }
63 trace2("Dictionary for read");
64
65 var __dicin:* = null;
66 _tim = getTimer();
67 for(__dicin in _dic)
68 {
69 r = _dic[__dicin];
70 }
71 trace2('Dictionary for in read');
72
73 var __diceach:* = null;
74 _tim = getTimer();
75 for each(__diceach in _dic)
76 {
77 r = __diceach;
78 }
79 trace2('Dictionary for each read');
80
81 _arr = [];
82 _tim = getTimer();
83 for(i=0;i<max;i++)
84 {
85 _arr[i] = i;
86 }
87 trace2("Array write");
88
89 _tim = getTimer();
90 for(i=0;i<max;i++)
91 {
92 r = _arr[i];
93 }
94 trace2("Array for read");
95
96 var __arrin:* = null;
97 _tim = getTimer();
98 for(__arrin in _arr)
99 {
100 r = _arr[__arrin];
101 }
102 trace2("Array for in read");
103
104 var __arreach:* = null;
105 _tim = getTimer();
106 for each(__arreach in _arr)
107 {
108 r = __arreach;
109 }
110 trace2("Array for each read");
111 }
112
113 private var _tim:int = 0;
114 private var _obj:Object = {};
115 private var _dic:Dictionary;
116 private var _arr:Array = [];
117
118 private function trace2($n:String):void
119 {
120 trace($n+':'+(getTimer() - _tim));
121 }
122}
123}
和 smithfox 讨论了一下,觉得留言中的“没考虑字符串的创建时间”应该指的是'a'+i
这部分,于是写了个测试,发现50万个a+i的创建,确实需要消耗600毫秒的时间。
于是乎,我对创建字符串进行了优化,首先想到的方法是创建一个临时的Object,将50万个字符串丢进去做缓存。写Object的时候,从缓存中读入值,应该会省掉这600毫秒了吧?下面将只展示部分代码:
1var __key:Object = {};
2
3//建立字符串缓存
4for(i=0;i<max;i++)
5{
6 __key['a'+i] = 'a'+i;
7}
8
9_tim = getTimer();
10for(i=0;i<max;i++)
11{
12 _obj['a'+i] = i;
13}
14trace2('Object write');
由于粗心,上面对_obj进行写入的时候,并没有调用__key中缓存的值,但就是这样,速度却降到了2500毫秒!
那么这样写会如何?
1_tim = getTimer();
2for(i=0;i<max;i++)
3{
4 _obj[__key['a'+i]] = i;
5}
6trace2('Object write');
为了调用__key中的缓存,我必须知道键名,而键名也是用'a'+i
拼出来的,这个创建字符串的过程,依然在循环中。准确的说,在这个循环中不仅仅包含写入的时间,还包含创建字符串以及从Object中读取的时间。但即使是这样,速度也仅有2626毫秒!
看着这有如神助的速度,我只能猜想Adobe在编译器上做了优化,或者在AVM中进行了缓存,这个优化针对Object或者Dictionary的键名。只要是使用过一次的键名,就会被缓存下来供下次使用。
那么再来看看对Object使用数字键名的结果吧:
[trace] Object write:142 [trace] Object for read:82 [trace] Object for in read:136 [trace] Object for each read:110 [trace] Dictionary write:7539 [trace] Dictionary for read:2349 [trace] Dictionary for in read:242 [trace] Dictionary for each read:110 [trace] Array write:127 [trace] Array for read:69 [trace] Array for in read:92 [trace] Array for each read:88
部分代码如下:
1_tim = getTimer();
2for(i=0;i<max;i++)
3{
4 _obj[i] = i;
5}
6trace2('Object write');
7
8_tim = getTimer();
9for(i=0;i<max;i++)
10{
11 r = _obj[i];
12}
13trace2('Object for read');
如此可见,对Object使用数字键名的速度,居然已经和数组相仿。或许,它们在AVM中就是一回事吧。
- 文章ID:1284
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/compare-performance-in-object-dictionary-array/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。