zoukankan      html  css  js  c++  java
  • 转载:【原译】Erlang性能的八个误区(Efficiency Guide)

    转自:http://www.cnblogs.com/futuredo/archive/2012/10/16/2725770.html

    The Eight Myths of Erlang Performance

    Erlang/OTP R15B02

    1  Myth: Funs are slow

      Fun函数很慢(这里应该是指Module:Function(Arguments)这种形式的函数,其中M,F,A可以是变量类型,值不是固定的)

      Yes, funs used to be slow. Very slow. Slower than apply/3. Originally, funs were implemented using nothing more than compiler trickery, ordinary tuples, apply/3, and a great deal of ingenuity.

      是的,fun函数曾经很慢,非常慢,比apply/3方式还慢。最开始,fun函数只是通过编译器策略,普通元组,apply/3方式和其他一些新的东西来实现的。

      But that is ancient history. Funs was given its own data type in the R6B release and was further optimized in the R7B release. Now the cost for a fun call falls roughly between the cost for a call to local function and apply/3.

      但是那已经是过去的历史,在R6B版本中,fun函数有了自己的数据结构,在R7B版本中,fun函数进一步做了优化。现在一个fun函数调用的耗费大概在本地函数调用和apply/3方式之间。

    2  Myth: List comprehensions are slow

      列表解析很慢

      List comprehensions used to be implemented using funs, and in the bad old days funs were really slow.

      列表解析以前是使用函数来实现的,而在糟糕的以前,函数非常慢。

      Nowadays the compiler rewrites list comprehensions into an ordinary recursive function. Of course, using a tail-recursive function with a reverse at the end would be still faster. Or would it? That leads us to the next myth.

      现在,编译器把列表解析重新写成一个普通的递归函数。当然,使用一个尾递归函数并且在最后反转效率将更快。是吗?让我们来看看下一个因素

    3  Myth: Tail-recursive functions are MUCH faster than recursive functions

      尾递归函数比普通递归函数快得多

      According to the myth, recursive functions leave references to dead terms on the stack and the garbage collector will have to copy all those dead terms, while tail-recursive functions immediately discard those terms.

      谈到这个误区,递归函数把对不需要的数据引用保留在了堆栈上,垃圾回收器将必须拷贝所有这些不需要的数据,相反,尾递归函数会立即抛弃这些不需要的数据。

      That used to be true before R7B. In R7B, the compiler started to generate code that overwrites references to terms that will never be used with an empty list, so that the garbage collector would not keep dead values any longer than necessary.

      那种情况在R7B版本之前是对的,在R7B版本中,编译器开始通过使用一个空列表覆写那些将不会被用到的数据引用来产生代码,所以垃圾回收器在没必要的时候就可以不保留那些不需要的值。

      Even after that optimization, a tail-recursive function would still most of the time be faster than a body-recursive function. Why?

      即使是在优化过后,一个尾递归函数在大多数时候还是比一个体递归要快速,为什么?

      It has to do with how many words of stack that are used in each recursive call. In most cases, a recursive function would use more words on the stack for each recursion than the number of words a tail-recursive would allocate on the heap. Since more memory is used, the garbage collector will be invoked more frequently, and it will have more work traversing the stack.

      这个跟每次递归调用中有多少个字的堆栈空间被使用有关。在大多数情况下,一个普通递归调用每次占用堆栈空间的大小比尾递归要多。由于更多的内存被使用,垃圾回收器将被更频繁地唤醒,并花费更多的工作来遍历整个堆栈。

      In R12B and later releases, there is an optimization that will in many cases reduces the number of words used on the stack in body-recursive calls, so that a body-recursive list function and tail-recursive function that calls lists:reverse/1 at the end will use exactly the same amount of memory. lists:map/2lists:filter/2, list comprehensions, and many other recursive functions now use the same amount of space as their tail-recursive equivalents.

      在R12B及 以后的版本中,做了一个优化,使得在许多情况下能减小体递归调用时在堆栈上占用的空间,所以一个体递归函数和一个尾递归函数,同样在最后调用 lists:reverse/1函数实现列表反转,所使用的内存空间几乎相等。现在,lists:map/2,lists:filter/2,列表解析, 以及许多其他普通递归函数占用的内存空间跟它们的尾递归实现一样。

      So which is faster?

      那哪一个更快?

      It depends. On Solaris/Sparc, the body-recursive function seems to be slightly faster, even for lists with very many elements. On the x86 architecture, tail-recursion was up to about 30 percent faster.

      根据情况有所不同。在Solaris/Sparc上,体递归函数看起来稍微快一点,即使对于那些有很多元素的列表。在x86架构上,尾递归要比体递归快近30%。

      So the choice is now mostly a matter of taste. If you really do need the utmost speed, you must measure. You can no longer be absolutely sure that the tail-recursive list function will be the fastest in all circumstances.

      所以现在怎么选择只是一个喜好的问题。若果真的需要极快的速度,你必须衡量一下。你不再能保证在所有的情况下,尾递归列表函数是最快的。

      Note: A tail-recursive function that does not need to reverse the list at the end is, of course, faster than a body-recursive function, as are tail-recursive functions that do not construct any terms at all (for instance, a function that sums all integers in a list).

      注意:一个不需要在最后反转列表的尾递归函数,当然比一个体递归函数快,尾递归函数不构造任何数据项(例如,一个计算列表中所有整数的和的函数)

    4  Myth: '++' is always bad

      '++'总是不好的

      The ++ operator has, somewhat undeservedly, got a very bad reputation. It probably has something to do with code like

      ++操作符有一个不好的声誉,某种程度上不应该是这样的。这个可能跟下面的代码有关

    DO NOT

    naive_reverse([H|T]) ->
        naive_reverse(T)++[H];
    naive_reverse([]) ->
        [].

      which is the most inefficient way there is to reverse a list. Since the ++ operator copies its left operand, the result will be copied again and again and again... leading to quadratic complexity.

      这是反转一个列表最低效率的方式。由于++操作符复制它左边的操作数,得到的结果会被一遍又一遍的复制......导致二次方的复杂度

      On the other hand, using ++ like this

      另一方面,像这样使用++操作符还好

    OK

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

    is not bad. Each list element will only be copied once. The growing result Acc is the right operand for the ++ operator, and it will not be copied.

      每个列表元素将仅被复制一次。增长中的结果是++操作符的右操作数,不会被复制。

      Of course, experienced Erlang programmers would actually write

      当然,有经验的Erlang程序员实际上会这样做

    DO

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

      which is slightly more efficient because you don't build a list element only to directly copy it. (Or it would be more efficient if the the compiler did not automatically rewrite [H]++Acc to [H|Acc].)

      这个略微更有效率,因为你不再构建一个列表元素,只是直接复制它而已。(如果编译器不自动将[H]++Acc重写为[H|Acc],那上面的写法会更有效)

    5  Myth: Strings are slow

      字符串很慢

      Actually, string handling could be slow if done improperly. In Erlang, you'll have to think a little more about how the strings are used and choose an appropriate representation and use the re module instead of the obsolete regexp module if you are going to use regular expressions.

      实际上,字符串处理如果做得不适当会显得很慢。在Erlang中,你将不得不稍稍多思考一下字符串是怎样被使用的,并选择一个恰当的展现方式,如果你要用正则表达式,使用re模块,不要用旧的regexp模块。

    6  Myth: Repairing a Dets file is very slow

      修复一个Dets文件非常慢

      The repair time is still proportional to the number of records in the file, but Dets repairs used to be much, much slower in the past. Dets has been massively rewritten and improved.

      现在的修复时间仍然和文件里记录的数目成正比,但是Dets文件的修复在以前要慢很多很多。Dets已经被大量地做了重写和改进。

    7  Myth: BEAM is a stack-based byte-code virtual machine (and therefore slow)

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

      BEAM is a register-based virtual machine. It has 1024 virtual registers that are used for holding temporary values and for passing arguments when calling functions. Variables that need to survive a function call are saved to the stack.

      BEAM是一个基于寄存器的虚拟机。它有1024个用来存储临时变量和在调用函数时传参的寄存器。在函数调用期间需要存在的变量被保存在堆栈中

      BEAM is a threaded-code interpreter. Each instruction is word pointing directly to executable C-code, making instruction dispatching very fast.

      BEAM是一个threaded-code(螺纹代码?)解释器。每条指令直接指向可执行C代码,使得指令调度非常快

    8  Myth: Use '_' to speed up your program when a variable is not used

      用'_'来表示不使用的变量来加快你的程序

      That was once true, but since R6B the BEAM compiler is quite capable of seeing itself that a variable is not used.

      这个以往是正确的,但是自R6B版本以来,BEAM编译器完全有能力注意到有个变量未被使用。

  • 相关阅读:
    CSU L: 就多了两分钟
    CSU 1112【机器人的指令】模拟
    P3388 【模板】割点(割顶)
    go 学习 2
    go 学习 1
    netconf协议
    lua 学习 5
    lua 学习 4
    lua 学习 3
    lua 学习 2
  • 原文地址:https://www.cnblogs.com/ribavnu/p/3472005.html
Copyright © 2011-2022 走看看