《深入理解计算机系统》(CSAPP)实验五 —— Perfom Lab

本次实验是CSAPP的第5个实验,这次实验主要是让我们熟悉如何优化程序,如何写出更具有效率的代码。通过这次实验,我们可以更好的理解计算机的工作原理,在以后编写代码时,具有能结合软硬件思考的能力。

实验简介

  本次实验主要处理优化内存密集型代码。图像处理提供了许多可以从优化中受益的功能示例。在本实验中,我们将考虑两种图像处理操作:旋转,可将图像逆时针旋转90o,平滑,可以“平滑”或“模糊”图片。

  在本实验中,我们将考虑将图像表示为二维矩阵M,其中 M i , j {M_{i,j}} Mi,j表示M的第(i,j)个像素的值,像素值是红色,绿色和蓝色(RGB)值的三倍。我们只会考虑方形图像。令N表示图像的行(或列)数。行和列以C样式编号,从0到N − 1。

  给定这种表示形式,旋转操作可以非常简单地实现为以下两个矩阵运算:

  转置:对于每对(i,j), M i , j {M_{i,j}} Mi,j M j , i {M_{j,i}} Mj,i是互换的

  交换行:第i行与第N-1 − i行交换。

  具体如下图所示

image-20201203203611824

    通过用周围所有像素的平均值替换每个像素值(在以该像素为中心的最大3×3窗口)中替换每个像素值来实现平滑操作。如下图所示。像素的值 M 2 [ 1 ] [ 1 ] M2[1][1] M2[1][1] M 2 [ N − 1 ] [ N − 1 ] M2[N - 1][N - 1] M2[N1][N1]如下所示:

   M 2 [ 1 ] [ 1 ] = ∑ i = 0 2 ∑ j = 0 2 M 1 [ i ] [ j ] 9 M2[1][1] = \frac{{\sum\nolimits_{i = 0}^2 {\sum\nolimits_{j = 0}^2 {M1[i][j]} } }}{9} M2[1][1]=9i=02j=02M1[i][j]

   M 2 [ N − 1 ] [ N − 1 ] = ∑ i = N − 2 N − 1 ∑ j = N − 2 N − 1 M 1 [ i ] [ j ] 4 M2[N - 1][N - 1] = \frac{{\sum\nolimits_{i = N - 2}^{N - 1} {\sum\nolimits_{j = N - 2}^{N - 1} {M1[i][j]} } }}{4} M2[N1][N1]=4i=N2N1j=N2N1M1[i][j]

image-20201203204903889

  本次实验中,我们需要修改唯一文件是kernels.c。driver.c程序是一个驱动程序,可让对我们修改的程序进行评分。使用命令make driver生成驱动程序代码并使用./driver命令运行它。

数据结构体

  图像的核心数据是用结构体表示的。像素是一个结构,如下所示:

typedef struct {
    unsigned short red; /* R value */
    unsigned short green; /* G value */
    unsigned short blue; /* B value */
} pixel;

  可以看出,RGB值具有16位表示形式(“ 16位颜色”)。图像I表示为一维像素阵列,其中第(i,j)个像素为I [RIDX(i,j,n)]。这里n是图像矩阵的维数,  RIDX是定义如下的宏:

#define RIDX(i,j,n) ((i)*(n)+(j))

  有关此代码,请参见文件defs.h。

旋转Rotate

  以下C函数计算将源图像src旋转90°的结果,并将结果存储在目标图像dst中。dim是图像的尺寸。

void naive_rotate(int dim, pixel *src, pixel *dst) {
    int i, j;
    for(i=0; i < dim; i++)
    for(j=0; j < dim; j++)
    dst[RIDX(dim-1-j,i,dim)] = src[RIDX(i,j,dim)];
    return;
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_16933601/article/details/110741493

  上面的代码扫描源图像矩阵的行,然后复制到目标图像矩阵的列中。我们的任务是使用代码移动,循环展开和阻塞等技术重写此代码,以使其尽可能快地运行。(有关此代码,请参见文件kernels.c。)

平滑Smooth

  平滑功能将源图像src作为输入,并在目标图像dst中返回平滑结果。这是实现的一部分:

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); /* Smooth the (i,j)th pixel */
    return;
}

  函数avg返回第(i,j)个像素周围所有像素的平均值。我们的任务是优化平滑(和avg)以尽可能快地运行。 (注意:函数avg是一个局部函数,可以完全摆脱它而以其他方式实现平滑)。(这段代码(以及avg的实现)位于kernels.c文件中。)

