在优化代码之前,首先要知道我们有哪些方法可以优化性能?
翻书!把教材翻到目录,第五章赫然写着优化方法:
1、使用内联函数。一种替换代码方法,尽可能减少函数调用;
2、消除循环中的低效率。比如说for循环判断里面带了个函数,那么就属于低效率的循环,从时间复杂度上面也能分析;
3、减少过程调用。比如说经常需要先取数,再操作,那么优化的方法通常是把这些数安排在连续的地址里面,这样可以减少地址的计算;
4、消除不必要的存储器引用。在看书之前,我认为是减少局部变量的定义,毕竟定义一个变量就要占一个寄存器,但是书上的意思是尽量拿一个变量专门用来做某一件特定的事情,举个例子,inti就是用来for循环的,不要再拿来做计数器或者权值计算器了,如果要统计和,就应该再定义一个sum,避免汇编代码层次中,要额外使用一个寄存器临时保存变量的值。
5、循环展开。减少for循环的步长,每一次循环,尽可能的让cpu做更多的运算,提高并行性,充分发挥流水的作用。
这个实验妙处在于可以融合以上所有优化方法,容我慢慢道来~
首先看smooth函数initial版本:
char naive_smooth_descr[] = "naive_smooth: Naive baselineimplementation";
void naive_smooth(int dim, pixel *src, pixel *dst)
{
int i, j;
for (i = 0; i < dim; i++)
for(j = 0; j < dim; j++)
dst[RIDX(i, j, dim)] =avg(dim, i, j, src);
}
首先要明白RIDX(i,j, dim)是什么?
在头文件def.h中,写着#defineRIDX(i,j,n) ((i)*(n)+(j))
这是我们平时不常接触的函数型宏定义,传入i和j还有n,就计算出(i)*(n)+(j),这是dst的地址。
作为一个二维的图片,通常是用二维数组存储的,但是二维数组在内存中依然是连续的物理地址。可以把dst[RIDX(i,j, dim)] 直接理解为二维数组a[i][j]。
然后问题来了,avg(dim,i, j, src) 又是什么?直觉告诉我它是一个函数!
果然在同一个文件下面找到了这个函数:
static pixel avg(int dim, int i, int j, pixel *src) //因为缺点出于函数内部,所以这个函数无法使用
{
int ii, jj;
pixel_sum sum;
pixel current_pixel;
initialize_pixel_sum(&sum);
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++)
accumulate_sum(&sum,src[RIDX(ii, jj, dim)]);
assign_sum_to_pixel(¤t_pixel, sum);
return current_pixel;
}
这个函数有个问题:
就是循环中的低效率! max(i-1, 0) 和min(i+1, dim-1)都是函数,每次for循环判断条件的时候,都要调用函数min(i+1, dim-1),所以我们首先想到的是消除循环中的低效率。
但是,这个for循环被封装在avg函数里面了,怎么办?只能放弃这个函数了,我们自己写函数。
我们首先看计算像素点平均颜色是如何计算的?
static void accumulate_sum(pixel_sum *sum, pixel p) //统计颜色数据
{
sum->red += (int) p.red;
sum->green += (int) p.green;
sum->blue += (int) p.blue;
sum->num++;
return;
}
static void assign_sum_to_pixel(pixel *current_pixel, pixel_sumsum) //计算平均值
{
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);
return;
}
通过以上两个函数,我们看到了原理,就是把相邻的像素点的RGB颜色各取平均值。
现在开始负优化啦~~~
对于32*32的图片,关键是要对四个角以及边框进行讨论。如何避免讨论呢?在迷宫问题中,解决方法就是在它的外面加一个边框。
变成了:
可以看出,原来的dim边长变成了newdim=dim+2的边长。
主要思路:
1、 把原图移位
2、 边框加上标记值
3、 双层for循环遍历,引入计数器,判断相邻像素是否有标记,(若不是标记就count++)求平均值
我们先来看看头文件以及drive.c文件,里面大有乾坤啊~
/*Align the images to BSIZE byte boundaries */
orig = data;
while ((unsigned)orig %BSIZE)
orig = (pixel *)((char *)orig) + 1;
result = orig + dim*dim;
copy_of_orig = result + dim*dim;
//fuck
for (i = 0; i < dim; i++) {
for (j = 0; j < dim; j++) {
/* Original imageinitialized to random colors */
orig[RIDX(i,j,dim)].red =random_in_interval(0, 65536);
orig[RIDX(i,j,dim)].green =random_in_interval(0, 65536);
orig[RIDX(i,j,dim)].blue =random_in_interval(0, 65536);
/* Copy of originalimage for checking result */
copy_of_orig[RIDX(i,j,dim)].red = orig[RIDX(i,j,dim)].red;
copy_of_orig[RIDX(i,j,dim)].green = orig[RIDX(i,j,dim)].green;
copy_of_orig[RIDX(i,j,dim)].blue = orig[RIDX(i,j,dim)].blue;
/* Result imageinitialized to all black */
result[RIDX(i,j,dim)].red = 0;
result[RIDX(i,j,dim)].green = 0;
result[RIDX(i,j,dim)].blue = 0;
}
}
看到了什么?图像复制代码,是用来评价我们的算法最后是否正确的,所以它备份了图片。
从下面的代码也可以看出来:
static int check_orig(intdim)
{
int i, j;
for (i = 0; i < dim; i++)
for (j = 0; j < dim; j++) //fuck
if(compare_pixels(orig[RIDX(i,j,dim)], copy_of_orig[RIDX(i,j,dim)])) {
printf("\n");
printf("Error: Original image has beenchanged!\n");//报错语句原来是从这里输出的
return 1;
}
return 0;
}
定位到这行注释的下面
/*
* If we are running in autograder mode, wewill only test
* the rotate() and bench() functions.
*/
可以看到我们自己写的代码,需要在这里注册并且申明数组来使用它,前提是在之前的c文件里面注册了add_smooth_function(&smooth_in4,smooth_descr4);并且还要在def.h中注册函数。drive.c文件里面套路太多,比如你可以修改cpe的初始值,那么跑分cpe的值就特别小啦,这里面作弊真的很简单。
好像有点跑题,再回到我们的负优化的话题。直接上代码吧。
为什么可以是10086呢?讲道理颜色值应该是0-255不是吗?我们再看drive.c文件。
orig[RIDX(i,j,dim)].red =random_in_interval(0, 65536);
orig[RIDX(i,j,dim)].green =random_in_interval(0, 65536);
orig[RIDX(i,j,dim)].blue = random_in_interval(0, 65536);
随机数0-65536(因为是unsignedshort),果断用了我大移动作为颜色值。
判断并且累计计数器count
最后计算平均值就OK啦,别忘了计数器归零!!
跑分!
跑出了加速比7.6,创造新低!哈哈,连初始函数都有15.2的加速比呢!负优化成功!
负优化分析:为什么CPE变成了原来的两倍之多?因为用到了两次的全图遍历,而原函数只用了一次。因此差不多就是两倍啦。