最近在图书馆随便翻翻的的时候,看到一本因特尔Intel出的《多核多线程技术》,冲着名字,也得拿起来看看啊。翻过之后,内容泛泛,感觉是一门入门读物。但就是这本薄薄的一本小书,定价愣是为49.5元,可能就是因为被惯了intel的名字吧。如果希望看,还是到读书馆去看吧。
在里面看到了一个小例子,说的是有关“
性能调优方法
”的,还是有点意思的。
数组累加和问题描述
已知:
有一个结构体,保存两个变量:一个是指向整型数组的指针,一个该数组的长度
问题:
求这个数组中所有元素的累加和,就是所有项的和
数据定义和相关方法:
如下所示
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int len;
int *data;
} vec_rec, *vec_ptr;
//分配内存,参数len表示结构体中的数组长度
vec_ptr new_vec( int len ) {
//动态分配结构体内存
vec_ptr result = (vec_ptr)malloc( sizeof(vec_rec) );
if(!result) {
return NULL;
}
if(len>0) {
//动态分配数组内存
int *data = (int *) calloc(len, sizeof(int));
if(!data) {
free( result );
return NULL;
}
result->len = len;
result->data = data;
} else { //len小于0
result->data = NULL;
result->len = 0;
}
return result;
}
//获得数组指定下标的元素,在参数dest中返回,函数的结果表示是否成功获得元素值
int get_vec_element( vec_ptr v, int index, int *dest ){
if( index < 0 || index >= v->len ){
return 0;
}
*dest = v->data[index];
return 1;
}
//获得结构体中的数组长度
int vec_length(vec_ptr v){
return v->len;
}
//获得数组的起始位置
int * get_vec_start(vec_ptr v){
return v->data;
}
初步解答代码如下:
初步解法
程序很简单,写出的计算代码和测试代码如下所示:
//第一次实现
void combine1(vec_ptr v, int* dest){
int i;
*dest = 0;
for(i=0; i<vec_length(v); i++){
int val;
get_vec_element(v, i, &val);
*dest = *dest + val;
}
}
int main(int argc, char **argv) {
vec_ptr p = new_vec(5);
//给结构体中的数组赋初值
int i;
for(i=0; i<5; i++){
p->data[i] = i+1;
}
//计算累加和
int result=0;
combine1(p, &result);
printf("result: %d /n", result);//打印结果为15
return 0;
}
优化一:消除循环不变量
在combine方法的for循环中,验证条件 i<vec_length(v) 每次都要调用vec_length()函数,而实际上每次返回的值都是一样的。
如果验证条件里有函数调用,编译器是没有办法进行优化的。
既然vec_length的返回值是不变的,将其提取出来,代码可以修改成下面的形式:
//优化一:消除循环不变量
void combine2(vec_ptr v, int* dest){
int i;
*dest = 0;
int length = vec_length(v); //提取vec_length(v)
for(i=0; i<length; i++){
int val;
get_vec_element(v, i, &val);
*dest = *dest + val;
}
}
优化二:减少不必要的函数调用
在combine方法中,每次都要调用get_vec_element()函数,在该函数内部首先判断下标是否合法。这里包含
两个
开销:一个是函数调用(涉及到上下会转换,指令跳转等);一个是函数内部的条件判断;条件判断检查牵扯到条件转移指令,进而牵扯到操作系统的流水线机制中“猜测式执行”,可能会造成指令流水线停顿或回溯。
可以利用数组元素每个元素都
连续存放
的特点,对程序进行下面优化:
//优化二:消除不必要的函数调用
void combine3(vec_ptr v, int* dest){
int i;
*dest = 0;
int length = vec_length(v);
int * data = get_vec_start(v);//获取开始位置
for(i=0; i<length; i++){
*dest += data[i];//利用数组元素的连续存放性
}
}
优化三:消除不必要的内存读取
在combine方法中,循环体内每次都要对*dest执行读取操作,由于 *dest += data[i] 相当于 *dest += *dest +data[i] ,所以每次循环要进行2次内存读、1次内存写,代价比较昂贵。
如果你知道,
函数中的 局部变量 一般会放到寄存器中
,你就可以进行下面优化:
//优化三:消除不必要的内存读取
void combine4(vec_ptr v, int* dest){
int i;
//*dest = 0; //该行可以去掉
int result = 0; //利用局部变量保存结果
int length = vec_length(v);
int * data = get_vec_start(v);
for(i=0; i<length; i++){
result += data[i];
}
*dest = result; //最后将结果赋值给 *dest
}
总结
在你确定了程序使用的
数据结构、算法、
以及
整体结构
之后,还有几个小技巧可以继续优化你的程序:
1. 避免多余的函数调用;
2. 避免多余的条件判断和边界检查;
3. 利用局部变量保存中间计算结果;
本文链接:
http://blog.csdn.net/daheiantian/archive/2011/02/23/6452199.aspx