评价指标

  我们的主要性能指标是CPE。如果某个函数需要C个周期来运行大小为N×N的图像,则CPE值为 C / N 2 C/{N^2} C/N2

版本管理

  我们可以编写旋转和平滑例程的许多版本。为了帮助您比较编写的所有不同版本的性能,我们提供了一种“注册”功能的方式。
  例如,我们提供给您的文件kernels.c包含以下功能:

void register_rotate_functions() {
	add_rotate_function(&rotate, rotate_descr);
}

  此函数包含一个或多个调用以添加旋转函数。在上面的示例中,添加旋转函数将函数旋转与字符串旋转说明一起注册,该字符串是函数功能的ASCII描述。请参阅文件kernels.c以了解如何创建字符串描述。该字符串的长度最多为256个字符。

驱动

  将编写的源代码将与我们提供给驱动程序二进制文件的目标代码链接。要创建此二进制文件,您将需要执行以下命令

unix> make driver

  每次更改kernels.c中的代码时,都需要重新制作驱动程序。要测试您的实现,然后可以运行以下命令:

unix> ./driver

  该驱动程序可以在四种不同的模式下运行:

  默认模式,在其中运行实施的所有版本。

  Autograder模式,其中仅运行rotation()和smooth()函数。这是当我们使用驱动程序对您的切纸进行评分时将运行的模式。

  文件模式,其中仅运行输入文件中提到的版本。

  转储模式,其中每个版本的单行描述转储到文本文件中。然后,您可以编辑该文本文件,以仅使用文件模式保留要测试的版本。您可以指定是在转储文件之后退出还是要运行您的实现。

  如果不带任何参数运行,驱动程序将运行所有版本(默认模式)。其他模式和选项可以通过驱动程序的命令行参数来指定,如下所示:

  -g:仅运行rotate()和smooth()函数(自动分级模式)。

  -f :仅执行在中指定的那些版本(文件模式)。

  -d :将所有版本的名称转储到名为的转储文件中,将一行转储到版本(转储模式)。

  -q :将版本名称转储到转储文件后退出。与-d一起使用。例如,要在打印转储文件后立即退出,请键入./driver -qd dumpfile。

  -h:打印命令行用法。

优化程序的方法

emsp; 回顾下常用的优化程序的方法,总结如下:

(1)高级设计

  为遇到的问题选择适当的算法和数据结构。要特别警觉,避免使用那些会渐进地产生糟糕性能的算法或编码技术。

(2)基本编码原则

  避免限制优化的因素,这样编译器就能产生高效的代码。

  消除连续的函数调用。在可能时,将计算移到循环外。考虑有选择地妥协程序的模块性以获得更大的效率。

  消除不必要的内存引用。引入临时变量来保存中间结果。只有在最后的值计算出来时,才将结果存放到数组或全局变量中。

(3)低级优化

  结构化代码以利用硬件功能。

  展开循环,降低开销,并且使得进一步的优化成为可能。

  通过使用例如多个累积变量和重新结合等技术,找到方法提高指令级并行。

  用功能性的风格重写条件操作,使得编译采用条件数据传送。

(4)使用性能分析工具

  当处理大型程序时,将注意力集中在最耗时的部分变得很重要。代码剖析程序和相关的工具能帮助我们系统地评价和改进程序性能。我们描述了 GPROF,一个标准的Unix剖析工具。还有更加复杂完善的剖析程序可用,例如 Intel的VTUNE程序开发系统,还有 Linux系统基本上都有的 VALGRIND。这些工具可以在过程级分解执行时间,估计程序每个基本块( basic block)的性能。(基本块是内部没有控制转移的指令序列,因此基本块总是整个被执行的。)

Optimizing Rotate

  在这一部分中,我们将优化旋转以实现尽可能低的CPE。您应该编译驱动程序,然后使用适当的参数运行它以测试您的实现。例如,运行提供的原始版本(用于旋转)的驱动程序将生成如下所示的输出:

  函数源码如下:

