热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

尾递归优化【-O2】

用了“尾递归”,不一定有尾递归优化。优化要开启-O2选项才行!而且有其他约束条件哦。c代码:#include<stdio.h>#include<s


用了“尾递归”,不一定有尾递归优化。优化要开启-O2选项才行!而且有其他约束条件哦。

c代码:

#include 
#include 

int geta(int n1, int n2, int n)
{ 
    if(n<=1)
        return n2;
    else
    {
       // printf("&n1: %p\n&n2: %p\n&n:%p\n", &n1, &n2, &n);
        return geta(n2,n1+n2,n-1);
    }
} 

#define N 30
#define ASD(x) #x
int main()
{ 

    printf("The " ASD(N) " %d\n", geta(1,1,N));
    return 0;
    
} 
 

geta函数尾递归优化前:

(gdb) disas geta
Dump of assembler code for function geta:
   0x080483e4 <+0>:	push   ebp
   0x080483e5 <+1>:	mov    ebp,esp
   0x080483e7 <+3>:	sub    esp,0x18
   0x080483ea <+6>:	cmp    DWORD PTR [ebp+0x10],0x1
   0x080483ee <+10>:	jg     0x80483f5 
   0x080483f0 <+12>:	mov    eax,DWORD PTR [ebp+0xc]
   0x080483f3 <+15>:	jmp    0x8048416 
   0x080483f5 <+17>:	mov    eax,DWORD PTR [ebp+0x10]
   0x080483f8 <+20>:	lea    edx,[eax-0x1]
   0x080483fb <+23>:	mov    eax,DWORD PTR [ebp+0xc]
   0x080483fe <+26>:	mov    ecx,DWORD PTR [ebp+0x8]
   0x08048401 <+29>:	add    eax,ecx
   0x08048403 <+31>:	mov    DWORD PTR [esp+0x8],edx
   0x08048407 <+35>:	mov    DWORD PTR [esp+0x4],eax
   0x0804840b <+39>:	mov    eax,DWORD PTR [ebp+0xc]
   0x0804840e <+42>:	mov    DWORD PTR [esp],eax
   0x08048411 <+45>:	call   0x80483e4       // !!函数递归调用!!
   0x08048416 <+50>:	leave  
   0x08048417 <+51>:	ret    
End of assembler dump.

优化后:

(gdb) disas geta
Dump of assembler code for function geta:
   0x08048450 <+0>:	push   ebx
   0x08048451 <+1>:	mov    edx,DWORD PTR [esp+0x10]
   0x08048455 <+5>:	mov    ecx,DWORD PTR [esp+0xc]
   0x08048459 <+9>:	mov    ebx,DWORD PTR [esp+0x8]
   0x0804845d <+13>:	cmp    edx,0x1
   0x08048460 <+16>:	mov    eax,ecx
   0x08048462 <+18>:	jg     0x804846c 
   0x08048464 <+20>:	jmp    0x8048477 
   0x08048466 <+22>:	xchg   ax,ax
   0x08048468 <+24>:	mov    ebx,ecx
   0x0804846a <+26>:	mov    ecx,eax
   0x0804846c <+28>:	sub    edx,0x1
   0x0804846f <+31>:	cmp    edx,0x1
   0x08048472 <+34>:	lea    eax,[ebx+ecx*1]
   0x08048475 <+37>:	jne    0x8048468 
   0x08048477 <+39>:	pop    ebx
   0x08048478 <+40>:	ret    
End of assembler dump.
优化后没有函数调用,栈帧不生长,相当于变递归为迭代了。


回到上一篇博客的例子,用尾递归计算Fibonacci,尾递归未优化,因此,尾递归虽然优化了时间性能(从指数级变为线性),但是空间复杂度依然是线性,比不上迭代版本的O(1)。这里加以优化,再对比。先再贴一遍代码:



PS:顺便提一下其他优化。

1. 不使用栈帧。call一个函数的时候,压入参数,push eip,然后就开始sub esp了,没有保存ebp和ebp切换的工作。

-fomit-frame-pointer

Dump of assembler code for function fib_rw(int, int, int):
   0x080486e4 <+0>:	sub    esp,0x1c                      <----没有针对ebp的操作
   0x080486e7 <+3>:	cmp    DWORD PTR [esp+0x28],0x1
   0x080486ec <+8>:	jg     0x80486f4 
   0x080486ee <+10>:	mov    eax,DWORD PTR [esp+0x24]
   0x080486f2 <+14>:	jmp    0x8048719 
   0x080486f4 <+16>:	mov    eax,DWORD PTR [esp+0x28]
   0x080486f8 <+20>:	lea    edx,[eax-0x1]
   0x080486fb <+23>:	mov    eax,DWORD PTR [esp+0x24]
   0x080486ff <+27>:	mov    ecx,DWORD PTR [esp+0x20]
   0x08048703 <+31>:	add    eax,ecx
   0x08048705 <+33>:	mov    DWORD PTR [esp+0x8],edx
   0x08048709 <+37>:	mov    DWORD PTR [esp+0x4],eax
   0x0804870d <+41>:	mov    eax,DWORD PTR [esp+0x24]
   0x08048711 <+45>:	mov    DWORD PTR [esp],eax
   0x08048714 <+48>:	call   0x80486e4 
   0x08048719 <+53>:	add    esp,0x1c
   0x0804871c <+56>:	ret    
End of assembler dump.

2.循环展开。对编译时能确定执行几次的循环,进行展开。  -funroll-loops

3.尾递归单独的优化选项据说是

-foptimize-sibling-calls.
,但是我单独试了下好像没用。




