考察以下代码:
#define IDENT 0 #define OP * typedef int data_t; typedef struct { long int len; data_t *data; }vec_rec, *vecptr; vec_ptr NewVec(long int len) { vec_ptr result = (vec_ptr) malloc(sizeof(vec_rec)); if(!result){ return NULL; } result-> len = len; if(len > 0){ data_t *data = (data_t*)calloc(len, sizeof(data_t)); if(!data){ free((void*) result); return NULL; } result->data = data; } else{ result->data = NULL; } return result; } //Retrieve vector elemernt and store at dest int GetVecElement(vec_ptr v, long int index, data_t *dest) { if(index < 0 || index >= v->len){ return 0; } *dest = v->data[index]; return 1; } long int VecLength(vec_ptr v) { return v->len; } void Combine1(vec_ptr v, data_t *dest) { *dest = IDENT; for(long int i = 0; i < VecLength(v); ++i){ data_t val; GetVecElement(v, i, &val); *dest = *dest OP val; } }
其中对于 Combine1(), 每次在 for 循环中进行检查时, 都要调用 VecLength() 获取链表长度, 可是在此例中链表的长度是不变的, 因此在首次获取链表长度之后, 接下来对 VecLength() 就成了累赘, 优化的方法就是消除循环的低效率:
//消除循环低效率后的代码 void Combine2(vec_ptr v, data_t *dest) { long int length = VecLength(v); *dest = IDENT; for(long int i = 0; i < length; ++i){ data_t val; GetVecElemet(v, i, &val); *dest = *dest OP val; } }
对于 Combine2(), 每次循环迭代都会调用 GetVecElement() 来获取下一个向量元素, 对每一个向量引用都要把索引做边界检查明显会造成低效率, 所以就需要增加一个函数 GetVecStart() 这个函数来返回数组的起始地址, 从而直接访问数组以避免调用函数来获取每个元素.
//减少过程调用后的代码 data_t* GetVecStart(vec_ptr v) { return v->data; } void Combine3(vec_ptr v, deta_t *dest) { long int length = vec_length(v); data_t *data = GetVecStart(v); *dest = IDENT; for(long int i = 0; i < length; ++i){ *dest = *dest OP data[i]; } }
在 Combine3 的 i 次迭代中, 程序需要读出指针 dest 所存放的寄存器的位置的值, 并乘以 deta[i], 再将结果存回 dest. 这样做很浪费, 因为每次迭代开始时从 dest 读出的值就是上次迭代最后写入的值. 解决方法就是引入一个临时变量存放在寄存器中来累计计算出来的值, 只有在循环完成之后才存放进 dest 中, 于是我们就把每次迭代的储存器操作从两次读和一次写减少到只需要一次读.
//消除不必要的储存器引用后的代码 void Conbine4(vec_ptr v, data_t *dest) { long int length = VecLength(v); data_t *data = GetVecStart(v); data acc = IDENT; //use local variable for(long int i = 0; i < length; ++i){ acc = acc OP data[i]; } *dest = acc; }