2010年10月3日 星期日

初試Visual Studio 2010平行處理函式 (PPL)

VS2010發表時很強調他的平行處理函式的功能,因此我也嘗試看看是不是真如廣告說的那麼神奇。測試使用一張8MB(32bit)的影像進行500次的平滑化處理。

平滑化處理公式為

I'(x) = (I(x) - dc) * W(x)

首先,必載入平行處理函式的檔頭,由於這是2010才支援的功能,我會先判斷_MSC_VER是否為1600以上的版本,以確認編譯器有支援

#if  _MSC_VER >= 1600
#include "ppl.h"

using namespace Concurrency;
using namespace std;
#endif  //  _MSC_VER

接著將原有的

unsigned long dwPos = 0;
long value = 0;

for(unsigned long i=0 ; i<dwSize ; i++)
{
    value = (((pIData[dwPos] - DC[0]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos++;

    value = (((pIData[dwPos] - DC[1]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos++;

    value = (((pIData[dwPos] - DC[2]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos+=2;
}

改寫為

unsigned long dwPos = 0;
long value = 0;

parallel_for(unsigned long(0),dwSize,[&](unsigned long i)
{
    dwPos = (i << 2);

    value = (((pIData[dwPos] - DC[0]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos++;

    value = (((pIData[dwPos] - DC[1]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos++;

    value = (((pIData[dwPos] - DC[2]) * pWeight[dwPos]) >> 8);

    if(value < 0)         pOData[dwPos] = 0;
    else if(value > 255)  pOData[dwPos] = 255;
    else                  pOData[dwPos] = (unsigned char)value;

    dwPos+=2;
});

這裡我犯了一個明顯的錯誤,在做並行化處理時必注意資料必需保持獨立性,共享資料則必需唯讀或同步化,上述的程式執行後,影像上會有明顯的雜點,主要原因是共享資料dwPosvalue並未保持唯讀的特性,使得資料混亂。

解決方案為將dwPosvalue變為local的變數不共享,程式修改如下:

parallel_for(unsigned long(0),dwSize,unsigned long(1),[&](unsigned long i)
{
    for(int c=0 ; c<3 ; c++)
    {
        unsigned long dwPos = (i << 2) + c;

        long value = (((pIData[dwPos] - DC[c]) * pWeight[dwPos]) >> 8);

        if(value < 0)         pOData[dwPos] = 0;
        else if(value > 255)  pOData[dwPos] = 255;
        else                  pOData[dwPos] = (unsigned char)value;
    }
});
 
先在Core dual 2.2G + XP系統上執行,比較serial / parallel上執行的效能

serial:       0.01639851 sec / frame
paralle:     0.03472216 sec / frame

在雙核主機上serial的效能比parallel

換成 Coro i7 920 2.7G + XP64再次比較
serial:       0.01312059
parallel:    0.01394287

平行處理的效能與使用單處理器相當,也就是說,單純使用平行處理函式的速度並未有提升而且可能降低原有的效能。看來事實並不如Microsoft廣告說的如此簡單,要發揮出真正的效能仍有不少最佳化的動作要做。

沒有留言:

張貼留言