The Seven Myths of Erlang Performance
Erlang的七个性能误区 otp23.2
http://erlang.org/doc/efficiency_guide/myths.html
1 Myth: Tail-Recursive Functions are Much Faster Than Recursive Functions
误区1:尾递归函数比递归函数快得多
According to the myth, using a tail-recursive function that builds a list in reverse followed by a call to lists:reverse/1 is faster than a body-recursive function that builds the list in correct order; the reason being that body-recursive functions use more memory than tail-recursive functions.
That was true to some extent before R12B. It was even more true before R7B. Today, not so much. A body-recursive function generally uses the same amount of memory as a tail-recursive function. It is generally not possible to predict whether the tail-recursive or the body-recursive version will be faster. Therefore, use the version that makes your code cleaner (hint: it is usually the body-recursive version).
For a more thorough discussion about tail and body recursion, see Erlang's Tail Recursion is Not a Silver Bullet.
Note
A tail-recursive function that does not need to reverse the list at the end is faster than a body-recursive function, as are tail-recursive functions that do not construct any terms at all (for example, a function that sums all integers in a list).
谈到这个误区,使用尾部递归函数建立一个反向的列表,然后调用lists:reverse/1比按照正确顺序建立列表的主体递归函数要快;原因是主体递归函数比尾递归函数使用更多的内存。
在R12B之前,这在某种程度上是正确的。在R7B之前更是如此。但今天,情况并非如此。主体递归函数通常使用与尾递归函数相同的内存量。通常不可能预测尾部递归还是主体递归版本会更快。因此,请使用使代码更简洁的版本(提示:它通常是主体递归版本)。
要更深入地讨论尾递归和主体递归,请参阅
《Erlang的尾部递归并不是灵丹妙药》。
https://ferd.ca/erlang-s-tail-recursion-is-not-a-silver-bullet.html
请注意
不需要在列表末尾反转列表的尾递归函数比主体递归函数更快,完全不构造任何数据项的尾递归函数(例如,一个对列表中所有整数求和的函数)。
2. Myth: Operator "++" is Always Bad
误区2.'++'操作总是不好的
The ++ operator has, somewhat undeservedly, got a bad reputation. It probably has something to do with code like the following, which is the most inefficient way there is to reverse a list:
++操作符有一些不好的声誉,某种程度上不应该是这样的。这个可能跟下面的代码有关
DO NOT
naive_reverse([H|T]) ->
naive_reverse(T)++[H];
naive_reverse([]) ->
[].
As the ++ operator copies its left operand, the result is copied repeatedly, leading to quadratic complexity.
But using ++ as follows is not bad:
这是反转一个列表最低效率的方式。由于++操作符复制它左边的操作数,得到的结果会被一遍又一遍的复制......导致二次方的复杂度
但是像这样使用++操作符还好
OK
naive_but_ok_reverse([H|T], Acc) ->
naive_but_ok_reverse(T, [H]++Acc);
naive_but_ok_reverse([], Acc) ->
Acc.
Each list element is copied only once. The growing result Acc is the right operand for the ++ operator, and it is not copied.
Experienced Erlang programmers would write as follows:
每个列表元素将仅被复制一次。增长中的结果是++操作符的右操作数,不会被复制。
有经验的Erlang程序员实际上会这样做:
DO
vanilla_reverse([H|T], Acc) ->
vanilla_reverse(T, [H|Acc]);
vanilla_reverse([], Acc) ->
Acc.
This is slightly more efficient because here you do not build a list element only to copy it directly. (Or it would be more efficient if the compiler did not automatically rewrite [H]++Acc to [H|Acc].)
这个略微更有效率,因为你不再构建一个列表元素,只是直接复制它而已。(如果编译器不自动将[H]++Acc重写为[H|Acc],那上面的写法会更有效)
3. Myth: Strings are Slow
误区3.字符串很慢
String handling can be slow if done improperly. In Erlang, you need to think a little more about how the strings are used and choose an appropriate representation. If you use regular expressions, use the re module in STDLIB instead of the obsolete regexp module.
实际上,字符串处理如果做得不适当会显得很慢。在Erlang中,你将不得不稍稍多思考一下字符串是怎样被使用的,并选择一个恰当的展现方式,如果你要用正则表达式,使用re模块,不要用旧的regexp模块。
4. Myth: Repairing a Dets File is Very Slow
误区4.修复一个Dets文件非常慢
The repair time is still proportional to the number of records in the file, but Dets repairs used to be much slower in the past. Dets has been massively rewritten and improved.
现在的修复时间仍然和文件里记录的数目成正比,但是Dets文件的修复在以前要慢很多很多。Dets已经被大量地做了重写和改进。
5. Myth: BEAM is a Stack-Based Byte-Code Virtual Machine (and Therefore Slow)
误区5.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 is a threaded-code interpreter. Each instruction is word pointing directly to executable C-code, making instruction dispatching very fast.
BEAM是一个基于寄存器的虚拟机。它有1024个用来存储临时变量和在调用函数时传参的寄存器。在函数调用期间需要存在的变量被保存在堆栈中。
BEAM是一个线程-代码解释器。每条指令直接指向可执行C代码,使得指令调度非常快。
6. Myth: Use "_" to Speed Up Your Program When a Variable is Not Used
误区6.用'_'来表示不使用的变量来加快你的程序
That was once true, but from R6B the BEAM compiler can see that a variable is not used.
Similarly, trivial transformations on the source-code level such as converting a case statement to clauses at the top-level of the function seldom makes any difference to the generated code.
这个以往是正确的,但是自R6B版本以来,BEAM编译器完全有能力注意到有个变量未被使用。
类似地,源代码级别上的简单转换如将case语句转换为函数顶级的子句,很少对生成的代码产生任何影响。
7. Myth: A NIF Always Speeds Up Your Program
误区7.NIF总是会加速你的程序
Rewriting Erlang code to a NIF to make it faster should be seen as a last resort. It is only guaranteed to be dangerous, but not guaranteed to speed up the program.
Doing too much work in each NIF call will degrade responsiveness of the VM. Doing too little work may mean that the gain of the faster processing in the NIF is eaten up by the overhead of calling the NIF and checking the arguments.
Be sure to read about Long-running NIFs before writing a NIF.
将Erlang代码重写为NIF以使其更快应该被视为最后的手段。它只能保证是危险的,但不能保证加快程序。
在每个NIF调用中做太多的工作将降低VM的响应性。做的工作太少可能意味着在NIF中获得的更快的处理速度会被调用NIF和检查参数的开销所消耗。
在编写NIF之前,一定要阅读关于长时间运行的NIFs。