Erlang方法调用有m:f(a),M:F(a),fun,f(),apply/3几种方法,调用效率如何呢?《Erlang/OTP in Action》一书中有一个总结我们看下:
即模块内调用和模块间的方法调用速度差异不大;要关注一下元编程方式调用(apply调用)要慢的多,除此之外除非是在性能要求特别高的场景否则不必过于在意这点性能差异;这个在官方文档中有大致相同的结果:
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 about six times as expensive as calling a local function.
DocLink: http://www.erlang.org/doc/efficiency_guide/functions.html
概念明确
对于上面的结论,明确一下上面表中跨模块调用这个概念在Erlang中常用的表达方式:
In the first form of function calls, ExprM:ExprF(Expr1,...,ExprN), each of ExprM and ExprF must be an atom or an expression that evaluates to an atom. The function is said to be called by using the
fully qualified function name. This is often referred to as a
remoteor
external function call. DocLink:
http://www.erlang.org/doc/reference_manual/expressions.html#id76190
速度差异因何而来
fun包含或者间接包含了实现了该方法的指针调用不涉及hash-table的查询,apply/3必须要在HashTable中查找funtion对应的Code实现,所以通常比直接调用或者fun调用速度要慢.
编译时优化
在参数个数确定的时候,apply/3的调用会在编译时被优化为m:f(a)形式的external function call.通过一段代码看一下:
a() -> M=erlang, F=now, apply(M,F,[]). b(M,F,A) -> apply(M,F,A). c(M,F) -> apply(M,F,[a,b,c]). d(M)-> apply(M,do_something,[]).
我们添加to_core参数,看一下编译出来的Core Erlang代码:
'a'/0 = %% Line 33 fun () -> %% Line 36 call 'erlang':'now' () 'b'/3 = %% Line 38 fun (_cor2,_cor1,_cor0) -> %% Line 39 call 'erlang':'apply' (_cor2, _cor1, _cor0) 'c'/2 = %% Line 41 fun (_cor1,_cor0) -> %% Line 42 call _cor1:_cor0 ('a', 'b', 'c') 'd'/1 = %% Line 44 fun (_cor0) -> %% Line 45 call _cor0:'do_something' ()
可以看到在参数个数确定的情况下,apply/3的调用已经都被优化为mfa的调用方式.
Erlang代码加载自后的调用方式细节需要找资料深入研究一下,估计是在Code Server相关的代码中,待续