0%

CSAPP优化程序性能

优化程序性能

来源于CSAPP第五章

消除不必要的内存引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//可以设计不同的数据类型来测量程序性能
typedef long data_t;
typedef struct
{
long len;
data_t *data;
} vec_rec, *vec_ptr;

//设计对数据执行不同的运算
#define IDENT 0 //求和,如果是求乘积设为1
#define OP + //如果求乘积设为*

void combin3(vec_ptr v, data_t* dest)
{
long i;
long length = vec_length(v); //减少循环中的函数调用
data_t* data = get_vec_start(v); //减少过程调用的优化

*dest = IDENT;

for(int i = 0; i < length; ++i)
{
*dest = *dest OP data[i];
}
}

上述代码的汇编码如下:

1
2
3
4
5
6
7
8
# vmovsd代表从指定的位置读数据
.L17: # loop:
vmovsd (%rbx), %xmm0 # Read product from dest 指针dest的地址从%xmm0读出来,存在寄存器%rbx中
vmulsd (%rdx), %xmm0 # Multiply product by data[i] 第i个元素的指针保存在%rdx中
vmovsd %xmm0, (%rbx) # Store product at dest 将rbx中的数据读出,存入%xmm0中
addq $8, %rdx # Increment data + i
cmpq %rax, %rdx # Compare to data+length data+length存放在%rax中
jne .L17 # If !=, goto loop

进入循环后,从内存中读数据,和data[i]乘,再写入到内存中。这种操作比较浪费,因为循环下次读的数据是上次写入内存的数据。

可以用一个临时变量来存储结果,等循环结束后再写入内存中。这样每次循环可以减少一次写的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void combin4(vec_ptr v, data_t* dest)
{
long i;
long length = vec_length(v);
data_t* data = get_vec_start(v);
data_t acc = IDENT; //temp variable

for(int i = 0; i < length; ++i)
{
acc = acc OP data[i];
}

*data = acc;
}

值得注意的是,从代码层面看指令是一条一条执行的,但是实际处理器中是同时对多条指令求值,这种现象称为指令级并行。所以这两种操作限制了程序的瓶颈:第一个是指令必须按照严格顺序执行,那就会遇到延迟界限;第二个是吞吐量界限(取决于处理器原始计算能力)。