优化编译器的能力以及局限
- 编译器使用精密的算法来确定一个程序中计算的是什么值,以及它是如何被使用的
- 然后编译器会利用一些机会简化表达式,在几个不同的地方使用同一个计算
- 最简单的优化方法就是定制优化级别,比如GCC的”-O1”,”-O2”等,但是会增加程序规模
通过以下代码展示如何通过代码影响编译程序性能
void test1(int *p, int *t){
*p += *t;
*p += *t;
}
void test2(int *p, int *t){
*p += 2* *t;
}
这两个程序似乎有相同的行为,都是将储存在t指针位置的值两次加到p指针指向的位置处,
但是可以发现函数test2更加高效一些,它只要三次内存引用(读p,读t,写p)
而test1则需要2次读p,2次读y,2次写p,所以test2比test1性能要高些
简单的优化代码方法
消除循环的低效率
void test1(strint str){
for(int i = 0; i < str.length(); i++){
...
}
}
void test2(string str){
int n = str.length();
for(int i = 0; i < n; i++){
...
}
}
可以观察到在test1函数中,str.length()作为for循环的测试条件,每次循环迭代的时候
都必须测试条件求值。并且str的长度不会随着循环的改变而改变
而test2着这个测试条件移出了for循环,提升了代码性能
这类优化称为代码移动,主要是识别要执行多次(例如在循环里)但是计算结果不会改变的的计算
因而可以将这部分移出循环中
减少过程调用
过程调用会带来相当大的开销,并且妨碍大多数编译器优化,例如
void test1(strint str){
for(int i = 0; i < str.length(); i++){
int val;
get_next_element(v,i,&val);
...
}
}
每次循环都会调用get_next_lelment()函数,使得效率低下
作为代替我们增加一个函数get_next_start(),这个函数返回数组的起始地址,
在循环中么有函数调用,而是直接访问数组
int get_next_start(string str){
return &str;
}
void test1(strint str){
string *s = get_next_start(str);
for(int i = 0; i < str.length(); i++){
int val;
s[i]...
...
}
}
最下面那段代码???