1. 使用 Thrust

Thrust 是一个开源的 C++ 库,用于开发高性能并行应用程序,以 C++ 标准模板库为蓝本实现。

官方文档见这里:CUDA Thrust

/* ... */

float *fMatrix_Device; // 指向设备显存

int iMatrixSize = iRow * iCol; // 矩阵元素个数

cudaMalloc((void**)&fMatrix_Device, iMatrixSize * sizeof(float)); // 在显存中为矩阵开辟空间

cudaMemcpy(fMatrix_Device, fMatrix_Host, iMatrixSize * sizeof(float), cudaMemcpyHostToDevice); // 将数据拷贝到显存

thrust::device_ptr<float> dev_ptr(fMatrix_Device);

float thrustResult = thrust::reduce(dev_ptr, dev_ptr + size_t(iMatrixSize), (float)0, thrust::plus<float>());

其中,fMatrix_Host 为指向主机内存的矩阵的头指针。

2. 我的 Reduction

/**

* 每个 warp 自动同步,不用 __syncthreads();

* volatile : 加上关键字volatile的变量将被定义为敏感变量,意思是加了volatile

*            的变量在内存中的值可能会随时发生变化,当程序要去读取这个变量时,

             必须要从内存中读取,而不是从缓存中读取

* sdata  数组头指针,数组位于共享内存

* tid    线程索引

*/

__device__ void warpReduce(volatile float *sdata, int tid)

{

    sdata[tid] += sdata[tid + 32];

    sdata[tid] += sdata[tid + 16];

    sdata[tid] += sdata[tid + 8];

    sdata[tid] += sdata[tid + 4];

    sdata[tid] += sdata[tid + 2];

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

}

/**

* 优化:解决了 reduce3 中存在的多余同步操作(每个warp默认自动同步)。

* globalInputData  输入数据,位于全局内存

* globalOutputData 输出数据,位于全局内存

*/

__global__ void reduce4(float *globalInputData, float *globalOutputData, unsigned int n)

{

    __shared__ float sdata[BLOCK_SIZE];

    // 坐标索引

    unsigned int tid = threadIdx.x;

    unsigned int index = blockIdx.x*(blockDim.x * 2) + threadIdx.x;

    unsigned int indexWithOffset = index + blockDim.x;

    if (index >= n) sdata[tid] = 0;

    else if (indexWithOffset >= n) sdata[tid] = globalInputData[index];

    else sdata[tid] = globalInputData[index] + globalInputData[indexWithOffset];

    __syncthreads();

    // 在共享内存中对每一个块进行规约计算

    for (unsigned int s = blockDim.x / 2; s>32; s >>= 1)

    {

        if (tid < s) sdata[tid] += sdata[tid + s];

        __syncthreads();

    }

    if (tid < 32) warpReduce(sdata, tid);

    // 把计算结果从共享内存写回全局内存

    if (tid == 0) globalOutputData[blockIdx.x] = sdata[0];

}

/**

* 计算 reduce4 函数的时间

* fMatrix_Host  矩阵头指针

* iRow          矩阵行数

* iCol          矩阵列数

* @return       和

*/

float RuntimeOfReduce4(float *fMatrix_Host, const int iRow, const int iCol)

{

    float *fReuslt = (float*)malloc(sizeof(float));;

    float *fMatrix_Device; // 指向设备显存

    int iMatrixSize = iRow * iCol; // 矩阵元素个数

    cudaMalloc((void**)&fMatrix_Device, iMatrixSize * sizeof(float)); // 在显存中为矩阵开辟空间

    cudaMemcpy(fMatrix_Device, fMatrix_Host, iMatrixSize * sizeof(float), cudaMemcpyHostToDevice); // 将数据拷贝到显存

    /* ... */

    for (int i = 1, int iNum = iMatrixSize; i < iMatrixSize; i = 2 * i * BLOCK_SIZE)

    {

        int iBlockNum = (iNum + (2 * BLOCK_SIZE) - 1) / (2 * BLOCK_SIZE);

        reduce4<<<iBlockNum, BLOCK_SIZE>>>(fMatrix_Device, fMatrix_Device, iNum);

        iNum = iBlockNum;

    }

    cudaMemcpy(fReuslt, fMatrix_Device, sizeof(float), cudaMemcpyDeviceToHost); // 将数据拷贝到内存

    /* ... */

    cudaFree(fMatrix_Device);// 释放显存空间

    return fReuslt[0];

}

