原文链接:http://www.erlang.org/doc/efficiency_guide/commoncaveats.html
(水平有限,错误之处欢迎指正)
3 一般说明
这里列出一些需要注意的模块和内建函数,这里不只关注性能,还有其他。
3.1 timer模块
用erlang:send_after/3和erlang:start_time/3创建的定时器,远比timer模块创建的定时器高效得多。timer模块用单独的进程管理定时器,如果大量进程经常性的创建和注销定时器,这个进程很容易过载(尤其在使用SMP虚拟机的时候)。
有些timer模块的函数不涉及定时器(例如timer:tc/3,timer:sleep/1),他们不会call timer_server进程,因此是无害的。
3.2 list_to_atom/1
原子不参与垃圾回收。一旦原子被创建,就永远不会消失。如果原子数量达到上限(默认1048576),虚拟机会终止。
因此,随意将字符串转换成原子对需要持续运行的系统来说是非常危险的。如果只允许输入某些被定义好的原子,可以使用list_to_existing_atom/1来避免“拒绝服务攻击”。(所有被允许的原子必须已经被创建,例如在一个模块中使用它们,并且加载这个模块。)
使用list_to_atom/1去构建一个原子,并像下面这样这样传递给apply/3,代价非常高,在时间要求严格的代码中不被推荐。
apply(list_to_atom("some_prefix"++Var), foo, Args)
3.3 length/1
计算列表长度的时间和列表长度成正比,相反tuple_size/1,byte_size/1和bit_size/1的执行时间是一个常量。
通常你不需要担心length/1的速度,因为它是用C高效实现的。但在一些时间要求严格的代码中,如果输入的列表可能非常长,你就要避免使用length/1。
一些length/1可以用匹配来替换,例如代码
foo(L) when length(L) >= 3 -> ...
可写作
foo([_,_,_|_]=L) ->
...
(一个明显的不同是,如果L不是一个合法的列表,length(L)会失败,而在第二个代码片段中的匹配却不会。)
3.4 setelement/3
setelement/3会拷贝它所修改的元组,因此,在循环中用setelement/3更新元组每次都会拷贝这个元组。
以上规则有个例外,就是这个元组被拷贝过。如果编译器能够明显的分辨出直接更新和拷贝一份更新是完全一样的,那么setelement/3调用将被替换成一个特别的破坏性的setelement命令。
multiple_setelement(T0) -> T1 = setelement(9, T0, bar), T2 = setelement(7, T1, foobar), setelement(5, T2, new_value).
第一次setelement/3调用会拷贝元组,并改变第九个元素。紧接着第二个setelement/3调用会改变刚才拷贝的那个元组。
要使这个优化被应用,一下所有情况都必须满足:
- 索引(setelement/3的第一个参数)必须写成整数,不是变量和表达式。
- 索引必须是倒序给出的。
- 在setelement/3的调用之间不能有其他函数的调用。
- 从setelement/3返回的元组必须只被用作后续的setelement/3调用。
如果不能像例子multiple_setlement/1一样构造代码,修改大元组的最好方法是把元组转换成列表,修改后,再转换回元组。
3.5 size/1
size/1返回元组和binary的大小。
用R12B引进的新的内建函数tuple_size/1和byte_size/1为编译器和运行时系统提供更多的优化机会。更大的好处是新的内建函数可以帮助Dialyzer找到程序中更多的bugs。
3.6 split_binary/2
通常用匹配来分割一个二进制比调用split_binary/2函数更高效,此外将位语法的匹配和split_binary/2混合使用将妨碍位语法匹配的优化。
DO
<<Bin1:Num/binary,Bin2/binary>> = Bin,
DO NOT
{Bin1,Bin2} = split_binary(Bin, Num)
3.7 '--'操作符
注意:'--'操作符与其操作对象的长度的乘积成正比,也就是说,如果两个操作对象都是长列表,速度会很慢。
DO NOT
HugeList1 -- HugeList2
用ordsets模块代替:
DO
HugeSet1 = ordsets:from_list(HugeList1),
HugeSet2 = ordsets:from_list(HugeList2),
ordsets:subtract(HugeSet1, HugeSet2)
明显的,如果列表的原始顺序很重要,这种方法是不可行的。如果列表的顺序必须被保留,就这样做:
DO
Set = gb_sets:from_list(HugeList2), [E || E <- HugeList1, not gb_sets:is_element(E, Set)]
细节注意1:如果列表中有重复的元素,那这段代码和'--'是不同的(这段代码会删掉HugeList1中所有在HugeList2中出现的元素;'--'只删除对应个数。)
细节注意2:这段代码用'=='比较操作对象,而'--'用'=:='比较。如果对差别的精度要求高,可以用sets代替gb_sets,但是sets:from_list/1处理长列表比gb_sets:from_list/1慢很多。
用'--'操作符少出列表中的一个元素不存在性能问题:
OK
HugeList1 -- [Element]
(原创翻译,欢迎任何形式的转载,但请务必注明出处:http://www.cnblogs.com/liangjingyang/archive/2012/06/20/2557065.html)