void naive_rotate(int dim, pixel *src, pixel *dst) {
    int i, j;
    for(i=0; i < dim; i++)
    for(j=0; j < dim; j++)
    dst[RIDX(dim-1-j,i,dim)] = src[RIDX(i,j,dim)];
    return;
}

  其中,defs.h中RIDX定义为:#define RIDX(i,j,n) ((i)*(n)+(j))下面详细分析下程序。

i = 0     j = 0    dest[20] = src[0]           i = 1   j = 0    dest[21] = src[5]
i = 0     j = 1    dest[15] = src[1]           i = 1   j = 1    dest[16] = src[6]
i = 0     j = 2    dest[10] = src[2]           i = 1   j = 2    dest[11] = src[7]
i = 0     j = 3    dest[5] = src[3]            i = 1   j = 3    dest[6] = src[8]
i = 0     j = 4    dest[0] = src[4]            i = 1   j = 4    dest[1] = src[9]

  具体如下图所示:

image-20201204104234750

  这段代码的作用就是将dim * dim大小的方块中所有的像素进行行列调位、导致整幅图画进行了90度旋转。观察源代码我们发现,程序进行了嵌套循环,随着dim的增加,循环的复杂度越来越大,而且每循环一次,dim-1-j就要计算一次,因此,我们考虑进行分块优化。

优化版本一:分块 8 * 8

  对于循环分块,这里的分块指的是一个应用级的数据组块,而不是高速缓存中的块,这样构造程序,能将一个片加载到L1高速缓存中去,并在这个片中进行所需要的所有读和写,然后丢掉这个片,加载下一个片,以此类推。

/*分块:8 * 8*/
char rotate_descr[] = "rotate1: Current working version";
void rotate(int dim, pixel *src, pixel *dst) {
    int i,j,i1,j1;
    for(i=0; i < dim; i+=8)
    for(j=0; j < dim; j+=8)
    for(i1=i; i1 < i+8; i1++)
    for(j1=j; j1 < j+8; j1++)
    dst[RIDX(dim-1-j1,i1,dim)] = src[RIDX(i1,j1,dim)];
}

  优化后的版本测试如下所示:

image-20201204163559473

  右上图可以看到,得分有了明显的提升,Dim规模较小时,提升并不明显,在Dim为1024*1024时,由原来的17.1降低到了6.0.说明我们的方法还是有效的,但是最后的总得得分只有9.3分,效果不是很好。

优化版本二:分块 32 * 32

char rotate_descr[] = "rotate2: Current working version";
void rotate(int dim, pixel *src, pixel *dst) {
    int i,j,i1,j1;
    for(i=0; i < dim; i+=32)
    for(j=0; j < dim; j+=32)
    for(i1=i; i1 < i+32; i1++)
    for(j1=j; j1 < j+32; j1++)
    dst[RIDX(dim-1-j1,i1,dim)] = src[RIDX(i1,j1,dim)];
}

  本次继续采用的是分块策略,分为了32块,但是由下图的得分可以看到,性能基本有提升,所以,需要换个思路了。

image-20201204165713471

优化版本三:循环展开,32路并行

  在版本二的基础上,我们进行循环展开,32路并行,并使用指针代替RIDX进行数组访问,这里牺牲了程序的尺寸来换取速度优化。

char rotate_descr[] = "rotate3: Current working version";
void rotate(int dim, pixel *src, pixel *dst) {
    int i,j;
    int dst_base = (dim-1)*dim;
    dst +=dst_base;
    for(i = 0;i < dim;i += 32){
    	for(j = 0;j < dim;j++){
    		*dst = *src; src +=dim; dst++;       //31组
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;       
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;       
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;       
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;       
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;       
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src +=dim; dst++;
    		*dst = *src; src++;
    		src -= (dim<<5)-dim;              //src -=31*dim;
    		dst -=31+dim;
    	}
    	dst +=dst_base + dim;
    	dst +=32;
    	src +=(dim<<5)-dim;                    //src +=31*dim;
    	}
}

image-20201204205049830

  我们在版本二的基础上,将原来的程序的内循环展开成32个并行,每一次同时处理32个像素点,即将循环次数减少了32倍,大大加速了程序。分数由版本二的10.3分涨到了15.3分。特别是在哦1024 * 1024时,由15.1直接降到了5.0,性能提升还是很明显的。