上述程序是优化的最终版本,优化的主要内容包括:

1. 避免每个 Warp 中出现分支导致效率低下。 

2. 减少取余操作。 

3. 减小不必要的同步操作,每个warp都是默认同步的,不用额外的同步操作。 

4. 减小线程的闲置,提高并行度

3. 时间对比

数据的大小为:

iRow = 1000; 

iCol = 1000;

时间为:

ReduceThrust 的运行时间为:0.179968ms.

494497

Reduce0 的运行时间为:0.229152ms.

494497

Reduce1 的运行时间为:0.134816ms.

494497

Reduce2 的运行时间为:0.117504ms.

494497

Reduce3 的运行时间为:0.086016ms.

494497

Reduce4 的运行时间为:0.07424ms.

494497

CPU的运行时间为:1 ms.

494497

数据的大小为:

iRow = 2000; 

iCol = 2000;

时间为:

ReduceThrust 的运行时间为:0.282944ms.

1.97828e+006

Reduce0 的运行时间为:0.779776ms.

1.97828e+006

Reduce1 的运行时间为:0.42624ms.

1.97828e+006

Reduce2 的运行时间为:0.343744ms.

1.97828e+006

Reduce3 的运行时间为:0.217248ms.

1.97828e+006

Reduce4 的运行时间为:0.160416ms.

1.97828e+006

CPU的运行时间为:3 ms.

1.97828e+006

数据的大小为:

iRow = 4000; 

iCol = 4000;

时间为:

ReduceThrust 的运行时间为:0.536832ms.

7.91319e+006

Reduce0 的运行时间为:2.9919ms.

7.91319e+006

Reduce1 的运行时间为:1.56054ms.

7.91319e+006

Reduce2 的运行时间为:1.26618ms.

7.91319e+006

Reduce3 的运行时间为:0.726016ms.

7.91319e+006

Reduce4 的运行时间为:0.531712ms.

7.91319e+006

CPU的运行时间为:11 ms.

7.91319e+006

数据的大小为:

iRow = 6000; 

iCol = 6000;

时间为:

ReduceThrust 的运行时间为:0.988992ms.

1.7807e+007

Reduce4 的运行时间为:1.09286ms.

1.7807e+007

CPU的运行时间为:25 ms.

1.7807e+007

数据的大小为:

iRow = 11000; 

iCol = 11000;

时间为:

ReduceThrust 的运行时间为:2.9208ms.

5.98583e+007

Reduce4 的运行时间为:3.36998ms.

5.98583e+007

CPU的运行时间为:85 ms.

5.98583e+007

从上可以看出,2 中介绍的几种优化方式取得了良好的效果;另外,当数据量较少时,我自己优化的规约函数比 Thrust 中的规约更高效,但是当数据量大于 4000 * 4000 时,Thrust 更高效,因此还有优化的空间。

4. 完整代码

GitHub

