《深入理解计算机系统》 优化程序性能的几个方法

本文几个优化程序性能的方法出自CSAPP第五章,通过不断修改源代码,试图欺骗编译器产生有效的代码

我们先引入度量标准每元素的周期数(CPE),表示程序性能。

 

我们先定义一个数据结构   data_t 代表数据类型

1 typedef struct{
2   long len;
3   data_t *data;        
4 }vec_rec,*vec_prt;

以及常数IDENT和OP以便在后续的代码中进行不同的操作

//对所有向量的元素求和
#define IDENT 0
#define OP +

//对所有向量元素乘积
#define IDENT 1
#define OP *

我们首先看最初的代码版本,这是一个具有很大优化空间的代码,具体函数实现可参考原书。

 1 void combine1(vec_ptr v, data_t *dest)
 2 {
 3     long int i;
 4     *dest = IDENT;
 5     for (i = 0; i < vec_length(v); i++) {  //vec_length返回向量长度
 6         data_t val;
 7         get_vec_element(v, i, &val);//先进行边界检查再获取索引 i 处的值并赋值给val
 8         *dest = *dest OP val;
 9     }
10 }

 

1.消除循环的低效率

因为每次迭代循环的时候都必须对测试条件求值,但在此循环中,向量的长度值并不会随着循环的进行而改变,因此只需要计算一次vec_length(v)并保存在一个变量中,在后续的循环中使用此变量。

因此我们得到第二个版本的代码。这一常见的优化方式称为 代码移动,即识别要执行多次但值不会改变的代码,将其移动到代码前部分,避免重复求值。

 1 void combine2(vec_ptr v, data_t *dest)
 2 {
 3     long int i;
 4     long int length = vec_length(v);//只进行一次计算
 5     *dest = IDENT;
 6     for (i = 0; i < length; i++) {
 7         data_t val;
 8         get_vec_element(v, i, &val);
 9         *dest = *dest OP val;
10     }
11 }

 

2.减少过程调用

过程调用(函数调用)会带来开销,因此我们增加一个函数 get_vec_start.

1 data_t *get_vec_start(vec_ptr v)
2 {
3   return v->data;  
4 }

由此我们可得第三版代码

void combine3(vec_ptr v,data_t *dest)
{
  long i;
  long length = vec_length(v);
  data_t *data = get_vec_start(v); 

  *dest = IDENT;
  for(i = 0;i<length;i++){
      *dest = *dest OP data[i];  //在循环中减少过程调用
  }
}

 

3.消除不必要的内存引用

虽然我们在第三版的代码中减少了过程的调用,但是第三版的代码相比第二版代码性能并没有明显的提升,这说明第三版中的代码还有别的制约性能的因素。

先看第三版代码的内循环汇编代码:

//dest in %rbx, data+i in %rdx, data+length in %rax

.L17
    vmovsd (%rbx),%xmm0
    vmulsd (%rdx),%xmm0,%xmm0
    vmovsd %xmm0,(%rbx)
    addq $8,%rbx
    cmpq %rax,%rdx
    jne    .L17

由汇编代码可见,第三版的代码对内存进行了两次读操作,一次写操作,通过引入一个临时变量,使其在循环中累计值,在循环结束后再讲值写入内存。

这样我们将循环中的内存操作又两次读一次写减少到一次读操作。程序性能显著提高。

void combine4(vec_ptr v, data_t *dest)
{
    long int i;
    long int length = vec_length(v);
    data_t *data = get_vec_start(v);
    data_t acc = IDENT;
    for (i = 0; i < length; i++) {
        acc = acc OP data[i];
    }
    *dest = acc;
}

 

posted @ 2018-09-21 19:44  DXYE  阅读(922)  评论(0编辑  收藏  举报