Optimizing Smooth

  在这一部分中,您将优化平滑度以实现尽可能低的CPE。例如,运行提供的朴素版本(为了平滑)的驱动程序将生成如下所示的输出:

unix> ./driver
Smooth: Version = naive_smooth: Naive baseline implementation:
Dim               32      64       128     256     512     Mean
Your CPEs         695.8   698.5    703.8   720.3   722.7
Baseline CPEs     695.0   698.0    702.0   717.0   722.0
Speedup           1.0     1.0      1.0     1.0     1.0     1.0  
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); /* Smooth the (i,j)th pixel */
    return;
}

  这个函数的作用是平滑图像,在smooth函数中因为要求周围点的平均值,所以会频繁的调用avg函数,而且avg函数还是一个2层for循环,所以我们可以考虑循环展开或者消除函数调用等方法,减少avg函数调用和循环。

  Smooth函数处理分为4块,一为主体内部,由9点求平均值;二为4个顶点,由4点求平均值;三为四条边界,由6点求平均值。从图片的顶部开始处理,再上边界,顺序处理下来,其中在处理左边界时,for循环处理一行主体部分。

  未经优化的函数性能如下,得分为12.2分。

image-20201205213200537

优化版本一:消除函数调用

void smooth(int dim, pixel *src, pixel *dst)
{
pixel_sum rowsum[530][530];
    int i, j, snum;  
    for(i=0; i<dim; i++)  
    {  
        rowsum[i][0].red = (src[RIDX(i, 0, dim)].red+src[RIDX(i, 1, dim)].red);  
        rowsum[i][0].blue = (src[RIDX(i, 0, dim)].blue+src[RIDX(i, 1, dim)].blue);  
        rowsum[i][0].green = (src[RIDX(i, 0, dim)].green+src[RIDX(i, 1, dim)].green);  
        rowsum[i][0].num = 2;  
        for(j=1; j<dim-1; j++)
        {
            rowsum[i][j].red = (src[RIDX(i, j-1, dim)].red+src[RIDX(i, j, dim)].red+src[RIDX(i, j+1, dim)].red);  
            rowsum[i][j].blue = (src[RIDX(i, j-1, dim)].blue+src[RIDX(i, j, dim)].blue+src[RIDX(i, j+1, dim)].blue);  
            rowsum[i][j].green = (src[RIDX(i, j-1, dim)].green+src[RIDX(i, j, dim)].green+src[RIDX(i, j+1, dim)].green);  
            rowsum[i][j].num = 3;  
        }  
        rowsum[i][dim-1].red = (src[RIDX(i, dim-2, dim)].red+src[RIDX(i, dim-1, dim)].red);  
        rowsum[i][dim-1].blue = (src[RIDX(i, dim-2, dim)].blue+src[RIDX(i, dim-1, dim)].blue);  
        rowsum[i][dim-1].green = (src[RIDX(i, dim-2, dim)].green+src[RIDX(i, dim-1, dim)].green);  
        rowsum[i][dim-1].num = 2;  
    }  
    for(j=0; j<dim; j++)  
    {  
        snum = rowsum[0][j].num+rowsum[1][j].num;  
        dst[RIDX(0, j, dim)].red = (unsigned short)((rowsum[0][j].red+rowsum[1][j].red)/snum);  
        dst[RIDX(0, j, dim)].blue = (unsigned short)((rowsum[0][j].blue+rowsum[1][j].blue)/snum);  
        dst[RIDX(0, j, dim)].green = (unsigned short)((rowsum[0][j].green+rowsum[1][j].green)/snum);  
        for(i=1; i<dim-1; i++)  
        {  
            snum = rowsum[i-1][j].num+rowsum[i][j].num+rowsum[i+1][j].num;  
            dst[RIDX(i, j, dim)].red = (unsigned short)((rowsum[i-1][j].red+rowsum[i][j].red+rowsum[i+1][j].red)/snum);  
            dst[RIDX(i, j, dim)].blue = (unsigned short)((rowsum[i-1][j].blue+rowsum[i][j].blue+rowsum[i+1][j].blue)/snum);  
            dst[RIDX(i, j, dim)].green = (unsigned short)((rowsum[i-1][j].green+rowsum[i][j].green+rowsum[i+1][j].green)/snum);  
        }  
        snum = rowsum[dim-1][j].num+rowsum[dim-2][j].num;  
        dst[RIDX(dim-1, j, dim)].red = (unsigned short)((rowsum[dim-2][j].red+rowsum[dim-1][j].red)/snum);  
        dst[RIDX(dim-1, j, dim)].blue = (unsigned short)((rowsum[dim-2][j].blue+rowsum[dim-1][j].blue)/snum);  
        dst[RIDX(dim-1, j, dim)].green = (unsigned short)((rowsum[dim-2][j].green+rowsum[dim-1][j].green)/snum);  
}
}

  在以上的优化中,我们取消了对avg函数的直接调用,而是直接对像素点的fgb颜色分别求均值,并且将重复利用的数据存储在了数组之中,因此,速度比之前有所提升,但是提升并不高,由12.2提升到15.4。

