erlang的官方文档部分翻译

来源:互联网 发布:java集合类实现类 编辑:程序博客网 时间:2024/05/19 17:06

erlang的八个秘密

1.        复杂函数Funs较慢

funs的速度比apply/3要慢,它从实现机制上利用了编译器的小伎俩,速度上比apply/3使用元组和大量精巧设计来的慢。

2.        列表解析比较慢

在以前列表解析是使用funs的方式实现的,因此它的速度是非常慢的。

但现在使用递归函数实现,当然,采用尾递归函数,在末尾多加一个列表反转仍然要快很多。这在下一个秘密中会讲到。

3.        尾递归函数比普通递归函数要快得多

普通递归函数在堆栈上留下了废旧对象的指针,垃圾回收器需要将所有的废旧对象拷贝一份。而在尾递归函数中,原地开栈意味着废旧对象直接被弃掉了

 

以上是R7B 之前的版本的情况,在R7B 版本中,编译器运行一段代码,用空列表覆盖了指向废旧对象的指针,因此垃圾回收器不用将废旧数据一直保留了。

即便采用了上述优化,采用尾递归仍然要比普通递归函数快得多,为什么呢?

 

这就与递归函数被调用时占据了多少字节的堆栈有关系。在绝大多数情况下,一个普通递归函数会比一个尾递归函数开辟更多字节的堆栈,正因为如此,垃圾回收器需要被更频繁地使用,而且它需要更多的来回访问堆栈。

 

在R12B以及更后来的版本中,已经减少了递归函数调用对于堆栈字数的使用,所以在R12B以后的版本中,使用普通递归函数与使用尾递归调用函数再在其末尾使用一个lists:reverse/1反转列表这两种方式使用相同多的空间。

既然如此,那么哪一个更快?

 

看情况,在Solaris/Sparc 中,普通递归函数稍微快,即便在元素比较多的列表处理上。而在X86中,尾递归比普通递归快 30%.

因此在选择时,若你要极大限度的提升速度,就要进行权衡,因为尾递归调用并非在所有环境中都快于普通递归。

当然,在不必在尾部使用lists:reverse/1进行列表反转的情况下,尾递归是毫无疑问快于普通递归的(比如,写一个函数计算列表中所有整数和)

4.        ++’运算是非常糟糕的

‘++’运算是无论如何不应该存在的,

例如这样的例子:

      naive_reverse([H|T]) ->

             naive_reverse(T)++[H];

      naive_reverse([]) ->

             [].

很显然可以改写成这样:

      naive_but_ok_reverse([H|T], Acc) ->
              naive_but_ok_reverse(T, [H]++Acc);
      naive_but_ok_reverse([], Acc) ->
              Acc.

实际上经验丰富的程序员应该这样:

      vanilla_reverse([H|T], Acc) ->
             vanilla_reverse(T, [H|Acc]);
      vanilla_reverse([], Acc) ->
             Acc.

5.        Strings 比较慢

在erlang里面字符串如果处理得不合适将会非常的慢,你需要根据字符串是如何处理的合理地选择 re.erl 模块取代 regexp 模块。

6.        整理 Dets文件非常慢

整理时间与文件中存在的record 数量成正比。

7.        BEAM 是一个基于堆栈的字节码的虚拟机(因此慢)

 

8.        当变量不使用的时候,用_’提高程序的速度

这个是之前的优化方法,自从R6B之后编译器就能自动识别不使用的变量了。