转载: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 ...
随机推荐
- JEECG图表配置说明
图表配置可以做什么? 图表配置可以通过在线配置,无需编写代码生成图形报表页面.使用highcharts.js实现,可以运行在任何现代浏览器,包括移动终端以及IE6.目前支持曲线图.柱状图等基础报表. ...
- MySQL变量的定义与赋值
MySQL存储过程中,定义变量有两种方式:1.使用set或select直接赋值,变量名以 @ 开头.例如:set @var=1;可以在一个会话的任何地方声明,作用域是整个会话,称为会话变量. 2.以 ...
- Android: TODO 应用交互的两种实现方法(Behavior)
最近在写 TODO app,涉及到 Calendar 和 RecyclerView 的交互, 需求: 1. 往上滑动, Calendar 显示为周 2. 周显示模式下,往下滑动,显示为月 3. 列表下 ...
- Books from Joe's blog
Some books that I really enjoy(ed) It's been quite some time since I blogged about what I've been re ...
- LoadRunner Controller 常见用法
Controller 工作原理:通过场景设计来模拟用户的真实操作并调用vugen中的脚本,再通过设置的压力机产生压力 Scenario-convert scenario to the percenta ...
- web 安全问题(一):CSRF 攻击
什么是CSRF CSRF是怎么产生的 CSRF的攻击对象 CSRG的攻击手段 CSRF的防御措施 什么是CSRF 全称是(Cross Site Request Forgery)跨站请求伪造.也就是恶意 ...
- cocos2d-x Schedule详解
原理介绍 Cocos2d-x调度器为游戏提供定时事件和定时调用服务.所有Node对象都知道如何调度和取消调度事件,使用调度器有几个好处: 每当Node不再可见或已从场景中移除时,调度器会停止. Coc ...
- linux命令(33):less
一.less指令 less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大.less 的用法比起 more 更加的有弹性. 在 more 的时候,我 ...
- 关于 \t 水平制表符 Horizontal Tab (TAB)
今天在学learn python the hard way ex26修改的时候,有一个关于\t的问题,下面分别为代码以及输出结果: 1 poem = """ 2 \tTh ...
- 将ASCII字符串转换为UNICODE字符串
写在前面的话:在MFC的网络编程中,由于现在项目都是使用UNICODE编码,但是网络API的许多函数却只能接受const char*的参数,所以经常会遇到需要将char*转换为TCHAR*的时候,有一 ...