image-20201205221916055

优化版本二:分开讨论,分块求平均

void smooth(int dim, pixel *src, pixel *dst)
{
    int i,j;
    int dim0=dim;
    int dim1=dim-1;
    int dim2=dim-2;
    pixel *P1, *P2, *P3;
    pixel *dst1;
    P1=src;
    P2=P1+dim0;
    //左上角像素处理
    dst->red=(P1->red+(P1+1)->red+P2->red+(P2+1)->red)>>2;
    dst->green=(P1->green+(P1+1)->green+P2->green+(P2+1)->green)>>2;
    dst->blue=(P1->blue+(P1+1)->blue+P2->blue+(P2+1)->blue)>>2;
    dst++;
    //上边界处理
    for(i=1;i<dim1;i++)
    {
        dst->red=(P1->red+(P1+1)->red+(P1+2)->red+P2->red+(P2+1)->red+(P2+2)->red)/6;
        dst->green=(P1->green+(P1+1)->green+(P1+2)->green+P2->green+(P2+1)->green+(P2+2)->green)/6;
        dst->blue=(P1->blue+(P1+1)->blue+(P1+2)->blue+P2->blue+(P2+1)->blue+(P2+2)->blue)/6;
        dst++;
        P1++;
        P2++;
    }
    //右上角像素处理
    dst->red=(P1->red+(P1+1)->red+P2->red+(P2+1)->red)>>2;
    dst->green=(P1->green+(P1+1)->green+P2->green+(P2+1)->green)>>2;
    dst->blue=(P1->blue+(P1+1)->blue+P2->blue+(P2+1)->blue)>>2;
    dst++;
    P1=src;
    P2=P1+dim0;
    P3=P2+dim0;
    //左边界处理
    for(i=1;i<dim1;i++)
    {
        dst->red=(P1->red+(P1+1)->red+P2->red+(P2+1)->red+P3->red+(P3+1)->red)/6;
        dst->green=(P1->green+(P1+1)->green+P2->green+(P2+1)->green+P3->green+(P3+ 1)->green)/6;
        dst->blue=(P1->blue+(P1+1)->blue+P2->blue+(P2+1)->blue+P3->blue+(P3+1)->blue)/6;
        dst++;
        dst1=dst+1;
        //中间主体部分处理
        for(j=1;j<dim2;j+=2)
        {        
            //同时处理两个像素
           dst->red=(P1->red+(P1+1)->red+(P1+2)->red+P2->red+(P2+1)->red+(P2+2)->red+P3->red+(P3+1)->red+(P3+2)->red)/9;
           dst->green=(P1->green+(P1+1)->green+(P1+2)->green+P2->green+(P2+1)->green+(P2+2)->green+P3->green+(P3+1)->green+(P3+2)->green)/9;
           dst->blue=(P1->blue+(P1+1)->blue+(P1+2)->blue+P2->blue+(P2+1)->blue+(P2+2)->blue+P3->blue+(P3+1)->blue+(P3+2)->blue)/9;
           dst1->red=((P1+3)->red+(P1+1)->red+(P1+2)->red+(P2+3)->red+(P2+1)->red+(P2+2)->red+(P3+3)->red+(P3+1)->red+(P3+2)->red)/9;
           dst1->green=((P1+3)->green+(P1+1)->green+(P1+2)->green+(P2+3)->green+(P2+1)->green+(P2+2)->green+(P3+3)->green+(P3+1)->green+(P3+2)->green)/9;
           dst1->blue=((P1+3)->blue+(P1+1)->blue+(P1+2)->blue+(P2+3)->blue+(P2+1)->blue+(P2+2)->blue+(P3+3)->blue+(P3+1)->blue+(P3+2)->blue)/9;
           dst+=2;
           dst1+=2;
           P1+=2;
           P2+=2;
           P3+=2;
        }
        for(;j<dim1;j++)
        {         
           dst->red=(P1->red+(P1+1)->red+(P1+2)->red+P2->red+(P2+1)->red+(P2+2)->red+P3->red+(P3+1)->red+(P3+2)->red)/9;
           dst->green=(P1->green+(P1+1)->green+(P1+2)->green+P2->green+(P2+1)->green+(P2+2)->green+P3->green+(P3+1)->green+(P3+2)->green)/9;
           dst->blue=(P1->blue+(P1+1)->blue+(P1+2)->blue+P2->blue+(P2+1)->blue+(P2+2)->blue+P3->blue+(P3+1)->blue+(P3+2)->blue)/9;
           dst++;
           P1++;
           P2++;
           P3++;
        }
        //右侧边界处理
        dst->red=(P1->red+(P1+1)->red+P2->red+(P2+1)->red+P3->red+(P3+1)->red)/6;
        dst->green=(P1->green+(P1+1)->green+P2->green+(P2+1)->green+P3->green+(P3+1)->green)/6;
        dst->blue=(P1->blue+(P1+1)->blue+P2->blue+(P2+1)->blue+P3->blue+(P3+1)->blue)/6;
        dst++;
        P1+=2;
        P2+=2;
        P3+=2;
     } 
    //右下角处理
     dst->red=(P1->red+(P1+1)->red+P2->red+(P2+1)->red)>>2;
     dst->green=(P1->green+(P1+1)->green+P2->green+(P2+1)->green)>>2;
     dst->blue=(P1->blue+(P1+1)->blue+P2->blue+(P2+1)->blue)>>2;
     dst++;
    //下边界处理
     for(i=1;i<dim1;i++)
     {
        dst->red=(P1->red+(P1+1)->red+(P1+2)->red+P2->red+(P2+1)->red+(P2+2)->red)/6;
        dst->green=(P1->green+(P1+1)->green+(P1+2)->green+P2->green+(P2+1)->green+(P2+2)->green)/6;
        dst->blue=(P1->blue+(P1+1)->blue+(P1+2)->blue+P2->blue+(P2+1)->blue+(P2+2)->blue)/6;
        dst++;
        P1++;
        P2++;
     } 
    //右下角像素处理
     dst->red=(P1->red+(P1+1)->red+P2->red+(P2+1)->red)>>2;
     dst->green=(P1->green+(P1+1)->green+P2->green+(P2+1)->green)>>2;
     dst->blue=(P1->blue+(P1+1)->blue+P2->blue+(P2+1)->blue)>>2;
 }

  在这个版本中,我们在版本一的基础上继续优化。将Smooth函数分为内部-顶点-边界的四部分,一为主体内部,由9点求平均值;二为4个顶点,由4点求平均值;三为四条边界,由6点求平均值。从图片的顶部开始处理,再上边界,顺序处理下来,其中在处理左边界时,for循环处理一行主体部分,就是以上的代码。

  下图为测试结果,由版本一的分15.4分提升到了44.1分,性能提升显著!

