Erlang性能的八個誤區(Efficiency Guide)

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編譯器完全有能力注意到有個變量未被使用。

 

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

發佈了19 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章