Gcc 优化选项 与尾递归优化

今天做高性能计算机系统的作业的时候,发现gcc中的优化选项有很多应用 。

例如对于C源码:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int x[101],y[101];
    int a,i;
    a = 5;
    for(i=0;i<=100;i++)
    {
        x[i] = i+1;
        y[i] = i;
    }

    for(i=100; i>=0; i--)
        y[i] += a*x[i];
    return 0;
}

1、直接用gcc main.c –S –O0进行编译,即禁止编译器进行优化,生成的汇编语言文件为:

 .file  "main.c"

         .def  ___main; .scl   2;      .type         32;   .endef

         .text

.globl _main

         .def  _main;      .scl   2;      .type         32;   .endef

_main:

         pushl         %ebp

         movl %esp, %ebp

         andl $-16, %esp

         subl  $816, %esp

         call   ___main

         movl $5, 808(%esp)

         movl $0, 812(%esp)

         jmp  L2

L3:

         movl 812(%esp), %eax

         movl 812(%esp), %edx

         incl   %edx

         movl %edx, 404(%esp,%eax,4)

         movl 812(%esp), %eax

         movl 812(%esp), %edx

         movl %edx, (%esp,%eax,4)

         incl   812(%esp)

L2:

         cmpl $100, 812(%esp)

         jle     L3

         movl $100, 812(%esp)

         jmp  L4

L5:

         movl 812(%esp), %eax

         movl 812(%esp), %edx

         movl (%esp,%edx,4), %ecx

         movl 812(%esp), %edx

         movl 404(%esp,%edx,4), %edx

         imull 808(%esp), %edx

         leal   (%ecx,%edx), %edx

         movl %edx, (%esp,%eax,4)

         decl  812(%esp)

L4:

         cmpl $0, 812(%esp)

         jns    L5

         movl $0, %eax

         leave

         ret

2、当用gcc main.c –S –O1进行编译的时候,尝试优化编译时间和可执行文件大小等,汇编程序如下:

 1      .file  "main.c"
 2
 3          .def  ___main; .scl   2;      .type         32;   .endef
 4
 5          .text
 6
 7 .globl _main
 8
 9          .def  _main;      .scl   2;      .type         32;   .endef
10
11 _main:
12
13          pushl         %ebp
14
15          movl %esp, %ebp
16
17          andl $-16, %esp
18
19          call   ___main
20
21          movl $0, %eax
22
23          leave
24
25          ret

3、当用gcc main.c –S –O2进行编译的时候,会进行部分优化,但不进行循环展开和函数内联等操作,相比于O1的1级优化得到的汇编代码,多了一条对齐指令即.p2align 2,3;汇编程序如下:

 1  .file  "main.c"
 2
 3          .def  ___main; .scl   2;      .type         32;   .endef
 4
 5          .text
 6
 7          .p2align 2,,3
 8
 9 .globl _main
10
11          .def  _main;      .scl   2;      .type         32;   .endef
12
13 _main:
14
15          pushl         %ebp
16
17          movl %esp, %ebp
18
19          andl $-16, %esp
20
21          call   ___main
22
23          xorl  %eax, %eax
24
25          leave
26
27          ret

4、用gcc main.c –S –O3进行优化时,会进行循环展开,分支预测,函数内联等,但与O2的2级优化得到的汇编代码一样,可能是因为在O2和O3的Gcc都能识别尾递归调用并进行优化,所以在这里使用了尾调用方式,从代码中也可以看到有一条递归调用指令call main。查资料得到,实现尾递归优化的选项是-foptimize-sibling-calls,是O2新增的的功能。汇编程序如下:

 1  .file  "main.c"
 2
 3          .def  ___main; .scl   2;      .type         32;   .endef
 4
 5          .text
 6
 7          .p2align 2,,3
 8
 9 .globl _main
10
11          .def  _main;      .scl   2;      .type         32;   .endef
12
13 _main:
14
15          pushl         %ebp
16
17          movl %esp, %ebp
18
19          andl $-16, %esp
20
21          call   ___main
22
23          xorl  %eax, %eax
24
25          leave
26
27          ret

说到尾递归,则要说到递归调用。尾递归的wiki解释如下:

