天天看點

轉載:Erlang 函數(Efficiency Guide)Functions1  Pattern matching模式比對2  Function Calls函數調用3  Memory usage in recursion遞歸中的記憶體使用情況

轉自:http://www.cnblogs.com/futuredo/archive/2012/10/26/2737644.html

Pattern matching in function head and in case and receive clauses are optimized by the compiler. With a few exceptions, there is nothing to gain by rearranging clauses.

函數頭以及case和receive子句中的模式比對是經過編譯器優化的。重新整理子句是沒有用的,當然也有一些例外。

One exception is pattern matching of binaries. The compiler will not rearrange clauses that match binaries. Placing the clause that matches against the empty binary last will usually be slightly faster than placing it first.

一個例外是二進制資料的模式比對。編譯器不會去重新整理那些比對上二進制資料的子句。把比對空資料的子句放到最後比放在首位效率要稍快一些。

Here is a rather contrived example to show another exception:

這裡是一個人為(一般不應該這樣)的例子來表示另外一個例外:

DO NOT

The problem is the clause with the variable Int. Since a variable can match anything, including the atoms four, five, and six that the following clauses also will match, the compiler must generate sub-optimal code that will execute as follows:

問題在于那個帶變量的子句。因為變量可以比對任何值,包括後面的原子four, five和six也是,編譯器會産生次優的代碼,像下面這樣執行:

First the input value is compared to one, two, and three (using a single instruction that does a binary search; thus, quite efficient even if there are many values) to select which one of the first three clauses to execute (if any).

首先,輸入值會跟one, two和three比較(使用一個二分查找的指令;這樣的話,即使有很多值也會很高效)來選擇三個子句裡面執行哪一個。

If none of the first three clauses matched, the fourth clause will match since a variable always matches. If the guard test is_integer(Int) succeeds, the fourth clause will be executed.

如果前三個子句沒有一個比對上,第四個子句會去比對,因為變量總是能比對上輸入值。如果驗證is_integer(Int)成功,第四個子句會被執行。

If the guard test failed, the input value is compared to four, five, and six, and the appropriate clause is selected. (There will be a function_clause exception if none of the values matched.)

如果驗證失敗,輸入值會跟four, five和six去比較,選擇其中合适的子句。(如果沒有一個比對上,會産生一個function_clause的異常。)

Rewriting to either

重寫成下面任意一個

DO

or

will give slightly more efficient matching code.

會産生更高效的代碼。

Here is a less contrived example:

這裡有個稍貼近一般情況的例子:

The first argument is not a problem. It is variable, but it is a variable in all clauses. The problem is the variable in the second argument, Xs, in the middle clause. Because the variable can match anything, the compiler is not allowed to rearrange the clauses, but must generate code that matches them in the order written.

第一個參數沒有問題,它是一個變量,所有子句中都有的那個變量。問題在于中間那個子句的第二個變量Xs。因為變量可以比對任何值,是以這裡不允許編譯器來重新整理子句,而是按照所寫的順序來産生編譯代碼。

If the function is rewritten like this

如果像這樣來重寫這個函數

the compiler is free to rearrange the clauses. It will generate code similar to this

編譯器就能夠重新整理子句。它會産生類似于這樣的代碼

DO NOT (already done by the compiler)

which should be slightly faster for presumably the most common case that the input lists are not empty or very short. (Another advantage is that Dialyzer is able to deduce a better type for the variable Xs.)

對于可預見的大多數情況來講(輸入參數清單為非空或者很短),效率會快一點。

Here is an intentionally rough guide to the relative costs of different kinds of calls. It is based on benchmark figures run on Solaris/Sparc:

Calls to local or external functions (foo(), m:foo()) are the fastest kind of calls.

Calling or applying a fun (Fun(), apply(Fun, [])) is about three times as expensive as calling a local function.

Applying an exported function (Mod:Name(), apply(Mod, Name, [])) is about twice as expensive as calling a fun, or aboutsix times as expensive as calling a local function.

(%% 這邊以及下面所指的fun,應該是Module:Function(Arguments)這種形式的函數,其中M,F,A可以是變量類型,值不是固定的 %%)

這邊是一個粗略的對不同類型調用的開銷做個比較的指南。這是根據Solaris/Sparc上benchmark的跑分來評判的。

直接調用本地或者外部函數是效率最快的。

直接調用一個fun,或者使用apply的方式來調用一個fun(Fun(), apply(Fun, [])),開銷大概是直接調用本地函數的三倍左右。

使用apply的方式來調用一個外部子產品的公開函數(Mod:Name(), apply(Mod, Name, [])),開銷大概是調用一個fun的兩倍,或者說,是直接調用本地函數的六倍。

注意點和實作細節

Calling and applying a fun does not involve any hash-table lookup. A fun contains an (indirect) pointer to the function that implements the fun.

調用和應用一個fun,沒有用到任何哈希表查詢。一個fun(要應用的那個函數的名稱)包含一個(間接)指針,指向那個實作功能的函數。

  Warning

  提醒

  Tuples are not fun(s). A "tuple fun", {Module,Function}, is not a fun. The cost for calling a "tuple fun" is similar to that of apply/3 or worse. Using "tuple funs" is strongly discouraged, as they may not be supported in a future release, and because there exists a superior alternative since the R10B release, namely the fun Module:Function/Arity syntax.

  元組不是 fun(s)。"tuple fun",{Module, Function},不是一個fun。調用一個"tuple fun"的開銷和apply/3的方式差不多或者更糟。強烈建議不要使用"tuple funs",因為後續的版本可能不再支援,而且從R10B版本開始,有一個更好的替代方式,叫做Module:Function/Arity syntax。

(%% tuple fun就是{Module, Function},沒遇到過 %%)

apply/3 must look up the code for the function to execute in a hash table. Therefore, it will always be slower than a direct call or a fun call.

apply/3方式必須在一個哈希表中查找對應函數的代碼。是以,它總是比直接調用或者fun調用要慢得多。

(%% apply/3方式和Module:Function(Argument)的差別 %%)

It no longer matters (from a performance point of view) whether you write

 (從性能角度來講)你用(下面)哪種方式寫都沒有關系

(The compiler internally rewrites the latter code into the former.)

(編譯器會在内部結構上把後者重寫成前者)

The following code

下面的代碼

is slightly slower because the shape of the list of arguments is not known at compile time.

會稍慢一點,因為參數清單Arguments在編譯期還是未知狀态。

When writing recursive functions it is preferable to make them tail-recursive so that they can execute in constant memory space.

寫遞歸函數的時候,推薦使用尾遞歸的形式,這樣函數就可以在常量記憶體空間中執行。

繼續閱讀