zoukankan      html  css  js  c++  java
  • [Erlang0002][OTP] Efficiency Guide User's Guide > The Eight Myths of Erlang Performance

    原文链接:http://www.erlang.org/doc/efficiency_guide/myths.html
    (翻译水平有限,错误之处欢迎指正)
     
    2 Erlang性能的八个谬论

    有些真理依然被人们相信,尽管早已被推翻成为谬论,或许是因为“信息”通过口口相传总是比通过发布公告传得快,例如,匿名函数已经变快。
     
    这里,我们秒杀那些已成为谬论的,曾经的真理。
     
    2.1 谬论:匿名函数很慢
     
    是的,匿名函数(funs)过去很慢,非常慢,比apply/3还要慢。起初,匿名函数仅仅是用语法糖来实现的,编译诡计,普通元组,apply/3,以及其他一堆技巧。
     
    但那已经是古老的历史了。匿名函数在R6B版本有了自己的数据类型,并且在R7B版本做了进一步的优化。现在调用一个匿名函数的消耗已经大致下降到调用本地函数与apply/3之间。
     
    2.2 谬论:列表解析很慢
     
    列表解析以前是用匿名函数实现的,而在万恶的旧社会匿名函数确实很慢。
     
    现在编译器把列表解析重写成一个普通的递归函数。当然用尾递归+反转的方式也会很快。要不将它写成尾递归?我们先看看下一个谬论。
     
    2.3 谬论:尾递归函数比递归函数快很多

    据说,递归函数在栈上留下了很多无用的项(term,在erlang中代表所有数据类型,这里翻译成项,下同),垃圾回收机制必须拷贝这些无用的项,而尾递归会马上释放这些项。

    在R7B之前这是正确的,在R7B中,编译器开始重新生成代码,把从来不会被用到的项的引用重写成空列表,这样垃圾回器收器就再也不必保存无用的值了。

    就算进行了优化,在绝大多数时间里,尾递归函数比递归函数要快,why?

    首先,必须弄清楚每次递归调用消耗了多少栈空间。大多数情况下,递归函数每次递归需要的栈空间比尾递归函数每次递归所分配的堆空间要多。因为更多的内存消耗,垃圾回收器要更频繁的回收,这会增加对栈的操作。

    在R12B以及后续版本中,进行了优化,大多数情况下会减少递归调用需要的栈空间,所以,列表递归的函数和一个尾递归+lists:reverse/1恰好需要相同的内存。lists:map/2,lists:filter/2,列表解析,以及其他递归函数和他们的尾递归版本相比,消耗同样数量内存。(靖阳:注意这里指的是列表的递归)。

    那么到底谁快?

    要根据实际情况。在Solaris/Sparc上,递归函数似乎稍微快些,甚至是有很多元素的列表。而在X86架构中,尾递归要快30%。

    所以现在更像是根据个人口味来选择。如果你真的需要速度至上,必须测试。你再也不能完全肯定,尾递归列表函数在所有的环境中都是最快的。

    注意:一个尾递归函数,如果不需要再最后进行倒转,显然比一个递归函数要快,因为尾递归函数不需要构造任何项(例如,对列表中所有整数求和的函数)。


    2.4 谬论:‘++’总是很糟糕
     
     
    ++操作符,臭名远洋,很可能是因为这样用它:

    DO NOT
    naive_reverse([H|T]) ->
        naive_reverse(T)++[H];
    naive_reverse([]) ->
        [].
    用++来反转列表效率最为低下。因为++操作符会拷贝左边的运算对象,结果会被一遍一遍的拷贝...导致二次运算。
    另一种情况,这样用++

    OK
    
    
    naive_but_ok_reverse([H|T], Acc) ->
        naive_but_ok_reverse(T, [H]++Acc);
    naive_but_ok_reverse([], Acc) ->
        Acc.
    这还好,每个列表元素只会被拷贝一次。增长的结果Acc对于++操作符来说是右边的运算对象,不会被拷贝。
    当然,老练的Erlang程序员会这样写

    DO
    
    
    vanilla_reverse([H|T], Acc) ->
        vanilla_reverse(T, [H|Acc]);
    vanilla_reverse([], Acc) ->
        Acc.
    
    
    这样会稍微高效一些,因为你没有构建一个列表元素而是直接拷贝它。(如果编译器没有自动的把[H]++Acc重写成[H|Acc],这样的写法会更高效)。
     
    2.5 谬论:字符串很慢

    事实上,如果操作不当,字符串处理起来是很慢。在Erlang中,你要在字符串的使用方式上花点心思,选择一个适当的表现形式。如果打算用正则表达式,用re模块,而不要用废弃的regexp模块。
     
    2.6 谬论:修复一个Dets文件非常慢

    修复时间仍然和Dets文件中的记录数量成正比,但是过去修复Dets非常非常慢。Dets已经被大规模重写和提高。
     
    2.7 谬论:BEAM是一个基于栈的,字节码虚拟机(因此很慢)

    BEAM是一个基于寄存器的虚拟机。它有1024个虚拟寄存器来存放临时的值或在函数调用时传递参数。函数调用所要用到的变量被存储在栈中。
     
    2.8 谬论:当变量不会被用到时,用'_'会加速你的程序运行

    曾几何时,这是对的,但是自R6B开始,BEAM编译器已经完全能够自己辨识变量是否会被使用。


    (原创翻译,欢迎任何形式的转载,但请务必注明出处:
    http://www.cnblogs.com/liangjingyang/archive/2012/06/20/2556193.html


  • 相关阅读:
    【读书笔记】iOS-解析XML
    【读书笔记】iOS-iOS开发之iOS程序偏好设置(Settings Bundle)的使用
    【读书笔记】iOS-Settings Bundle
    【读书笔记】iOS-自定义 URL Scheme 完全指南
    【读书笔记】iOS-自定义URL Scheme注意事项
    【读书笔记】iOS-iCloud文件备份
    【读书笔记】iOS-iCloud介绍
    【读书笔记】iOS-后台运行模式
    【读书笔记】iOS-WiFi长连接
    【读书笔记】iOS-设置应用的硬件需求
  • 原文地址:https://www.cnblogs.com/liangjingyang/p/2556193.html
Copyright © 2011-2022 走看看