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
文章未经本人允许,禁止以任何形式复制,转载;使用在其他任何未经本人允许的地方。若要转载请留言联系。