本实验是《深入理解计算机系统》一书中的附带实验。在本实验中,学生们必须优化应用程序的核心函数(如卷积积分或矩阵转置)的性能。这个实验非常清晰地表明了高速缓存地特性,并带给学生们低级程序优化的经验。
本文用于记录之前做实验的一些信息,可能思路有些凌乱,谨慎参考!
常用的几种代码优化的思路:
关于第一种优化,思路就是消除循环的低效率,减少在循环内的操作次数;
我们可以观察到原先的代码dst[RIDX(dim-1-j,i,dim)] = src[RIDX(i,j,dim)];
中的j每变一次,dim-1-j就需要计算一次,共计算了 d i m 2 dim^2 dim2次,所以考虑将j换成外层循环变量i,将dim-1-i计算减少到dim次,直接写成dst[RIDX(i,j,dim)] = src[RIDX(j,dim-1-i,dim)];
即可
我们可以观察到原先的代码dst[RIDX(dim-1-j,i,dim)] = src[RIDX(i,j,dim)];
中的j每变一次,dim-1-j就需要计算一次,共计算了 d i m 2 dim^2 dim2次,所以考虑将j换成外层循环变量i,将dim-1-i计算减少到dim次
使用了分块技术,将矩阵分成相同规格的小矩阵,对这些小矩阵进行rotate操作,在这里我给出了将不同将矩阵分成不同规格(8x8、16x16、32x32、64x64)的矩阵的代码并比较性能
使用make
命令之后输入./driver
命令得到不同函数的性能参数
由此可以看出,将其分成64x64的矩阵时程序会出错(因为规模都是32的倍数);且发现程序在将矩阵划分成32x32的小矩阵的时候性能是最好的
利用“循环展开”思想,从减少存储器写次数、用指针代替计算内存地址、写优先和读优先四个角度来实现
(使用32路循环展开,减少存储器的写次数)
(使用循环展开,用指针代替计算内存地址)
(循环展开,采取写优先)
(使用循环展开,采取读优先)
使用make driver
./driver
命令得到不同优化前后函数的性能参数
可以看出使用循环展开,用指针代替计算内存地址的操作能够使函数得到更大的优化;
int tmp1=dim*dim;
int tmp2=dim *31;
int tmp3=tmp1-dim;
int tmp4=tmp1+32;
int tmp5=dim+31;
dst+=tmp3;
这一段代码中的dst就是小矩阵的第一个元素的首地址,将一个32*1的矩阵进行rotate的时候,在矩阵内部,每次执行完一行都只需要让scr+=dim,dst++,当执行完到最后一行的时候,src++,这时候实际上应该要到下一列的第一行,故src-=tmp2,而dst应该到上一行的第一列,故dst-=tmp5;根据二维数组在内存中的存储特征,下一列的第一行的地址为上一列第一行的地址+tmp2,所以src+=tmp2,该行的下一列地址为上一行的第一列+dim+32;故dst+=tmp4;
这就解释了代码中执行这一段的原因
src++;
src-=tmp2;
dst-=tmp5;
}
src+=tmp2;
dst+=tmp4;
将initialize_pixel_sum函数、accumulate_sum函数、assign_sum_to_pixel函数、avg函数都放在smooth函数中实现,减少函数调用
可以看出本次优化效果不明显且存在不稳定性
使用make driver
./driver
命令得到不同优化前后函数的性能参数
可以看到CPE几乎翻了一倍,程序得到较大程度上的优化
优化思路:
使用make driver
./driver
命令得到不同优化前后函数的性能参数
优化思路: