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代碼是如何執行的:

image

      對第一個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,我們可以看到:

image

image

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

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

image

     第二次迭代的時候,只剩下前面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");

程序執行后結果如下:

image

 

完整的代碼請參考:

工程文件gclTutorial11

代碼下載:

稍后提供

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM