最近在图书馆随便翻翻的的时候,看到一本因特尔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