perflab實驗smooth函數前綴和優化

實驗內容

本次實驗要求修改kernel.c中的smooth函數,優化其加速比。

優化策略

優化策略:使用前綴和避免不必要的重複計算

  • 前綴和算法本次實驗慎用,不一定符合實驗的要求,在實驗要求範圍內的改進才被認作是好的改進,雖然你的算法達到了50.3的加速比!這裏將雖然沒有改動avg但是其實是變相的改變了avg的均值算法。

優化思路

剛開始拿到smooth優化時,想了很多方法,包括避免函數調用,分塊,多路並行,使用指針等等,但是優化效果十分有限,我不得不去尋找其他的方法,折騰一晚上後我發現這個smooth實現的像素點降噪實際上和我寒假學習canvas畫布操縱像素的作用是完全一樣的!而且是一個最簡單的均值降噪!我想,在計算某個像素點(I,j)時要反覆求和以他爲中心的9個像素格,這顯然是有很多重複的像素格被計算。我想起了一道矩陣計算的題目,當時是使用計算前綴和來避免重複計算,那麼我想在這裏能不能使用二維前綴和來避免重複計算呢?答案是完全可以!這個模型和那道二維前綴和的模型幾乎一致!。我們只需要首先進行預處理,計算前綴和,也就是將p(i,j)=p(i-1,j)+p(I,j-1)-p(i-1,j-1)+s(i-1,j-1)。前綴和計算完成之後我們將一張畫布分爲如圖所示的3個部分,即4個頂點,四條邊緣以及內部,其中頂點爲包圍它的四個像素格的1/4,邊緣爲包圍它的六個像素格的1/6,內部頂點則是包圍他的9個像素格的1/9:

實現優化

在這裏插入圖片描述
 定義3個像素數組存儲像素點RGB
 計算前綴和:p(i,j)=p(i-1,j)+p(I,j-1)-p(i-1,j-1)+s(i-1,j-1)
 四個頂點:這4個點的降噪實際上是取圍繞他的4個格子的均值
 四個邊緣:這些邊緣像素點對應圍繞它的6(包括它在內)的均值
 內部元素:內部元素和則是圍繞它的格子3*3=9的均值

代碼註釋

