转载:Erlang 函数(Efficiency Guide)
转自:http://www.cnblogs.com/futuredo/archive/2012/10/26/2737644.html
Functions
1 Pattern matching
模式匹配
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
atom_map1(one) -> 1;
atom_map1(two) -> 2;
atom_map1(three) -> 3;
atom_map1(Int) when is_integer(Int) -> Int;
atom_map1(four) -> 4;
atom_map1(five) -> 5;
atom_map1(six) -> 6.
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
atom_map2(one) -> 1;
atom_map2(two) -> 2;
atom_map2(three) -> 3;
atom_map2(four) -> 4;
atom_map2(five) -> 5;
atom_map2(six) -> 6;
atom_map2(Int) when is_integer(Int) -> Int.
or
DO
atom_map3(Int) when is_integer(Int) -> Int;
atom_map3(one) -> 1;
atom_map3(two) -> 2;
atom_map3(three) -> 3;
atom_map3(four) -> 4;
atom_map3(five) -> 5;
atom_map3(six) -> 6.
will give slightly more efficient matching code.
会产生更高效的代码。
Here is a less contrived example:
这里有个稍贴近一般情况的例子:
DO NOT
map_pairs1(_Map, [], Ys) ->
Ys;
map_pairs1(_Map, Xs, [] ) ->
Xs;
map_pairs1(Map, [X|Xs], [Y|Ys]) ->
[Map(X, Y)|map_pairs1(Map, Xs, Ys)].
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
如果像这样来重写这个函数
DO
map_pairs2(_Map, [], Ys) ->
Ys;
map_pairs2(_Map, [_|_]=Xs, [] ) ->
Xs;
map_pairs2(Map, [X|Xs], [Y|Ys]) ->
[Map(X, Y)|map_pairs2(Map, Xs, Ys)].
the compiler is free to rearrange the clauses. It will generate code similar to this
编译器就能够重新整理子句。它会产生类似于这样的代码
DO NOT (already done by the compiler)
explicit_map_pairs(Map, Xs0, Ys0) ->
case Xs0 of
[X|Xs] ->
case Ys0 of
[Y|Ys] ->
[Map(X, Y)|explicit_map_pairs(Map, Xs, Ys)];
[] ->
Xs0
end;
[] ->
Ys0
end.
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.)
对于可预见的大多数情况来讲(输入参数列表为非空或者很短),效率会快一点。
2 Function Calls
函数调用
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的两倍,或者说,是直接调用本地函数的六倍。
Notes and implementation details
注意点和实现细节
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(要应用的那个函数的名称)包含一个(间接)指针,指向那个实现功能的函数。
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
(从性能角度来讲)你用(下面)哪种方式写都没有关系
Module:Function(Arg1, Arg2)
or
apply(Module, Function, [Arg1,Arg2])
(The compiler internally rewrites the latter code into the former.)
(编译器会在内部结构上把后者重写成前者)
The following code
下面的代码
apply(Module, Function, Arguments)
is slightly slower because the shape of the list of arguments is not known at compile time.
会稍慢一点,因为参数列表Arguments在编译期还是未知状态。
3 Memory usage in recursion
递归中的内存使用情况
When writing recursive functions it is preferable to make them tail-recursive so that they can execute in constant memory space.
写递归函数的时候,推荐使用尾递归的形式,这样函数就可以在常量内存空间中执行。
DO
list_length(List) ->
list_length(List, 0). list_length([], AccLen) ->
AccLen; % Base case list_length([_|Tail], AccLen) ->
list_length(Tail, AccLen + 1). % Tail-recursive
DO NOT
list_length([]) ->
0. % Base case
list_length([_ | Tail]) ->
list_length(Tail) + 1. % Not tail-recursive
转载:Erlang 函数(Efficiency Guide)的更多相关文章
- 转载:【原译】Erlang构建和匹配二进制数据(Efficiency Guide)
转自:http://www.cnblogs.com/futuredo/archive/2012/10/19/2727204.html Constructing and matching binarie ...
- 转载:【原译】Erlang常见注意事项(Efficiency Guide)
转自:http://www.cnblogs.com/futuredo/archive/2012/10/17/2726416.html Common Caveats(常见注意事项) Erlang/OTP ...
- 转载:【原译】Erlang性能的八个误区(Efficiency Guide)
转自:http://www.cnblogs.com/futuredo/archive/2012/10/16/2725770.html The Eight Myths of Erlang Perform ...
- 转载:【原译】Erlang列表处理(Efficiency Guide)
转自:http://www.cnblogs.com/futuredo/archive/2012/10/22/2734186.html List handling 1 Creating a list ...
- (转载)函数:mysqli_query和mysql_query有何区别?
(转载)http://wzan315.blog.163.com/blog/static/37192636201241732045299/ Mysqli.dll是一个允许以对象的方式或者过程操作数据库的 ...
- 转载------------C函数之memcpy()函数用法
转载于http://blog.csdn.net/tigerjibo/article/details/6841531 函数原型 void *memcpy(void*dest, const void *s ...
- 转载 js函数声明和函数表达式
在js中函数有两种表达方式.1 函数声明 2 函数表达式 函数声明 function sayname(){ alert("li lei"); } 函数表达式 var sayname ...
- 【译】构造和匹配二进制(Efficiency Guide)
可以通过以下方式有效地构建二进制: my_list_to_binary(List) -> my_list_to_binary(List, <<>>). my_list ...
- [转载]findContours函数参数说明及相关函数
原文地址:findContours函数参数说明及相关函数作者:鸳都学童 findContours函数,这个函数的原型为: void findContours(InputOutputArray imag ...
随机推荐
- 批处理学习笔记8 - 深入学习For命令1
for命令用途非常广,它分为4种 /d /r /l /f /l是类似高级语言的数值循环,在之前就说过,这篇就不说了: http://www.cnblogs.com/HONT/p/3155187.htm ...
- [py]python的继承体系
python的继承体系 python中一切皆对象 随着类的定义而开辟执行 class Foo(object): print 'Loading...' spam = 'eggs' print 'Done ...
- Direct-X学习笔记--三维摄像机
一.介绍 哇! 到了传说中的3D摄像机啦! 之前我们写的东东,都是观察点不动,通过世界变换让东西动,今天,通过三维摄像机我们就能够改变我们的观察点,观察方向,任意在三维空间中驰骋.之前我们所设定的视角 ...
- find 命令查找文件大小为xx的文件
K:字节 G:gb 查找当前目录及子目录下大于1G的文件: # find ./ -size +1G -exec ls -lh {} \; 查找当前目录及子目录下大于1G小于20G的文件: # find ...
- 黑客编程教程(六)Windows的关机和重起
第六节 Windows的关机和重起 很多木马都有远程关机功能,但这并不是一个很好的功能.不过对于入侵服务器,有时需要重起服务器.其实对于关机和重起,只需要调用几个 API函数即可实现. 对于WIN9X ...
- .net+jquery+ashx实现客户端模拟登陆扩展
客户端实现:login namespace LoginApp { partial class Form1 { /// <summary> /// 必需的设计器变量. /// </su ...
- lua连续随机数
号外:惭愧,工作后几乎没有写博客了,其实是有时间的(每周单休),只是厌烦对着屏幕了,还有懒. 现在老板换人了,时间会多点,估计正常就每周双休了,决定还是每周写两篇(不一定是love2d), 写不出就翻 ...
- mproxy: c 语言实现的一个最小的http代理,支持FQ
https://github.com/examplecode/mproxy mproxy 使用说明 这是一个微型的http代理服务器实现,使用c语言实现,核心代码量不足500行,不依赖任何第三方的库( ...
- LeetCode: Spiral Matrix 解题报告
Spiral MatrixGiven a matrix of m x n elements (m rows, n columns), return all elements of the matrix ...
- Sqli-LABS通关笔录-5[SQL布尔型盲注]
/* 请为原作者打个标记.出自:珍惜少年时 */ 通过该关卡的学习我掌握到了 1.如何灵活的运用mysql里的MID.ASCII.length.等函数 2.布尔型盲注的认识 3.哦,对了还有.程序 ...