博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2.2优化编译器的能力和局限性
阅读量:6639 次
发布时间:2019-06-25

本文共 1984 字,大约阅读时间需要 6 分钟。

写程序最主要的目标是使他在所有的可能的情况下都能正确工作。程序应该写出清晰简单的代码,主要为了给后期维护,起作用;但常常我们在程序的简单性,维护性,与程序的运行速度进行权衡;

 

高效的程序需要几类活动:

(1)必须选择一组合适的算法和数据结构;

(2)必须编写出编译器能够优化以转换成高效可执行的源代码,因此理解优化编译器的能力和局限性很重要,因此程序即使是很小的变动,会引起编译器优化方式很大的变化;因此程序员经常能够使编译器更容易产生高效代码的方式来编写他们的程序,对于C语言的指针,就编译器已经很难优化了;

(3)并发编程,利用多处理器和多核的某种组合并行的计算;

 

现代编译器使用复杂精细的算法来确定一个程序中计算的是什么值,以及它们是如何使用的,然后利用一些机会来简化表达式,在几个不同的地方使用用一个计算,降低给定计算的执行次数;GCC编译器提供优化级别的控制,级别越高,程序的规模可能会提高,调试困难会提高;低级别的优化高效的C代码,要比高级别的优化初级的C代码性能要高;

 

要是编译器安全的优化,程序员必须花大力气编写编译器可以转换成有效的代码,举例如下:

(1)*x += * y; 

          *x += *y;

与(2)*x += 2 * *y;

这两个代码片段似乎有相同的行为;(2)要快一点,涉及3次存储器引用,而(1)涉及6次存储器引用,但是如果考虑*x = *y,则(1)会增加4倍的值,而(2)是增加3倍的值,此差别造成编译器对这两个片段的优化是不同的,在进行安全的优化时,编译器会假设两个指针指向同一个位置;

 

大多数的编译器不会试图判断一个函数是否有副作用,会优化可能的候选函数,成为(2)的例子,因此可能造成值得不同,如下:

(1)return f()+f()

(2)return 2 * f()

f(){

  reurn counter++;

}

 

注意,GCC完成最基本的优化,因此需要花费更多的精力,以一种简化编译器生成高效代码的任务方式来编写程序。

 

局限性:

考察以下代码:

void Twiddle1(int *xp, int *yp){    *xp += *yp;    *xp += *yp;}void Twiddle2(int *xp, *yp){    *xp += 2 * *yp;}

这两个过程等价吗? 事实上 Twiddle2 的效率更高, 因为它只要求 3 次存储器引用, 而 Twiddle1 需要 6 次, 但它们不能相互代替, 因为当 xp == yp 时,

*xp += *xp;*xp += *xp;

之后, xp 的值是原来的 4 倍, 但是类似 Twiddle2 的操作:

*xp += 2 * *xp;

的结果却是原来的 3 倍. 所以编译器不会产生 Twiddle2 的代码作为 Twiddle1 的优化代码.

考察以下代码:

x = 1000; y = 3000;*q = y;*p = x;t1 = *q    // ?

t1 的值依赖于指针 p 和 q 的位置关系, 如果 p == q, 那么 t1 == 1000; 如果不相等, t1 == 3000. 这就是一种妨碍优化的因素, 它严重限制了编译器产生优化代码的机会和优化策略.

两个指针可能指向同一个储存器的位置的情况称为储存器别名使用(memory aliasing). 在直至项安全优化的代码中, 编译器需要假设储存器别名使用的情况.
第二个妨碍优化的因素是函数调用, 考察以下代码:

int F();int Func1(){    return F() + F() + F() + F();}int Func2(){    return 4 *  F();}

看上去似乎是相同的结果, 但是 Func2() 只调用 F() 1 次, 而 Func1() 调用 4 次, 考虑以下代码:

int counter = 0;int F(){    return counter++;}

对于 Func1() 我们可以用内联函数进行优化即将函数调用替换为函数体:

//优化后的版本int Funct1Inline(){    int t = counter++;    // +0    t = counter++;        // +1    t = counter++;        // +2    t = counter++;        // +3    return t;}

这样的转换不仅可以减少了函数调用的开销, 也允许编译器对代码进行进一步优化:

//编译器进一步优化int Func1Opt(){    int t = 4 * counter + 6;    counter = t + 4;    return t;}

 

转载地址:http://fravo.baihongyu.com/

你可能感兴趣的文章
Linux中的计划任务at命令的用法
查看>>
使用游标——游标FOR循环
查看>>
5月收集
查看>>
Javascript获取DropDownList选项信息
查看>>
javaScript 正则表达式2
查看>>
原创:Qt自定义拖放
查看>>
策略模式
查看>>
(转)RGB接口和i80接口的区别
查看>>
6月21小记
查看>>
RESTful API 设计最佳实践
查看>>
定义文件XML——从简单开始
查看>>
android辅助开发工具包介绍
查看>>
JSP中scope属性 scope属性决定了JavaBean对象存在的范围
查看>>
Linux设备驱动开发详解-Note(11)--- Linux 文件系统与设备文件系统(3)
查看>>
实习第一天之数据绑定:<%#Eval("PartyName")%>'
查看>>
POJ 2318 TOYS (计算几何,叉积判断)
查看>>
第四章 Spring与JDBC的整合
查看>>
开源的Android视频播放器
查看>>
Java多线程-概念与原理
查看>>
“无法在web服务器上启动调试,不是Debugger User组成员..."
查看>>