探索c#之尾递归编译器优化
阅读目录:
递归运用
一个函数直接或间接的调用自身,这个函数即可叫做递归函数。
- 递归主要功能是把问题转换成较小规模的子问题,以子问题的解去逐渐逼近最终结果。
递归最重要的是边界条件,这个边界是整个递归的终止条件。
static int RecFact(int x)
{
if (x == )
return ;
return x * RecFact(x - );
}
RecFact();
上面是个经典阶乘函数的实现。这里分2步:
- 转换,把10的阶乘转化成10*9!,10(9*8!)....每次转换规模就变的更小。
- 逼近,转换到最小规模时0!,求解1。开始逆向合并逐渐逼近到10,得出解。
这里的x==0就是我们的边界条件(即终止条件),也有的依赖外部变量为边界。
如果一个递归函数没有边界,也就无法停止(无限循环至内存溢出),当然这样也没什么意义。
RecFact调用堆栈:
常见使用场景:
- 阶乘/斐波那契数列/汉诺塔
- 遍历硬盘文件
- InnerExceptions异常扑捉(exception.InnerException==null)
尾递归优化
当边界不明确的时候,递归就很容易出现溢出问题。
在阶乘过程中,堆栈需要保存每次(RecFact)调用的返回地址及当时所有的局部变量状态,期间堆栈空间是无法释放的(即容易出现溢出)。
为了优化堆栈占用问题,从而提出尾递归优化的办法。
static void TailRecursion(int x)
{
Console.WriteLine(x);
if (x == )
return;
TailRecursion(x + );
}
TailRecursion();
使用尾递归堆栈可以不用保存上次的函数返回地址/各种状态值,而方法遗留在堆栈上的数据完全可以释放掉,这是尾递归优化的核心思想。
由于尾递归期间,堆栈是可以释放/再利用的,也就解决递归过深而引起的溢出问题,这也是尾递归的优势所在。
编译器优化
尾递归优化,看起来是蛮美好的,但在net中却有点乱糟糟的感觉。
- Net在C#语言中是JIT编译成汇编时进行优化的。
- Net在IL上,有个特殊指令tail去实现尾递归优化的(F#中)。
我们执行 TailRecursion(0)(x==1000000) 得出如下结论:
C#/64位/Release是有JIT编译器进行尾递归优化的(非C#编译器优化)。
C#/32位或C#/Debug模式中JIT是不进行优化的。
F#在优化尾递归也分2种情况:
1、 简单的尾递归优化成while循环,如下:
let rec TailRecursion(x) =
if (x = ) then true
else TailRecursion(x + )
(方便演示C#呈现)编译器优化成:
public static bool TailRecursion(int x)
{
while (x != 0x3e8)
{
x++;
}
return true;
}
2、 复杂的尾递归,F#编译器会生成IL指令Tail进行优化,如下:
let TailRecursion2 a cont = cont (a + )
优化成:
如何定义复杂的尾递归呢?通常是后继传递模式(CPS)。
F#中在debug模式下,需要在编译时配置:
总结
在C#语言(过程式/面向对象编程思想)中,优先考虑的是循环,而不是递归/尾递归。 但在函数式编程思想当中,递归/尾递归使用则是主流用法,就像在C#使用循环一样。
参考资料
http://volgarev.me/blog/62412678347
http://stackoverflow.com/questions/15864670/generate-tail-call-opcode
探索c#之尾递归编译器优化的更多相关文章
- VS编译器优化诱发一个的Bug
VS编译器优化诱发一个的Bug Bug的背景 我正在把某个C++下的驱动程序移植到C下,前几天发生了一个比较诡异的问题. 驱动程序有一个bug,但是这个bug只能 Win32 Release 版本下的 ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part6:Move语义和编译器优化
本文为第六部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
- Visual C++中的编译器优化
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:Visual C++中的编译器优化.
- gcc编译器优化给我们带来的麻烦???
gcc编译器优化给我们带来的麻烦??? 今天看到一个很有趣的程序,如下: ? 1 2 3 4 5 6 7 8 9 int main() { const int a = 1; int * ...
- C#编译器优化那点事
使用C#编写程序,给最终用户的程序,是需要使用release配置的,而release配置和debug配置,有一个关键区别,就是release的编译器优化默认是启用的. 优化代码开关即optimize开 ...
- 【转】C 编译器优化过程中的 Bug
C 编译器优化过程中的 Bug 一个朋友向我指出一个最近他们发现的 GCC 编译器优化过程(加上 -O3 选项)里的 bug,导致他们的产品出现非常诡异的行为.这使我想起以前见过的一个 GCC bug ...
- C#编译器优化那点事 c# 如果一个对象的值为null,那么它调用扩展方法时为甚么不报错 webAPI 控制器(Controller)太多怎么办? .NET MVC项目设置包含Areas中的页面为默认启动页 (五)Net Core使用静态文件 学习ASP.NET Core Razor 编程系列八——并发处理
C#编译器优化那点事 使用C#编写程序,给最终用户的程序,是需要使用release配置的,而release配置和debug配置,有一个关键区别,就是release的编译器优化默认是启用的.优化代码 ...
- C#编译器优化
C#编译器优化 https://www.cnblogs.com/podolski/p/8987595.html 使用C#编写程序,给最终用户的程序,是需要使用release配置的,而release配置 ...
- 2018-8-10-win10-uwp-禁止编译器优化代码
title author date CreateTime categories win10 uwp 禁止编译器优化代码 lindexi 2018-08-10 19:16:50 +0800 2018-2 ...
随机推荐
- linux系统加快大文件的写入速度
linux系统加快大文件的写入速度 setvbuf进行优化内存IO
- Winform MDI窗体容器、权限、简单通讯
MDI窗体容器: 一般来说,窗体是顶级容器,不允许放在其他任何容器内,但是如果将某个窗体的IsMdiContainer属性设置为True,那此窗体就会成为窗体容器,可以在其中放入其他窗体 在内部的窗体 ...
- NXP恩智浦P87C51/52/54/58/591芯片解密单片机破解多少钱?
NXP恩智浦P87C51/52/54/58/591芯片解密单片机破解 芯片解密型号: P87C51x2.P87C52x2.P87C54x2.P87C58x2.P87C591 单片机解密 #####[ ...
- react native 刷新机制----通知
在项目中,不知道大家有没有遇到这样的一个问题,比如说有两个页面A,B.A页面中有某个按钮点击后可以跳转到B页面,现在有一个需求就是,我在B页面中做了某些操作,然后点击回退按钮,回到A页面,A页面中的数 ...
- Puppet自动化部署-前期环境准备(2)
在安装Puppet环境之前需要配置好机器的基本配置,如规范网络地址IP.hostname,certname认证名称,ntp时间同步等配置完毕,完善的搭建自动化环境. 1.环境介绍 此处实现部署的环境是 ...
- MYSQL性能优化的最佳20+条经验
MYSQL性能优化的最佳20+条经验 2009年11月27日 陈皓 评论 148 条评论 131,702 人阅读 今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数 ...
- 分布式服务协调技术zookeeper笔记
本文主要学习ZooKeeper的体系结构.节点类型.节点监听.常用命令等基础知识,最后还学习了ZooKeeper的高可用集群的搭建与测试.希望能给想快速掌握ZooKeeper的同学有所帮助. ZooK ...
- window10 office 手工完全卸载
在地址栏输入itellyou,点击第一个搜索结果,可以从微软官方网站下载office安装. 一下是一点需要注意到的地方: 本次安装的是office2016其它类似 下载解压有的目录结构: 如果你是x6 ...
- Code Complete 笔记—— 第二章 用隐喻来更充分理解软件开发
在这章里面,提到的隐喻,类同于比喻(建模)的方法的去理解软件开发. 隐喻的优点在于其可预期的效果能被所有人所理解.不必要的沟通和误解也因此大为减低,学习与教授更为快速,实际上,隐喻是对概念进行内在化和 ...
- python 基础之数据类型
一.python中的数据类型之列表 1.列表 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存储.修改等操作 二.列表常用操作 >切片>追加>插入>修改& ...