C语言循环优化二三事(一)

一.代码移动

将在循环里面多次计算,但是结果不会改变的计算,移到循环外面去。

例子:

优化前:

void lower1(char *s){
int i;
for(i=0;i<strlen(s);++i)
     if(s[i]>='A'&&s[i]<='Z')
        s[i]-=('A'-'a');
}

优化后:

void lower2(char *s){
int i;
int len=strlen(s);
for(int i=0;i<len;++i)
    if(s[i]>='A'&&s[i]<='Z')
        s[i]-=('A'-'a');
}

优化前的版本,由于每次循环都要调用strlen计算s的长度,实际上的复杂度成了O(n2)了,而优化后的版本只需计算一次s的长度,因此性能上比优化前版本要好。

二.减少函数调用

例子:

优化前:

void sum1(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
*dest=0;
for(i=0;i<len;++i){
    data_t val;
    get_vec_element(v,i,&val);
    *dest+=val;
}
}

优化后:

data_t get_vec_start(vec_ptr v){
return v->data;
}

void sum2(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
data_t  *data=get_vec_start(v);
*dest=0;
for(i=0;i<len;++i)
    *dest+=data[i];
}

优化前的版本在每次循环中都要调用一次get_vec_element获得相应的项,而优化后的版本只需在循环外调用一次get_vec_start获得开始的内存地址,循环内直接访问内存,无需调用函数。

三.减少内存访问

例子:

优化前:

void sum2(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
data_t  *data=get_vec_start(v);
*dest=0;
for(i=0;i<len;++i)
    *dest+=data[i];
}

优化后:

void sum3(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
data_t *data=get_vec_start(v);
data_t acc=0;
for(i=0;i<len;++i)
  acc+=data[i];
*dest=acc;
}

优化前的版本每次迭代都要从dest读出值再加上data[i],再将结果写回dest。这样的读写很浪费,因此每次迭代开始从dest读出的值就是上次迭代写回dest的指。优化后的版本通过加入acc临时变量,它循环中累积计算出的结果,循环结束后再写回。

这里给出两个版本相应的汇编结果就可以很清楚看出区别:

优化前:

第二行和第四行分别对dest进行了读写。

优化后:

从汇编结果可以看出编译器将acc直接放在了寄存器里,循环中无需对内存进行读写。

  以上三种优化方法不依赖于特定机器的特性,有较强的通用性。

参考文献:CSAPP 2nd edition

posted on 2012-11-14 18:38  lisperl  阅读(7389)  评论(10编辑  收藏  举报