测试:

#include 
#include 
#include 
using namespace std;

//尾递归
int fib_rw(int a, int b, int n)
{
    if(n<=1)return b;  
   // cout<<"a: "<<&a  <<" b: "<<&b<<" n: "<<&n<=2)
        N=atoi(argv[1]);
    if(argc>=3)
        TIMES=atoi(argv[2]);    
    struct timeval begin,end;
    int re;
    
    ////////////////////////
    gettimeofday(&begin,0); //尾递归
    {
        int i=TIMES;
        while(--i)
        {
            re=fib_rw(1,1,N);
        }
    }
    
    gettimeofday(&end,0); 
    if(begin.tv_usec>end.tv_usec)  
    {
        end.tv_sec--;
        end.tv_usec+=1000000;
    }
    cout<<"尾递归 "<end.tv_usec)  
    {
        end.tv_sec--;
        end.tv_usec+=1000000;
    }
    cout<<"迭代  "< 
 
编译

chen@chen-book1:~$ g++ a.cpp -o a -O2

运行

chen@chen-book1:~$ ./a 5 1000
尾递归 8 time: 0 s 14 us
迭代  8 time: 0 s 12 us
chen@chen-book1:~$ ./a 10 1000
尾递归 89 time: 0 s 31 us
迭代  89 time: 0 s 24 us
chen@chen-book1:~$ ./a 15 1000
尾递归 987 time: 0 s 39 us
迭代  987 time: 0 s 37 us
chen@chen-book1:~$ ./a 20 1000
尾递归 10946 time: 0 s 51 us
迭代  10946 time: 0 s 49 us
chen@chen-book1:~$ ./a 25 1000
尾递归 121393 time: 0 s 61 us
迭代  121393 time: 0 s 62 us
chen@chen-book1:~$ ./a 30 1000
尾递归 1346269 time: 0 s 74 us
迭代  1346269 time: 0 s 74 us
chen@chen-book1:~$ ./a 35 1000
尾递归 14930352 time: 0 s 83 us
迭代  14930352 time: 0 s 87 us
chen@chen-book1:~$ ./a 40 1000
尾递归 165580141 time: 0 s 104 us
迭代  165580141 time: 0 s 100 us
chen@chen-book1:~$ ./a 45 1000
尾递归 1836311903 time: 0 s 106 us
迭代  1836311903 time: 0 s 112 us


蓝的是迭代版本(真直!)红的是尾递归,两者差不多吧~


推荐阅读
  • c语言二元插值,二维线性插值c语言
    c语言二元插值,二维线性插值c语言 ... [详细]
  • 线段树详解与实现
    本文详细介绍了线段树的基本概念及其在编程竞赛中的应用,并提供了一个具体的线段树实现代码示例。 ... [详细]
  • 在1995年,Simon Plouffe 发现了一种特殊的求和方法来表示某些常数。两年后,Bailey 和 Borwein 在他们的论文中发表了这一发现,这种方法被命名为 Bailey-Borwein-Plouffe (BBP) 公式。该问题要求计算圆周率 π 的第 n 个十六进制数字。 ... [详细]
  • 本文通过C++语言实现了一个递归算法,用于解析并计算数学表达式的值。该算法能够处理加法、减法、乘法和除法操作。 ... [详细]
  • 洛谷 P4009 汽车加油行驶问题 解析
    探讨了经典算法题目——汽车加油行驶问题,通过网络流和费用流的视角,深入解析了该问题的解决方案。本文将详细阐述如何利用最短路径算法解决这一问题,并提供详细的代码实现。 ... [详细]
  • 在Qt框架中,信号与槽机制是一种独特的组件间通信方式。本文探讨了这一机制相较于传统的C风格回调函数所具有的优势,并分析了其潜在的不足之处。 ... [详细]
  • 本文详细介绍了如何在循环双链表的指定位置插入新元素的方法,包括必要的步骤和代码示例。 ... [详细]
  • 本文详细介绍了Oracle 11g中的创建表空间的方法,以及如何设置客户端和服务端的基本配置,包括用户管理、环境变量配置等。 ... [详细]
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 本文介绍了如何利用X_CORBA实现远程对象调用,并通过多个示例程序展示了其功能与应用,包括基础的Hello World示例、文件传输工具以及一个完整的聊天系统。 ... [详细]
  • 编译原理中的语法分析方法探讨
    本文探讨了在编译原理课程中遇到的复杂文法问题,特别是当使用SLR(1)文法时遇到的多重规约与移进冲突。文章讨论了可能的解决策略,包括递归下降解析、运算符优先级解析等,并提供了相关示例。 ... [详细]
  • 本文提供了一个使用C语言实现的顺序表区间元素删除功能的完整代码示例。该程序首先初始化一个顺序表,然后根据用户输入的数据进行插入操作,最后根据指定的区间范围删除相应的元素,并输出最终的顺序表。 ... [详细]
  • UVa 1579 - 套娃问题
    本题主要涉及动态规划(DP)的应用,通过计算将前i个套娃合并成多个套娃组所需的最小操作次数来解决问题。具体来说,f(i) 表示前i个套娃合并成多个套娃组所需的操作次数,其计算公式为 f(i) = min(f(j) + dp(j+1, i))。 ... [详细]
  • 本文探讨了如何通过状态压缩动态规划(状压DP)和矩阵快速幂技术来解决公交线路问题。特别地,我们利用连续K个站点的状态来进行状态压缩,并通过矩阵快速幂加速计算过程。 ... [详细]
author-avatar
性感然小猫儿丿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有