主頁 > 作業系統 > 《深入理解計算機系統》實驗五 —— Perfom Lab

《深入理解計算機系統》實驗五 —— Perfom Lab

2021-01-11 11:55:13 作業系統

本次實驗是CSAPP的第5個實驗,這次實驗主要是讓我們熟悉如何優化程式,如何寫出更具有效率的代碼,通過這次實驗,我們可以更好的理解計算機的作業原理,在以后撰寫代碼時,具有能結合軟硬體思考的能力,

@

目錄
  • 實驗簡介
    • 資料結構體
    • 旋轉Rotate
    • 平滑Smooth
    • 評價指標
    • 版本管理
    • 驅動
  • 優化程式的方法
  • Optimizing Rotate
    • 優化版本一:分塊 8 * 8
    • 優化版本二:分塊 32 * 32
    • 優化版本三:回圈展開,32路并行
  • Optimizing Smooth
    • 優化版本一:消除函式呼叫
    • 優化版本二:分開討論,分塊求平均
  • 總結

實驗簡介

??本次實驗主要處理優化記憶體密集型代碼,影像處理提供了許多可以從優化中受益的功能示例,在本實驗中,我們將考慮兩種影像處理操作:旋轉,可將影像逆時針旋轉90o,平滑,可以“平滑”或“模糊”圖片,

??在本實驗中,我們將考慮將影像表示為二維矩陣M,其中\({M_{i,j}}\)表示M的第(i,j)個像素的值,像素值是紅色,綠色和藍色(RGB)值的三倍,我們只會考慮方形影像,令N表示影像的行(或列)數,行和列以C樣式編號,從0到N ? 1,

??給定這種表示形式,旋轉操作可以非常簡單地實作為以下兩個矩陣運算:

??轉置:對于每對(i,j),\({M_{i,j}}\)\({M_{j,i}}\)是互換的

??交換行:第i行與第N-1 ? i行交換,

??具體如下圖所示

image-20201203203611824

????通過用周圍所有像素的平均值替換每個像素值(在以該像素為中心的最大3×3視窗)中替換每個像素值來實作平滑操作,如下圖所示,像素的值\(M2[1][1]\)\(M2[N - 1][N - 1]\)如下所示:

??\(M2[1][1] = \frac{{\sum\nolimits_{i = 0}^2 {\sum\nolimits_{j = 0}^2 {M1[i][j]} } }}{9}\)

??\(M2[N - 1][N - 1] = \frac{{\sum\nolimits_{i = N - 2}^{N - 1} {\sum\nolimits_{j = N - 2}^{N - 1} {M1[i][j]} } }}{4}\)

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;
}

??上面的代碼掃描源影像矩陣的行,然后復制到目標影像矩陣的列中,我們的任務是使用代碼移動,回圈展開和阻塞等技術重寫此代碼,以使其盡可能快地運行,(有關此代碼,請參見檔案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}\)

版本管理

??我們可以撰寫旋轉和平滑例程的許多版本,為了幫助您比較撰寫的所有不同版本的性能,我們提供了一種“注冊”功能的方式,
??例如,我們提供給您的檔案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

總結

??本次實驗的趣味性不如前幾個實驗,難度也沒有前幾個實驗的大,在實際優化程式時,我們不能一味的為了速度而展開程式,或者消除函式參考,以程式的體積和可讀性去換取性能的提升是非常不劃算的,在保證可讀性的前提下盡可能去提升程式的性能,

有任何問題,均可通過公告中的二維碼聯系我

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/247020.html

標籤:嵌入式

上一篇:面試官不講武德,居然讓我講講蠕蟲和金絲雀!

