- 浏览: 982277 次
- 性别:
- 来自: 广州
最新评论
-
qingchuwudi:
有用,非常感谢!
erlang进程的优先级 -
zfjdiamond:
你好 这条命令 在那里输入??
你们有yum 我有LuaRocks -
simsunny22:
这个是在linux下运行的吧,在window下怎么运行escr ...
escript的高级特性 -
mozhenghua:
http://www.erlang.org/doc/apps/ ...
mnesia 分布协调的几个细节 -
fxltsbl:
A new record of 108000 HTTP req ...
Haproxy 1.4-dev2: barrier of 100k HTTP req/s crossed
erlang的标准用法是尽可能的把函数调用写出尾递归的方式,实际的结果靠参数传递。尾递归的方式对进程的堆栈使用很小, 只要一个WORD, 但是非尾递归就要看递归的层数,如果数量很大,会把堆栈撑的很大。我们在汇编一级看下如何实现的:
root@nd-desktop:~# cat tailcall.erl
-module(tailcall).
-export([start/1]).
-compile(export_all).
start(N)->
X = loop(N),
Y = tail_loop(N),
X = Y,
done.
loop(0)->
1;
loop(N) when N >0 ->
N * loop(N-1).
tail_loop(N)->
tail_loop2(N, 1).
tail_loop2(0, R)->
R;
tail_loop2(N, R) ->
tail_loop2(N-1, N *R).
root@nd-desktop:~# erlc +"'S'" tailcall.erl
root@nd-desktop:~# cat tailcall.S
{module, tailcall}. %% version = 0
{exports, [{loop,1},
{module_info,0},
{module_info,1},
{start,1},
{tail_loop,1},
{tail_loop2,2}]}.
{attributes, []}.
{labels, 16}.
{function, start, 1, 2}.
{label,1}.
{func_info,{atom,tailcall},{atom,start},1}.
{label,2}.
{allocate,1,1}.
{move,{x,0},{y,0}}.
{call,1,{f,5}}.
{move,{x,0},{x,1}}.
{move,{y,0},{x,0}}.
{move,{x,1},{y,0}}.
{call,1,{f,8}}.
{test,is_eq_exact,{f,3},[{x,0},{y,0}]}.
{move,{atom,done},{x,0}}.
{deallocate,1}.
return.
{label,3}.
{badmatch,{x,0}}.
{function, loop, 1, 5}.
{label,4}.
{func_info,{atom,tailcall},{atom,loop},1}.
{label,5}.
{test,is_eq_exact,{f,6},[{x,0},{integer,0}]}.
{move,{integer,1},{x,0}}.
return.
{label,6}.
{test,is_lt,{f,4},[{integer,0},{x,0}]}.
{allocate_zero,1,1}.
%% 主要是这条 allocate_zero 这个指令 把当前的调用栈保存 同时分配个参数空间
%% 对应 AllocateZero 这个操作
{gc_bif,'-',{f,0},1,[{x,0},{integer,1}],{x,1}}.
{move,{x,0},{y,0}}.
{move,{x,1},{x,0}}.
{call,1,{f,5}}.
%% call opcode, 再次调用 堆栈层数+1
{gc_bif,'*',{f,0},1,[{y,0},{x,0}],{x,0}}.
{deallocate,1}.
%% 恢复调用栈
%% 对应 deallocate_I操作
return.
{function, tail_loop, 1, 8}.
{label,7}.
{func_info,{atom,tailcall},{atom,tail_loop},1}.
{label,8}.
{move,{integer,1},{x,1}}.
{call_only,2,{f,10}}.
{function, tail_loop2, 2, 10}.
{label,9}.
{func_info,{atom,tailcall},{atom,tail_loop2},2}.
{label,10}.
{test,is_eq_exact,{f,11},[{x,0},{integer,0}]}.
{move,{x,1},{x,0}}.
return.
{label,11}.
{gc_bif,'-',{f,0},2,[{x,0},{integer,1}],{x,2}}.
{gc_bif,'*',{f,0},3,[{x,0},{x,1}],{x,1}}.
{move,{x,2},{x,0}}.
{call_only,2,{f,10}}.
%% 调用 call_only opcode 没有建立堆栈的过程
{function, module_info, 0, 13}.
{label,12}.
{func_info,{atom,tailcall},{atom,module_info},0}.
{label,13}.
{move,{atom,tailcall},{x,0}}.
{call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
{function, module_info, 1, 15}.
{label,14}.
{func_info,{atom,tailcall},{atom,module_info},1}.
{label,15}.
{move,{x,0},{x,1}}.
{move,{atom,tailcall},{x,0}}.
{call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
我们在beam_emu.c中可以看到:
#define AllocateZero(Ns, Live) \
do { Eterm* ptr; \
int i = (Ns); \
AH(i, 0, Live); \
for (ptr = E + i; ptr > E; ptr--) { \
make_blank(*ptr); \
} \
} while (0)
#define AH(StackNeed, HeapNeed, M) \
do { \
int needed; \
needed = (StackNeed) + 1; \
if (E - HTOP < (needed + (HeapNeed))) { \
SWAPOUT; \
reg[0] = r(0); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
FCALLS -= erts_garbage_collect(c_p, needed + (HeapNeed), reg, (M)); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
r(0) = reg[0]; \
SWAPIN; \
} \
E -= needed; \
SAVE_CP(E); \
} while (0)
#define SAVE_CP(X) \
do { \
*(X) = make_cp(c_p->cp); \
c_p->cp = 0; \
} while(0)
#define RESTORE_CP(X) SET_CP(c_p, cp_val(*(X)))
#define D(N) \
RESTORE_CP(E); \
E += (N) + 1;
OpCase(deallocate_I): {
Eterm* next;
PreFetch(1, next);
D(Arg(0));
NextPF(1, next);
}
结论是:
尾递归的效率要比递归的好很多, 包括堆栈的使用和参数的分配,就是代码写起来不那么直观。
noway
并不是所有的递归都能转化成尾递归吧
good enough.
noway
root@nd-desktop:~# cat tailcall.erl
-module(tailcall).
-export([start/1]).
-compile(export_all).
start(N)->
X = loop(N),
Y = tail_loop(N),
X = Y,
done.
loop(0)->
1;
loop(N) when N >0 ->
N * loop(N-1).
tail_loop(N)->
tail_loop2(N, 1).
tail_loop2(0, R)->
R;
tail_loop2(N, R) ->
tail_loop2(N-1, N *R).
root@nd-desktop:~# erlc +"'S'" tailcall.erl
root@nd-desktop:~# cat tailcall.S
{module, tailcall}. %% version = 0
{exports, [{loop,1},
{module_info,0},
{module_info,1},
{start,1},
{tail_loop,1},
{tail_loop2,2}]}.
{attributes, []}.
{labels, 16}.
{function, start, 1, 2}.
{label,1}.
{func_info,{atom,tailcall},{atom,start},1}.
{label,2}.
{allocate,1,1}.
{move,{x,0},{y,0}}.
{call,1,{f,5}}.
{move,{x,0},{x,1}}.
{move,{y,0},{x,0}}.
{move,{x,1},{y,0}}.
{call,1,{f,8}}.
{test,is_eq_exact,{f,3},[{x,0},{y,0}]}.
{move,{atom,done},{x,0}}.
{deallocate,1}.
return.
{label,3}.
{badmatch,{x,0}}.
{function, loop, 1, 5}.
{label,4}.
{func_info,{atom,tailcall},{atom,loop},1}.
{label,5}.
{test,is_eq_exact,{f,6},[{x,0},{integer,0}]}.
{move,{integer,1},{x,0}}.
return.
{label,6}.
{test,is_lt,{f,4},[{integer,0},{x,0}]}.
{allocate_zero,1,1}.
%% 主要是这条 allocate_zero 这个指令 把当前的调用栈保存 同时分配个参数空间
%% 对应 AllocateZero 这个操作
{gc_bif,'-',{f,0},1,[{x,0},{integer,1}],{x,1}}.
{move,{x,0},{y,0}}.
{move,{x,1},{x,0}}.
{call,1,{f,5}}.
%% call opcode, 再次调用 堆栈层数+1
{gc_bif,'*',{f,0},1,[{y,0},{x,0}],{x,0}}.
{deallocate,1}.
%% 恢复调用栈
%% 对应 deallocate_I操作
return.
{function, tail_loop, 1, 8}.
{label,7}.
{func_info,{atom,tailcall},{atom,tail_loop},1}.
{label,8}.
{move,{integer,1},{x,1}}.
{call_only,2,{f,10}}.
{function, tail_loop2, 2, 10}.
{label,9}.
{func_info,{atom,tailcall},{atom,tail_loop2},2}.
{label,10}.
{test,is_eq_exact,{f,11},[{x,0},{integer,0}]}.
{move,{x,1},{x,0}}.
return.
{label,11}.
{gc_bif,'-',{f,0},2,[{x,0},{integer,1}],{x,2}}.
{gc_bif,'*',{f,0},3,[{x,0},{x,1}],{x,1}}.
{move,{x,2},{x,0}}.
{call_only,2,{f,10}}.
%% 调用 call_only opcode 没有建立堆栈的过程
{function, module_info, 0, 13}.
{label,12}.
{func_info,{atom,tailcall},{atom,module_info},0}.
{label,13}.
{move,{atom,tailcall},{x,0}}.
{call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
{function, module_info, 1, 15}.
{label,14}.
{func_info,{atom,tailcall},{atom,module_info},1}.
{label,15}.
{move,{x,0},{x,1}}.
{move,{atom,tailcall},{x,0}}.
{call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
我们在beam_emu.c中可以看到:
#define AllocateZero(Ns, Live) \
do { Eterm* ptr; \
int i = (Ns); \
AH(i, 0, Live); \
for (ptr = E + i; ptr > E; ptr--) { \
make_blank(*ptr); \
} \
} while (0)
#define AH(StackNeed, HeapNeed, M) \
do { \
int needed; \
needed = (StackNeed) + 1; \
if (E - HTOP < (needed + (HeapNeed))) { \
SWAPOUT; \
reg[0] = r(0); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
FCALLS -= erts_garbage_collect(c_p, needed + (HeapNeed), reg, (M)); \
PROCESS_MAIN_CHK_LOCKS(c_p); \
r(0) = reg[0]; \
SWAPIN; \
} \
E -= needed; \
SAVE_CP(E); \
} while (0)
#define SAVE_CP(X) \
do { \
*(X) = make_cp(c_p->cp); \
c_p->cp = 0; \
} while(0)
#define RESTORE_CP(X) SET_CP(c_p, cp_val(*(X)))
#define D(N) \
RESTORE_CP(E); \
E += (N) + 1;
OpCase(deallocate_I): {
Eterm* next;
PreFetch(1, next);
D(Arg(0));
NextPF(1, next);
}
结论是:
尾递归的效率要比递归的好很多, 包括堆栈的使用和参数的分配,就是代码写起来不那么直观。
评论
11 楼
EQualizer
2009-10-01
The Book 写道
编译尾递归的函数可以使一系列语句最后的一个函数调用可以被替换为一个简单的跳转指令,指向被调用函数的开头。这就意味着一个尾递归的函数可以无限循环而不需要消耗栈空间。
10 楼
mryufeng
2009-09-25
编译器可以做些 明显的可以转换的 但是人工又懒的去转的那种 就可以了。 我很喜欢erlang factory推荐的那个tidier 很强大,有人工的智能来帮你做erlang的最佳实践,不知道什么时候可以release给大众。
9 楼
t0uch
2009-09-25
Trustno1 写道
weiqingfei 写道
编译器能不能考虑把非尾递归优化为尾递归?
noway
并不是所有的递归都能转化成尾递归吧
8 楼
mryufeng
2009-09-25
尾递归和递归在erlang vm里占用的空间不同 一个使用堆 一个使用栈 但是都在一个进程的空间里, 所以效率差不多。 只是使用heap会很灵活 效率更高些些。
7 楼
argan
2009-09-25
受教了,新版本的erlang在有些情况下,非尾递归的性能不一定差的,所以有时候不写尾递归也无所谓
6 楼
mryufeng
2009-09-25
大部分是尾递归呀 部分的数据集很小 也无所谓的
5 楼
Trustno1
2009-09-25
xvyu 写道
但是erlang的很多标准库都没有用尾递归,比方lists,这是为什么呢
good enough.
4 楼
xvyu
2009-09-25
但是erlang的很多标准库都没有用尾递归,比方lists,这是为什么呢
3 楼
litaocheng
2009-09-25
开始只是知道要怎么做,在老大的剖析下知道了为什么这么做...
继续啊!
继续啊!
2 楼
Trustno1
2009-09-25
weiqingfei 写道
编译器能不能考虑把非尾递归优化为尾递归?
noway
1 楼
weiqingfei
2009-09-25
编译器能不能考虑把非尾递归优化为尾递归?
发表评论
-
OTP R14A今天发布了
2010-06-17 14:36 2677以下是这次发布的亮点,没有太大的性能改进, 主要是修理了很多B ... -
R14A实现了EEP31,添加了binary模块
2010-05-21 15:15 3030Erlang的binary数据结构非常强大,而且偏向底层,在作 ... -
如何查看节点的可用句柄数目和已用句柄数
2010-04-08 03:31 4814很多同学在使用erlang的过程中, 碰到了很奇怪的问题, 后 ... -
获取Erlang系统信息的代码片段
2010-04-06 21:49 3475从lib/megaco/src/tcp/megaco_tcp_ ... -
iolist跟list有什么区别?
2010-04-06 20:30 6529看到erlang-china.org上有个 ... -
erlang:send_after和erlang:start_timer的使用解释
2010-04-06 18:31 8386前段时间arksea 同学提出这个问题, 因为文档里面写的很不 ... -
Latest news from the Erlang/OTP team at Ericsson 2010
2010-04-05 19:23 2013参考Talk http://www.erlang-factor ... -
对try 异常 运行的疑问,为什么出现两种结果
2010-04-05 19:22 2842郎咸武<langxianzhe@163.com> ... -
Erlang ERTS Async基础设施
2010-03-19 00:03 2517其实Erts的Async做的很不错的, 相当的完备, 性能又高 ... -
CloudI 0.0.9 Released, A Cloud as an Interface
2010-03-09 22:32 2476基于Erlang的云平台 看了下代码 质量还是不错的 完成了不 ... -
Memory matters - even in Erlang (再次说明了了解内存如何工作的必要性)
2010-03-09 20:26 3439原文地址:http://www.lshift.net/blog ... -
Some simple examples of using Erlang’s XPath implementation
2010-03-08 23:30 2050原文地址 http://www.lshift.net/blog ... -
lcnt 环境搭建
2010-02-26 16:19 2614抄书:otp_doc_html_R13B04/lib/tool ... -
Erlang强大的代码重构工具 tidier
2010-02-25 16:22 2486Jan 29, 2010 We are very happy ... -
[Feb 24 2010] Erlang/OTP R13B04 has been released
2010-02-25 00:31 1387Erlang/OTP R13B04 has been rele ... -
R13B04 Installation
2010-01-28 10:28 1390R13B04后erlang的源码编译为了考虑移植性,就改变了编 ... -
Running tests
2010-01-19 14:51 1486R13B03以后 OTP的模块加入了大量的测试模块,这些模块都 ... -
R13B04在细化Binary heap
2010-01-14 15:11 1508从github otp的更新日志可以清楚的看到otp R13B ... -
R13B03 binary vheap有助减少binary内存压力
2009-11-29 16:07 1668R13B03 binary vheap有助减少binary内存 ... -
erl_nif 扩展erlang的另外一种方法
2009-11-26 01:02 3218我们知道扩展erl有2种方法, driver和port. 这2 ...
相关推荐
在高级语言中,递归的使用广泛且直观,但在低级语言如汇编语言中,递归的实现则需要更深入的理解和技巧。本文将详细探讨如何在汇编语言中编写递归程序,以求解一个整数的阶乘为例,展示递归在汇编中的具体应用。 ...
这几天恰好和朋友谈起了递归,忽然发现不少朋友对于“尾递归”的概念比较模糊,网上搜索一番也没有发现讲解地完整详细的资料,于是写了这么一篇文章,权当一次互联网资料的补充。:P 递归与尾递归 关于递归操作,相信...
斐波那契数列是一个经典的计算机科学问题,它的定义是这样的:第一项和第二项分别为0和1,从第三项开始,每一项都等于前两项之和。数学公式表示为 F(n) = F(n-...这进一步证明了循环和尾递归在处理此类问题上的优越性。
在本实验报告中,我们将使用MIPS汇编语言来实现插入排序,包括递归和非递归版本。 递归版本 在递归版本中,我们使用了递归函数sort来实现插入排序。sort函数将数组的首地址和要排序的数据个数作为参数。函数首先...
Cholesky分解递归算法与改进[汇编].pdf
在本文中,我们将深入探讨如何使用汇编语言来计算斐波那契数列的前22项,并且对比两种不同的实现方法:递归调用和普通循环加法。首先,让我们了解一下斐波那契数列的基本概念。 斐波那契数列是一个数学上的序列,...
fibonacci数列的各种解法,递归、存储优化的递归、自下而上的递归(迭代法)、尾递归。其中分析内容请移步我的博客、
这个压缩包"Q1069907.zip"似乎包含了用汇编语言实现递归计算斐波那契数列前N项的代码,以及可能的编译器和工具,如TASM(Turbo Assembler)和TurboC。 斐波那契数列是由0和1开始,后面的每一项数字都是前面两项数字...
递归调用算年龄 汇编 成绩为优 课设参考的而好东西
通过以上分析可以看出,尾递归与 Cooper 变换都是计算机科学领域内非常重要的概念和技术。尾递归可以帮助优化递归算法,提高程序效率;而 Cooper 变换则提供了一种方法,可以将非尾递归的递归函数转换为尾递归的形式...
尾递归是一种高效的递归形式,尤其在处理深度递归和大量数据时,可以有效避免栈溢出。然而,是否能从中受益取决于所使用的编程语言及其对尾递归的支持程度。在实际应用中,了解语言特性和编译器优化策略对于充分利用...
/*int f(int n,int a1,int a2) { if(n) return a1; else return f(n - 1, a2, a1 + a2); }*/
在实际编程中,还可以考虑其他优化策略,如使用自底向上的合并排序(先处理较小的子数组,减少不必要的合并操作),或者采用尾递归优化来减少栈空间的使用。这些方法可以在保持算法效率的同时,优化资源的利用。通过...
Python递归是编程中一种强大的技术,它允许函数在解决问题时自我调用。递归的基本原理是通过将复杂问题分解为相同或相似的子问题来解决。...在实际编程中,理解递归和尾递归的概念对于编写高效和优雅的代码至关重要。
14. **递归程序设计**:编写递归程序时,需要考虑递归函数的基线条件(base case)和递归情况,以及如何在每次递归调用中向解决方案靠近。 15. **效率优化**:对于递归算法,可以使用记忆化技术(如动态规划)来...
普通递归和尾递归如果仅仅只是从代码的角度出发来看,我们可能发现不了他的特点,所以笔者利用两张堆栈上的图来展示具体的差距在哪,首先我们来看看普通的递归调用的情况,如下图1.1所示: 假设这里执行的函数是...
直接转换法通常用来消除尾递归和单向递归,将递归结构用循环结构来替代。尾递归是指在递归算法中,递归调用语句只有一个,而且是处在算法的最后。当递归调用返回时,是返回到上一层的调用点,而不是返回到初始调用点...