OpenCL 学习step by step (11) 数组求和(reduction)

简介: 本篇教程中,我们学习一下如何用opencl有效实现数组求和,也就是通常所说的reduction问题。      在程序中,我们设置workgroup size为256,kernel的输入、输出缓冲参数都用uint4的格式,这样我们原始求和的数组大小为256*4的倍数,数据类型为uint。

     本篇教程中,我们学习一下如何用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

代码下载:

稍后提供

 

相关文章
|
计算机视觉
AutoCAD *.dxf文件解析,使用dxflib搞定polyline/spline/ellipse等复杂图形(★firecat推荐★)
AutoCAD *.dxf文件解析,使用dxflib搞定polyline/spline/ellipse等复杂图形(★firecat推荐★)
2733 0
AutoCAD *.dxf文件解析,使用dxflib搞定polyline/spline/ellipse等复杂图形(★firecat推荐★)
|
Java 关系型数据库 MySQL
mysql5.7 jdbc驱动
遵循上述步骤,即可在Java项目中高效地集成MySQL 5.7 JDBC驱动,实现数据库的访问与管理。
2629 1
|
并行计算 C++ 异构计算
CUDA编程一天入门
本文介绍了CUDA编程的基础知识,包括环境准备、编程模型、内核设置、示例代码simpleTexture3D,以及相关参考链接。
773 0
CUDA编程一天入门
QT设置widget背景图片
该内容介绍如何在Qt中为控件添加背景图片。主要方法包括:1) 在样式表中使用`border-image`属性指定控件及其背景图片;2) 使用调色板`QPalette`设置图片,但可能导致窗口显示不下;3) 在`paintEvent`中绘制图片,适合自定义绘图但不适用于子窗口;4) 通过覆盖一个大小与窗口相同的`QLabel`来设置背景图片,可实现动态背景。推荐使用样式表设置背景,简单高效且适合子窗口。
941 3
|
NoSQL MongoDB
解决 :MongoDB couldn‘t add user: not authorized on ‘your db‘ to execute command
解决 :MongoDB couldn‘t add user: not authorized on ‘your db‘ to execute command
913 0
|
小程序 Java API
如何优雅的设计一个SDK
如何优雅的设计一个SDK
816 0
|
Linux
Linux系统中驱动框架基本概述
Linux系统中驱动框架基本概述
230 0
|
编译器 Linux C语言
[Eigen中文文档] 从入门开始...
这是一个非常简短的Eigen入门文章。该文章有两层目的。对于想要尽快开始编码的人来说,该文章是对Eigen库的最简单介绍。你可以把该文章作为教程的第一部分,这更加详细的解释了Eigen库。看完这个教程后可以继续阅读 The Matrix class教程。
1124 0
|
存储 安全 编译器
嵌入式Linux C(二)——数据类型(详)
嵌入式Linux C(二)——数据类型(详)
469 0
嵌入式Linux C(二)——数据类型(详)
|
安全 Ubuntu
交叉编译eigen 到hi3559
交叉编译eigen 到3559av100 csdn博客,本人搬运。
574 0