下一篇:httpd.conf的設定問題

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • CA和證書

    1、在 CentOS7 中使用 gpg 創建 RSA 非對稱密鑰對 gpg --gen-key #Centos上生成公鑰/密鑰對(存放在家目錄.gnupg/) 2、將 CentOS7 匯出的公鑰,拷貝到 CentOS8 中,在 CentOS8 中使用 CentOS7 的公鑰加密一個檔案 gpg -a ......

    uj5u.com 2020-09-10 00:09:53 more
  • Kubernetes K8S之資源控制器Job和CronJob詳解

    Kubernetes的資源控制器Job和CronJob詳解與示例 ......

    uj5u.com 2020-09-10 00:10:45 more
  • VMware下安裝CentOS

    VMware下安裝CentOS 一、軟硬體準備 1 Centos鏡像準備 1.1 CentOS鏡像下載地址 下載地址 1.2 CentOS鏡像下載程序 點擊下載地址進入如下圖的網站,選擇需要下載的版本,這里選擇的是Centos8,點擊如圖所示。 決定選擇Centos8后,選擇想要的鏡像源進行下載,此 ......

    uj5u.com 2020-09-10 00:12:10 more
  • 如何使用Grep命令查找多個字串

    如何使用Grep 命令查找多個字串 大家好,我是良許! 今天向大家介紹一個非常有用的技巧,那就是使用 grep 命令查找多個字串。 簡單介紹一下,grep 命令可以理解為是一個功能強大的命令列工具,可以用它在一個或多個輸入檔案中搜索與正則運算式相匹配的文本,然后再將每個匹配的文本用標準輸出的格式 ......

    uj5u.com 2020-09-10 00:12:28 more
  • git配置http代理

    git配置http代理 經常遇到克隆 github 慢的問題,這里記錄一下幾種配置 git 代理的方法,解決 clone github 過慢。 目錄 git配置代理 git單獨配置github代理 git配置全域代理 配置終端環境變數 git配置代理 主要使用 git config 命令 git單獨 ......

    uj5u.com 2020-09-10 00:12:33 more
  • Linux npm install 裝包時提示Error EACCES permission denied解

    npm install 裝包時提示Error EACCES permission denied解決辦法 ......

    uj5u.com 2020-09-10 00:12:53 more
  • Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包

    Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包。 18 (flaskApi) [root@67 flaskDemo]# yum -y install nginx 19 已加載插件:fastestmirror, langpacks 20 Loading ......

    uj5u.com 2020-09-10 00:13:13 more
  • Linux查看服務器暴力破解ssh IP

    在公網的服務器上經常遇到別人爆破你服務器的22埠,用來挖礦或者干其他嘿嘿嘿的事情~ 這種情況下正確的做法是: 修改默認ssh的22埠 使用設定密鑰登錄或者白名單ip登錄 建議服務器密碼為復雜密碼 創建普通用戶登錄服務器(root權限過大) 建立堡壘機,實作統一管理服務器 統計爆破IP [root ......

    uj5u.com 2020-09-10 00:13:17 more
  • CentOS 7系統常見快捷鍵操作方式

    Linux系統中一些常見的快捷方式,可有效提高操作效率,在某些時刻也能避免操作失誤帶來的問題。 ......

    uj5u.com 2020-09-10 00:13:31 more
  • CentOS 7作業系統目錄結構介紹

    作業系統存在著大量的資料檔案資訊,相應檔案資訊會存在于系統相應目錄中,為了更好的管理資料資訊,會將系統進行一些目錄規劃,不同目錄存放不同的資源。 ......

    uj5u.com 2020-09-10 00:13:35 more
最新发布
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:43:21 more
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:42:36 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:26:53 more
  • 設定Windows主機的瀏覽器為wls2的默認瀏覽器

    這里以Chrome為例。 1. 準備作業 wsl是可以使用Windows主機上安裝的exe程式,出于安全考慮,默認情況下改功能是無法使用。要使用的話,終端需要以管理員權限啟動。 我這里以Windows Terminal為例,介紹如何默認使用管理員權限打開終端,具體操作如下圖所示: 2. 操作 wsl ......

    uj5u.com 2023-04-19 09:25:49 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:19:04 more
  • Linux學習筆記

    IP地址和主機名 IP地址 ifconfig可以用來查詢本機的IP地址,如果不能使用,可以通過install net-tools安裝。 Centos系統下ens33表示主網卡;inet后表示IP地址;lo表示本地回環網卡; 127.0.0.1表示代指本機;0.0.0.0可以用于代指本機,同時在放行設 ......

    uj5u.com 2023-04-18 06:52:01 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:50 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:01 more
  • 你是不是暴露了?

    作者:袁首京 原創文章,轉載時請保留此宣告,并給出原文連接。 如果您是計算機相關從業人員,那么應該經歷不止一次網路安全專項檢查了,你肯定是收到過資訊系統技術檢測報告,要求你加強風險監測,確保你提供的系統服務堅實可靠了。 沒檢測到問題還好,檢測到問題的話,有些處理起來還是挺麻煩的,尤其是線上正在運行的 ......

    uj5u.com 2023-04-05 16:52:56 more
  • 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實作

    1. 前文回顧 在之前的幾篇記憶體管理系列文章中,筆者帶大家從宏觀角度完整地梳理了一遍 Linux 記憶體分配的整個鏈路,本文的主題依然是記憶體分配,這一次我們會從微觀的角度來探秘一下 Linux 內核中用于零散小記憶體塊分配的記憶體池 —— slab 分配器。 在本小節中,筆者還是按照以往的風格先帶大家簡單 ......

    uj5u.com 2023-04-05 16:44:11 more