【CUDA开发】 CUDA Thrust 规约求和的更多相关文章

  1. CUDA开发 - CUDA 版本

    "CUDA runtime is insufficient with CUDA driver"CUDA 9.2: 396.xx CUDA 9.1: 387.xx CUDA 9.0: ...

  2. 【CUDA开发】Thrust库

    Thrust库从C++的STL中得到灵感,将最简单的类似于STL的结构放在Thrust库中,比如STL中的vector.此外,Thrust库还包含STL中的算法和迭代器.        Thrust函 ...

  3. Windows平台CUDA开发之前的准备工作

    CUDA是NVIDIA的GPU开发工具,眼下在大规模并行计算领域有着广泛应用. windows平台上面的CUDA开发之前.最好去NVIDIA官网查看说明,然后下载对应的driver. ToolKits ...

  4. 【ARM-Linux开发】【CUDA开发】【深度学习与神经网络】Jetson Tx2安装相关之三

    JetPack(Jetson SDK)是一个按需的一体化软件包,捆绑了NVIDIA®Jetson嵌入式平台的开发人员软件.JetPack 3.0包括对Jetson TX2 , Jetson TX1和J ...

  5. 【CUDA开发】CUDA面内存拷贝用法总结

    [CUDA开发]CUDA面内存拷贝用法总结 标签(空格分隔): [CUDA开发] 主要是在调试CUDA硬解码并用D3D9或者D3D11显示的时候遇到了一些代码,如下所示: CUdeviceptr g_ ...

  6. 【CUDA开发】CUDA编程接口(一)------一十八般武器

    子曰:工欲善其事,必先利其器.我们要把显卡作为通用并行处理器来做并行算法处理,就得知道CUDA给我提供了什么样的接口,就得了解CUDA作为通用高性能计算平台上的一十八般武器.(如果你想自己开发驱动,自 ...

  7. 【神经网络与深度学习】【CUDA开发】caffe-windows win32下的编译尝试

    [神经网络与深度学习][CUDA开发]caffe-windows win32下的编译尝试 标签:[神经网络与深度学习] [CUDA开发] 主要是在开发Qt的应用程序时,需要的是有一个使用的库文件也只是 ...

  8. 【神经网络与深度学习】【CUDA开发】【VS开发】Caffe+VS2013+CUDA7.5+cuDNN配置过程说明

    [神经网络与深度学习][CUDA开发][VS开发]Caffe+VS2013+CUDA7.5+cuDNN配置过程说明 标签:[Qt开发] 说明:这个工具在Windows上的配置真的是让我纠结万分,大部分 ...

  9. 【视频开发】【CUDA开发】ffmpeg Nvidia硬件加速总结

    原文链接:https://developer.nvidia.com/ffmpeg GPU-accelerated video processing integrated into the most p ...

随机推荐

  1. BZOJ 2157: 旅游 (树链剖分+线段树)

    树链剖分后线段树维护区间最大最小值与和. 支持单点修改与区间取反. 直接写个区间取反标记就行了.线段树板题.(200行6000B+ 1A警告) #include <cstdio> #inc ...

  2. SpringBoot入门系列:第五篇 JPA mysql(转)

    一,准备工作,建立spring-boot-sample-mysql工程1.http://start.spring.io/ A.Artifact中输入spring-boot-sample-mysql B ...

  3. Windows系统下载地址

    地址: https://msdn.itellyou.cn/ 里面给出的是迅雷下载链接,请提前安装好迅雷

  4. 题解 [SHOI2010]最小生成树

     题面 解析 看上去是黑题啊! 实际上也就是道网络流最大流. 当然,我们也知道网络流最关键的是建图. 首先,分析一下题目: 题目要求在操作后使给定的边lab一定在最小生成树上, 求最小的操作数. 先设 ...

  5. 题解 【USACO 4.2.1】草地排水

    [USACO 4.2.1]草地排水 Description 在农夫约翰的农场上,每逢下雨,贝茜最喜欢的三叶草地就积聚了一潭水.这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间.因此,农夫 ...

  6. np.max() 和 np.maximum()的区别

    1.np.max(a, axis=None, out=None, keepdims=False) 求序列的最值 最少接受一个参数 axis默认为axis=0即列向,如果axis=1即横向 ex: &g ...

  7. ZAP-Queries【luogu3455】

    题目大意 有不超过\(50000\)个询问,每次询问有多少正整数对\(x\),\(y\),满足\(x\leqslant a\),\(y \leqslant b\),并且\(gcd(x,y)=c\).其 ...

  8. Codeforces Gym Joyride(分层图,dijkstra)

    题意:有一张图,每条边有一个边权t表示经过所花时间,每个点有两个权t.p,分别表示经过该点所花时间和所花费用,要求找一条路径,从点1出发再回到点1,所花时间恰好为x且费用最小,输出其费用,找不到则输出 ...

  9. Jmeter -- 脚本录制

    步骤如下: 1. 添加http代理服务器(Add -> Non-TestElement -> HTTP(S)Test Script Recorder) 2. 对http代理进行配置,如下图 ...

  10. 两台Linux服务器之间的文件传输

    最近工作中有这样一个需求,需要将A服务器上的文件传到B服务器. 本来想用Java开发,但一想Java开发周期长,应对这样一个小需求没必要用Java,最后选择了Shell脚本,相关代码如下: #!/bi ...