CSAPP LAB4 perflab-handout性能优化 smooth负优化详解


在优化代码之前,首先要知道我们有哪些方法可以优化性能?

翻书!把教材翻到目录,第五章赫然写着优化方法:

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的值就特别小啦,这里面作弊真的很简单。

好像有点跑题,再回到我们的负优化的话题。直接上代码吧。

CSAPP LAB4 perflab-handout性能优化 smooth负优化详解_第1张图片

CSAPP LAB4 perflab-handout性能优化 smooth负优化详解_第2张图片

为什么可以是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

CSAPP LAB4 perflab-handout性能优化 smooth负优化详解_第3张图片

最后计算平均值就OK啦,别忘了计数器归零!!

跑分!

跑出了加速比7.6,创造新低!哈哈,连初始函数都有15.2的加速比呢!负优化成功!

CSAPP LAB4 perflab-handout性能优化 smooth负优化详解_第4张图片

负优化分析:为什么CPE变成了原来的两倍之多?因为用到了两次的全图遍历,而原函数只用了一次。因此差不多就是两倍啦。

你可能感兴趣的:(CSAPP LAB4 perflab-handout性能优化 smooth负优化详解)