尾部递归是一种编程技巧。递归函数是指一些会在函数内调用自己的函数,如果在递归函数中,递归调用返回的结果总被直接返回,则称为尾部递归。尾部递归的函数有助将算法转化成函数编程语言,而且从编译器角度来说,亦容易优化成为普通循环。这是因为从电脑的基本面来说,所有的循环都是利用重复移跳到代码的开头来实现的。如果有尾部归递,就只需要叠套一个堆栈,因为电脑只需要将函数的参数改变再重新调用一次。利用尾部递归最主要的目的是要优化,例如在Scheme语言中,明确规定必须针对尾部递归作优化。[1][2]可见尾部递归的作用,是非常依赖于具体实现的。(http://zh.wikipedia.org/wiki/%E5%B0%BE%E9%80%92%E5%BD%92)

这种调用总是在函数末尾执行,并且不会用到调用函数里的任何局部变量。所以有些编译器对此进行优化,在被调用函数执行时,直接利用调用函数的堆栈,不需要重新开辟堆栈空间,所以一般不会导致递归中出现的栈溢出。而一般递归因为调用过程中会存储局部变量,所以调用次数太多时就会发生溢出。但是并不是所有编译器都会对尾递归进行优化,一般在函数式编程语言中会优化。所以当在gcc中用了-O2或者-O3优化选项之后,就会对尾递归进行优化,一般不会造成溢出,而默认的进行可能会造成溢出。可以参考这篇博文:http://www.cnblogs.com/JeffreyZhao/archive/2009/04/01/tail-recursion-explanation.html。其中说到:与普通递归相比,由于尾递归的调用处于方法的最后,因此方法之前所积累下的各种状态对于递归调用结果已经没有任何意义,因此完全可以把本次方法中留在堆栈中的数据完全清除,把空间让给最后的递归调用。这样的优化便使得递归不会在调用堆栈上产生堆积,意味着即时是“无限”递归也不会让堆栈溢出。

时间: 2024-10-12 14:40:24

Gcc 优化选项 与尾递归优化的相关文章

GCC 优化选项 -O1 -O2 -O3 -OS 优先级,-FOMIT-FRAME-POINTER(O3的优化很小,只增加了几条优化而已)

四种编译优化类型的解释: `-O ' `-O1 '                 Optimize.      Optimizing   compilation   takes   somewhat   more   time,   and   a                 lot   more   memory   for   a   large   function.                   With   `-O ',   the   compiler   tries  

Python开启尾递归优化!

本文和大家分享的主要是python中尾递归相关使用方法,希望通过本文的分享能帮助大家更好的学习python语言,一起来看看吧. 一般递归与尾递归 一般递归 def normal_recursion(n): if n == 1: return 1 else: return n + normal_recursion(n-1) 执行: normal_recursion(5)5 + normal_recursion(4)5 + 4 + normal_recursion(3)5 + 4 + 3 + nor

C程序编译过程及优化选项介绍

当我们编译了一个c文件,最终目的是让它能够成为可以执行的代码,就是能够受控或者控制硬件的东西.控制硬件的东西,一般都是二进制代码.那么,问题来了,从c到控制硬件的二进制代码,中间经过了一个什么流程呢?它的过程是这样的: 一,编译预处理 读取c源文件,对其中的伪指令(以#开头)和特殊符号进行处理: 伪指令包括:宏定义: 比如:#defile PI (3.1415) 条件编译:#if (conditions) {do something} #else {do another thing} #endi

JVM原生不支持尾递归优化,但是Scala编译器支持

The JVM doesn't support TCO natively, so tail recursive methods will need to rely on the Scala compiler performing the optimization.----------"Scala in Depth" 3.5.2 Jvm本身是不支持尾递归优化得,需要编译器支持,而Java编译器不支持,但是Scala支持.写一个简单的计算1到n的和的递归算法验证一下. public cla

PNG与iOS优化选项

从App Store下载到的每一枚App最初都是一只IPA文件(其实是zip格式,内含特定规则的文件夹组织方式).但当作zip解开之后会发现里面很多的PNG文件看不了,这是因为在这些PNG图像都已被iOS开发工具「优化」过了. Bjango.com的一篇文章提到了不管开发者事先对PNG做了何种优化(例如优化色彩范围这种),最终得到的结果都是一样的文件尺寸-因为Xcode编译后把所有PNG都按自己的方式再处理了. 苹果究竟要对每个PNG做了什么?为啥呢. 苹果的提供的开发者文档以及各种会议/视频中

对SNL语言的解释器实现尾递归优化

对于SNL语言解释器的内容可以参考我的前一篇文章<使用antlr4及java实现snl语言的解释器>.此文只讲一下"尾递归优化"是如何实现的--"尾递归优化"并不是一个语言实现必须要做的,但这是一个比较有趣的东西,所以我还是想拿来讲一讲. 在前一篇文章中有一个例子: program recursion    procedure f(integer d);    begin        write(d);        f(d + 1)    endbe

.NET 4.6的RyuJIT尾递归优化的Bug

今天看到园子里有一篇新闻稿.NET 4.6的RyuJIT编译器中发现严重的Bug提到,在.Net 4.6的x64程序中默认启用新的JIT程序RyuJIT在处理尾递归指令的时候有一个Bug,导致无法得到正确的结果. 微软在其官方BlogRyuJIT Bug Advisory in the .NET Framework 4.6更是较为详细的介绍了这一bug.虽然尾递归使用得并不多(貌似在F#中有很多应用),但这个bug算是比较严重的了: 这个问题只有在应用了代码优化之后才会出现,由于多数开发者与项目

一个很Cool的Idear-&gt;Python的尾递归优化

偶然在国外一个网站瞅到的,非常的酷,发出来共享一下.一般来说,Python和Java,C#一样是没有尾递归自动优化的能力的,递归调用受到调用栈长度的限制被广泛的诟病,但是这个狂人用一个匪夷所思的方法解决了这个问题并在Python上实现了,从此Python的递归调用再也不用受到调用栈长度的制约,太酷了. 首先我们还是从递归说起,之前我发过一篇 <浅谈递归过程以及递归的优化>其中用到了斐波那契数来作为例子.线性递归的算法由于太过一低效就被我们Pass掉了,我们先来看尾递过方式的调用: 1 def 

尾递归优化

来自廖雪峰的官方网站 fact(n)可以表示为n x fact(n-1),只有n=1时需要特殊处理. 于是,fact(n)用递归的方式写出来就是: def fact(n): if n==1: return 1 return n * fact(n - 1) 上面就是一个递归函数.可以试试: >>> fact(1) 1 >>> fact(5) 120 >>> fact(100) 933262154439441526816992388562667004907