int pR[530][530],pG[530][530],pB[530][530];//前綴和數組存儲前綴和
void smooth6(int dim, pixel *src, pixel *dst)
{
    int i,j;
    for(i=1;i<dim+1;i++)
    for(j=1;j<dim+1;j++){//計算前綴和,p(i,j)=p(i-1,j)+p(I,j-1)-p(i-1,j-1)+s(i-1,j-1)
        pR[i][j]=pR[i-1][j]+pR[i][j-1]-pR[i-1][j-1]+src[(i-1)*dim+j-1].red;
        pG[i][j]=pG[i-1][j]+pG[i][j-1]-pG[i-1][j-1]+src[(i-1)*dim+j-1].green;
        pB[i][j]=pB[i-1][j]+pB[i][j-1]-pB[i-1][j-1]+src[(i-1)*dim+j-1].blue;
    }
    dst[0].red=pR[2][2]/4;//平均降噪左上角,取包圍他在內的4個像素點的均值,也就是p[2][2]
    dst[0].green=pG[2][2]/4;
    dst[0].blue=pB[2][2]/4;

    dst[dim-1].red=(pR[2][dim]-pR[2][dim-2])/4;//平均降噪右上角,取包圍他在內的4個像素點的均值
    dst[dim-1].green=(pG[2][dim]-pG[2][dim-2])/4;//也就是p[2][dim]-p[2][dim-2]的均值
    dst[dim-1].blue=(pB[2][dim]-pB[2][dim-2])/4;

    dst[(dim-1)*dim].red=(pR[dim][2]-pR[dim-2][2])/4;//平均降噪左下角取包圍他在內的4個像素點的均值
    dst[(dim-1)*dim].green=(pG[dim][2]-pG[dim-2][2])/4;//也就是p[dim][2]-p[dim-2][2]的均值
    dst[(dim-1)*dim].blue=(pB[dim][2]-pB[dim-2][2])/4;

    dst[(dim-1)*(dim+1)].red=(pR[dim][dim]-pR[dim-2][dim]-pR[dim][dim-2]+pR[dim-2][dim-2])/4;//平均降噪右下角
    dst[(dim-1)*(dim+1)].green=(pG[dim][dim]-pG[dim-2][dim]-pG[dim][dim-2]+pG[dim-2][dim-2])/4;//取包圍他在內的4個像素點的均值
    dst[(dim-1)*(dim+1)].blue=(pB[dim][dim]-pB[dim-2][dim]-pB[dim][dim-2]+pB[dim-2][dim-2])/4;
	//也就是p[dim][dim]-p[dim-2][dim]-p[dim][dim-2]+p[dim-2][dim-2]/4
    for(j=1;j<dim-1;j++){//第一行,取包圍他的6個像素點的均值
        dst[j].red=(pR[2][j+2]-pR[2][j-1])/6;//也就是p[2][j+2]-p[2][j-1]/6
        dst[j].green=(pG[2][j+2]-pG[2][j-1])/6;
        dst[j].blue=(pB[2][j+2]-pB[2][j-1])/6;
    }
    for(j=1;j<dim-1;j++){//最後一行,取包圍他的6個像素點的均值=p[dim][j+2]-p[dim][j-1]-p[dim-2][j+2]+p[dim-2][j-1]/6
        dst[(dim-1)*dim+j].red=(pR[dim][j+2]-pR[dim][j-1]-pR[dim-2][j+2]+pR[dim-2][j-1])/6;
        dst[(dim-1)*dim+j].green=(pG[dim][j+2]-pG[dim][j-1]-pG[dim-2][j+2]+pG[dim-2][j-1])/6;
        dst[(dim-1)*dim+j].blue=(pB[dim][j+2]-pB[dim][j-1]-pB[dim-2][j+2]+pB[dim-2][j-1])/6;
    }
    for(i=1;i<dim-1;i++){//第一列,取包圍他的6個像素點的均值p[i+2][2]-p[i-1][2]/6
        dst[i*dim].red=(pR[i+2][2]-pR[i-1][2])/6;
        dst[i*dim].green=(pG[i+2][2]-pG[i-1][2])/6;
        dst[i*dim].blue=(pB[i+2][2]-pB[i-1][2])/6;
    }
    for(i=1;i<dim-1;i++){//最後一列,取包圍他的6個像素點的均值p[i+2][dim]-p[i-1][dim]-p[i+2][dim-2]+p[i-1][dim-2]
        dst[i*dim+dim-1].red=(pR[i+2][dim]-pR[i-1][dim]-pR[i+2][dim-2]+pR[i-1][dim-2])/6;
        dst[i*dim+dim-1].green=(pG[i+2][dim]-pG[i-1][dim]-pG[i+2][dim-2]+pG[i-1][dim-2])/6;
        dst[i*dim+dim-1].blue=(pB[i+2][dim]-pB[i-1][dim]-pB[i+2][dim-2]+pB[i-1][dim-2])/6;
    }
    for(i=1;i<dim-1;i++)//中間像素點,取右下側前綴-左下側前綴-右上測前綴,+左上側前綴
    for(j=1;j<dim-1;j++){//p[i+2][j+2]-p[i+2][j-1]-p[i-1][j+2]+p[i-1][j-1]
        dst[i*dim+j].red=(pR[i+2][j+2]-pR[i+2][j-1]-pR[i-1][j+2]+pR[i-1][j-1])/9;
        dst[i*dim+j].green=(pG[i+2][j+2]-pG[i+2][j-1]-pG[i-1][j+2]+pG[i-1][j-1])/9;
        dst[i*dim+j].blue=(pB[i+2][j+2]-pB[i+2][j-1]-pB[i-1][j+2]+pB[i-1][j-1])/9;
    }
}

優化結果

在這裏插入圖片描述

結果分析

實驗結果:使用前綴和優化效果驚人,加速比提高到50.3,相比於之前的分塊,並行有很大的提升,但是其算法可讀性較差,模塊化不夠強。可以繼續優化:考慮重載運算符,直接進行結構體的加減乘除法則,提高可讀性。實際上我們執行這個算法也提高了並行性,爲什麼?因爲我們使用pR,pG,pB來計算而不是放在一起進行疊加運算,這無形中也避免疊加變量,提高了並行性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章