天天看點

OpenCL 學習step by step (11) 數組求和(reduction)

     本篇教程中,我們學習一下如何用opencl有效實作數組求和,也就是通常所說的reduction問題。

     在程式中,我們設定workgroup size為256,kernel的輸入、輸出緩沖參數都用uint4的格式,這樣我們原始求和的數組大小為256*4的倍數,資料類型為uint。我們設定每個workgroup處理處理512個uint4,即2048個uint

     為了簡便期間,我們輸出數組長度定為4096,即需要2個workgruop來處理。

kernel代碼如下:

__kernel void reduce(__global uint4* input, __global uint4* output, __local uint4* sdata)

{

    // 把資料裝入lds

    unsigned int tid = get_local_id(0);

    unsigned int bid = get_group_id(0);

    unsigned int gid = get_global_id(0);

    unsigned int localSize = get_local_size(0);

    unsigned int stride = gid * 2;

    sdata[tid] = input[stride] + input[stride + 1];

    barrier(CLK_LOCAL_MEM_FENCE);

    // 在lds中進行reduction操作,得到數組求和的結果

    for(unsigned int s = localSize >> 1; s > 0; s >>= 1)

    {

        if(tid < s)

        {

            sdata[tid] += sdata[tid + s];

        }

        barrier(CLK_LOCAL_MEM_FENCE);

    }

   // 把一個workgroup計算的結果輸出到輸出緩沖,是一個uint4,還需要在host端再進行一次reduction過程

    if(tid == 0) output[bid] = sdata[0];

}

     在程式中,global和local的NDRange,我們都用一維的形式。下面以圖的方式看下kernel代碼是如何執行的:

OpenCL 學習step by step (11) 數組求和(reduction)

      對第一個workgroup中的第一個thread的來說,它首先進行一次reduction操作,把兩個uint4相加,放到lds(shared memory)中,然後再在lds中進行reduction操作,此時要從global memory中取資料,可以看出連續的thread通路連續的global memory,這時可以利用合并讀寫。

      申請的shared memory大小為groupsize*sizeof(uint4),相加後uint4放入32bank的lds中,放置的方式應該是如下圖所示,因為放入的是uint4,是以會放入連續的4個bank中(每個bank都是dword寬),可見隻能同時有8個thread通路lds,是以會有一定程式的bank conflit。從App profiler session,我們可以看到:

OpenCL 學習step by step (11) 數組求和(reduction)
OpenCL 學習step by step (11) 數組求和(reduction)

      接下來,kernel會通過一個for循環疊代執行reduction操作,求得一個workgroup中的uint4的和。

疊代的第一次s=128,這時會執行如下圖的兩兩相加,workgroup中同時執行的thread為128,thread local id大于等于128的線程都不會做什麼事情,在每個循環的末尾,有一個barrier來同步所有thread,以便所有thread都完成這次循環後再進入下一次循環。

OpenCL 學習step by step (11) 數組求和(reduction)

     第二次疊代的時候,隻剩下前面128個uint4,workgroup中同時執行的thread為64。最後,當s=1時候,完成疊代reduction操作,然後把thread0(第一個thread)的結果輸出。

     在host段,我們還要做一次相加操作,把不同workgroup得到的uint4,拆分成uint,并相加求得最終的結果。

//在cpu reduction各個workgroup的結果以及uint4分量 reduction

output = 0;

for(int i = 0; i < numBlocks * VECTOR_SIZE; ++i)

    output += outMapPtr[i];

printf("gpu reduction result:%d\n", output);

if(refOutput==output) printf("passed\n");

程式執行後結果如下:

OpenCL 學習step by step (11) 數組求和(reduction)

完整的代碼請參考:

工程檔案gclTutorial11

代碼下載下傳:

稍後提供

繼續閱讀