此为本人在进行实验时所做的实验日志,仅供参考。这个应该是汇总版的实验报告,具体的忘了。
PART A
代码一:
void naive_rotate1(int dim,pixel *src,pixel *dst){
int i,j,tmp;//设置一个中间变量tmp,用来存储中间值
for(j=0;j
代码一说明:
这里是书上的第一个方法,消除循环的低效率。通过观察源代码可以发现在循环中对dim-1-j这个数据进行了重复的调用,所以这里可以对循环进行修改,将计算时的行列进行调换,可以提前计算dim-1-j,这样就可以省去每一次循环中重复计算的时间,可以很好的提高效率。
代码二:
void naive_rotate2(int dim,pixel *src,pixel *dst){
int i,j,i1,j1;//将程序分成4*4的小块
for(i1=0;i1
代码二说明:
通过划分成4*4的小方块对整个图进行划分,可以提高空间局部性,但当dim比较小的时候,反而会变慢,因为当dim比较小的时候,决定时间的主要因素是算法复杂度,而分块算法的复杂度比较高。
代码三:
void naive_rotate5(int dim, pixel *src, pixel *dst){
int i,j,tmp1=dim*dim,tmp2=dim *31,tmp3=tmp1-dim,tmp4=tmp1+32,tmp5=dim+31;//定义中间变量
dst+=tmp3;
for(i=0; i< dim; i+=32){
for(j=0;j
代码三说明:
将for循环进行展开,在每一次循环中尽可能多的做操作,以此来达到优化的目的。对于partA这是目前已有的办法中最好的办法。
PART B:
代码一:
void naive_smooth2(int dim, pixel *src, pixel *dst)
{
int i, j;
for (i = 0; i < dim; i=i+4){
for (j = 0; j < dim; j=j+4){//分块执行,每4*4为一块
dst[RIDX(i, j, dim)] = avg(dim, i, j, src);
dst[RIDX(i, j+1, dim)] = avg(dim, i, j+1, src);
dst[RIDX(i, j+2, dim)] = avg(dim, i, j+2, src);
dst[RIDX(i, j+3, dim)] = avg(dim, i, j+3, src);
dst[RIDX(i+1, j, dim)] = avg(dim, i+1, j, src);
dst[RIDX(i+1, j+1, dim)] = avg(dim, i+1, j+1, src);
dst[RIDX(i+1, j+2, dim)] = avg(dim, i+1, j+2, src);
dst[RIDX(i+1, j+3, dim)] = avg(dim, i+1, j+3, src);
dst[RIDX(i+2, j, dim)] = avg(dim, i+2, j, src);
dst[RIDX(i+2, j+1, dim)] = avg(dim, i+2, j+1, src);
dst[RIDX(i+2, j+2, dim)] = avg(dim, i+2, j+2, src);
dst[RIDX(i+2, j+3, dim)] = avg(dim, i+2, j+3, src);
dst[RIDX(i+3, j, dim)] = avg(dim, i+3, j, src);
dst[RIDX(i+3, j+1, dim)] = avg(dim, i+3, j+1, src);
dst[RIDX(i+3, j+2, dim)] = avg(dim, i+3, j+2, src);
dst[RIDX(i+3, j+3, dim)] = avg(dim, i+3, j+3, src);
}
}
}
代码一说明:
通过划分成4*4的小方块对整个图进行划分,可以提高空间局部性,但当dim比较小的时候,反而会变慢,因为当dim比较小的时候,决定时间的主要因素是算法复杂度,而分块算法的复杂度比较高。
代码二:
void naive_smooth5(int dim, pixel *src, pixel *dst){
int i, j;
int ii, jj;
pixel_sum sum;
pixel current_pixel;
for (i = 0; i < dim; i++){
for (j = 0; j < dim; j++){//avg函数展开,直接在主函数中使用
sum.red = sum.green = sum.blue = sum.num = 0;
for(ii = max(i-1, 0); ii <= min(i+1, dim-1); ii++)
for(jj = max(j-1, 0); jj <= min(j+1, dim-1); jj++){
sum.red += (int) src[ii*dim+jj].red;
sum.green += (int) src[ii*dim+jj].green;
sum.blue += (int) src[ii*dim+jj].blue;
sum.num++;
}
current_pixel.red = (unsigned short) (sum.red/sum.num);
current_pixel.green = (unsigned short) (sum.green/sum.num);
current_pixel.blue = (unsigned short) (sum.blue/sum.num);
dst[i*dim+j] =current_pixel;
}
}
}
代码二说明:
这里可以看到有一个交avg的函数被反复的调用了,所以这里我将这个函数直接放到了主函数中,免去了很多函数调用的时间,这种方法相对来讲是比较好的,但仍旧一般
代码三:
void naive_smooth8(int dim, pixel *src, pixel *dst){
int i, j;
pixel current_pixel;
pixel_sum sum;
sum.red = sum.green = sum.blue= 0;//左上角
sum.red=src[0*dim+0].red+src[1*dim+0].red+src[0*dim+1].red+src[1*dim+1].red; sum.green=src[0*dim+0].green+src[1*dim+0].green+src[0*dim+1].green+src[1*dim+1].green;
sum.blue=src[0*dim+0].blue+src[1*dim+0].blue+src[0*dim+1].blue+src[1*dim+1].blue;
current_pixel.red = (unsigned short) (sum.red/4);
current_pixel.green = (unsigned short) (sum.green/4);
current_pixel.blue = (unsigned short) (sum.blue/4);
dst[0] =current_pixel;
sum.red = sum.green = sum.blue= 0;//右上角
sum.red=src[0*dim+dim-1].red+src[1*dim+dim-1].red+src[0*dim+dim-2].red+src[1*dim+dim-2].red;
sum.green=src[0*dim+dim-1].green+src[1*dim+dim-1].green+src[0*dim+dim-2].green+src[1*dim+dim-2].green;
sum.blue=src[0*dim+dim-1].blue+src[1*dim+dim-1].blue+src[0*dim+dim-2].blue+src[1*dim+dim-2].blue;
current_pixel.red = (unsigned short) (sum.red/4);
current_pixel.green = (unsigned short) (sum.green/4);
current_pixel.blue = (unsigned short) (sum.blue/4);
dst[dim-1] =current_pixel;
sum.red = sum.green = sum.blue= 0;//左下角
sum.red=src[(dim-1)*dim+0].red+src[(dim-2)*dim+0].red+src[(dim-1)*dim+1].red+src[(dim-2)*dim+1].red;
sum.green=src[(dim-1)*dim+0].green+src[(dim-2)*dim+0].green+src[(dim-1)*dim+1].green+src[(dim-2)*dim+1].green;
sum.blue=src[(dim-1)*dim+0].blue+src[(dim-2)*dim+0].blue+src[(dim-1)*dim+1].blue+src[(dim-2)*dim+1].blue;
current_pixel.red = (unsigned short) (sum.red/4);
current_pixel.green = (unsigned short) (sum.green/4);
current_pixel.blue = (unsigned short) (sum.blue/4);
dst[dim*dim-dim] =current_pixel;
sum.red = sum.green = sum.blue= 0;//右下角
sum.red=src[(dim-1)*dim+(dim-1)].red+src[(dim-2)*dim+(dim-1)].red+src[(dim-1)*dim+dim-2].red+src[(dim-2)*dim+dim-2].red;
sum.green=src[(dim-1)*dim+(dim-1)].green+src[(dim-2)*dim+(dim-1)].green+src[(dim-1)*dim+dim-2].green+src[(dim-2)*dim+dim-2].green;
sum.blue=src[(dim-1)*dim+(dim-1)].blue+src[(dim-2)*dim+(dim-1)].blue+src[(dim-1)*dim+dim-2].blue+src[(dim-2)*dim+dim-2].blue;
current_pixel.red = (unsigned short) (sum.red/4);
current_pixel.green = (unsigned short) (sum.green/4);
current_pixel.blue = (unsigned short) (sum.blue/4);
dst[dim*dim-1] =current_pixel;
for (j=1;j
代码三说明:
这是最快的算法,将四个角(22),四条边(23),内部(3*3)进行分块计算,直接将avg函数去除,实际上如果再进行并行操作和展开,可能会更快,但如果从单个方法来说,这是最快的方法。
利用amdahl定律分析:
partA初始状态;
代码一优化:
代码二优化:
代码三优化:
可以看到对于part A影响最大的部分是for循环展开,这是因为part A 进行的是一个类似于“复制”的操作,上一次的操作结果不会影响到这次的运行结果,所以可以尽可能多的在依次循环中执行多次操作,这样就可以大大节约从寄存器中取值的时间。另一方面可以对行列的顺序进行交换,这样可以节约每次进行的算数计算的时间,同时因为数据在计算机中的存储方式的原因,如果先执行行再执行列,那么每次对一个数据进行复制的时候都要重新提取两个指针,而如果先执行列再执行行,那么就可以节约重新提取指针的时间,同样可以很强的优化。所以对于part A的优化方法中最有效的是变量提取和函数展开。
partB初始状态:
代码一优化:
代码二优化:
代码三优化:
可以看到对于part B影响最大的部分是avg函数去除。分块计算的效果对于part B影响并不是很明显,可能是因为part B是一个类似于“马赛克”的操作,这就导致了上一次的操作结果会影响到这一次的运行结果,所以操作与操作之间不是并行而是串行的关系,没有办法从增加for单次循环执行操作次数的操作来进行优化。而其原本的avg函数本身的效率并不高,所以只是将avg函数拿到主函数里面虽然可以起到优化的效果,但并不明显。
所以对于这个函数的优化思路应该从修改avg函数本身的方向入手。可以看到avg函数的思路是取九宫格,然后取整个九宫格的平均值给中间的格。而对于角、边的边界情况分别对应的是22和23的矩形,所以我们可以直接将整个avg函数进行修改,直接分成“四个角+四条边+中心内部”三个区域进行计算,从而省去了if条件判断的时间,最终可以达到极强的优化作用。