image-20201205223244700

总结

  本次实验的趣味性不如前几个实验,难度也没有前几个实验的大。在实际优化程序时,我们不能一味的为了速度而展开程序,或者消除函数引用,以程序的体积和可读性去换取性能的提升是非常不划算的。在保证可读性的前提下尽可能去提升程序的性能。

  • 5
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 深入理解计算机系统(CSAPP)是由Randal E. Bryant和David R. O'Hallaron编写的经典计算机科学教材。该教材通过涵盖计算机体系结构、机器级别表示和程序执行的概念,帮助学生深入理解计算机系统的底层工作原理和运行机制。 深入理解计算机系统的练习题对于学生巩固并应用所学知识非常有帮助。这些练习题涵盖了计算机硬件、操作系统和编译器等多个领域,旨在培养学生解决实际问题和设计高性能软件的能力。 对于深入理解计算机系统的练习题,关键是通过实践进行学习。在解答练习题时,应根据课本提供的相关知识和工具,仔细阅读问题描述,并根据实际需求设计相应的解决方案。 在解答练习题时,需要多角度思考问题。首先,应准确理解题目要求,并设计合适的算法或代码来解决问题。其次,应考虑代码的正确性和效率,以及对系统性能的影响。此外,还要注意处理一些特殊情况和异常情况,避免出现潜在的错误或安全漏洞。 解答练习题的过程中,应注重查阅相关资料和参考优秀的解答。这可以帮助我们扩展对问题的理解,并学习他人的思路和解决方法。同时,还可以通过与同学和老师的讨论,共同探讨问题和学习经验。 总之,通过解答深入理解计算机系统的练习题,可以帮助学生巩固所学知识,同时培养解决实际问题和设计高性能软件的能力。这是一个学以致用的过程,可以加深对计算机系统运行机制和底层工作原理的理解。 ### 回答2: 理解计算机系统(CSAPP)是一本经典的计算机科学教材,通过深入研究计算机系统的各个方面,包括硬件、操作系统和编程环境,对于提高计算机科学专业知识与能力具有很大帮助。 练习题是CSAPP中的重要部分,通过练习题的完成,可以加深对计算机系统的理解,并将理论知识转化为实践能力。练习题的数量、难度逐渐递增,从简单的概念与基础问题到复杂的系统设计与实现。 在解答练习题时,首先需要对题目进行仔细阅读和理解,明确题目的要求和限制条件。然后,可以利用课堂讲解、教材内容、网络资源等进行查阅和学习相应的知识。同时,还可以参考课后习题解答等资料,了解一些常见的解题方法和思路。 在解答练习题时,可以利用计算机系统的工具和环境进行实际测试和验证。例如,可以使用调试器、编译器和模拟器等工具对程序或系统进行分析和测试。这样可以更加深入地理解问题的本质,并找到恰当的解决方法。 另外,解答练习题时还可以与同学、教师和网上社区进行交流和讨论。这样可以互相学习和交流解题思路,共同解决问题。还可以了解不同的解题方法和技巧,提高解题效率和质量。 练习题的解答过程可能会遇到一些困难和挑战,例如理论知识的不足、复杂问题的分析与解决。但是通过不断地思考和实践,相信可以逐渐提高解题能力,更好地理解计算机系统。 总之,深入理解计算机系统(CSAPP)练习题是提高计算机科学专业知识和能力的重要途径。通过仔细阅读和理解题目,查阅相关知识,利用计算机系统工具和环境进行实践,与他人进行交流和讨论,相信可以更好地理解计算机系统的各个方面,并将知识转化为实际能力。 ### 回答3: 《深入理解计算机系统(CSAPP)》是计算机科学领域的经典教材之一,对于深入理解计算机系统的原理、设计和实现起到了极大的帮助。在阅读这本书的过程中,书中的习题也是非常重要的一部分,通过做习题,我们可以更好地理解书中所讲的概念和思想。 CSAPP的习题涵盖了课本中各个章节的内容,从基础的数据表示和处理、程序的机器级表示、优化技术、程序的并发与并行等方面进行了深入探讨。通过解答习题,我们可以对这些知识进行实践应用,巩固自己的理解,并培养自己的解决问题的思维方式。 在解答习题时,我们需要充分理解题目要求和条件,并从知识的角度进行分析。有些习题可能需要进行一些编程实践,我们可以通过编程实现来验证和测试我们的思路和解决方案。在解答问题时,我们还可以查阅一些参考资料和网上资源,充分利用互联网的学习资源。 在解答习题时,我们需要保持积极的思维和态度。可能会遇到一些困难和挑战,但是通过坚持和努力,我们可以克服这些困难,提高我们的解决问题的能力。同时,我们还可以通过与同学或者其他人进行讨论,相互分享解题经验和思路,从而更好地理解问题。 综上所述,通过深入理解计算机系统(CSAPP)的习题,我们可以进一步巩固和深化对计算机系统的理解。掌握这些知识,不仅可以提高我们在计算机领域的能力,还可以为我们未来的学习和职业发展奠定重要的基础。因此,认真对待CSAPP的习题,是我们在学习计算机系统知识中不可或缺的一部分。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式与Linux那些事

您的鼓励将使我写出更好的文章

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值