技术背景

在前面的几篇博客中我们介绍了在Python中使用Numba来写CUDA程序的一些基本操作和方法,并且展示了GPU加速的实际效果。在可并行化的算法中,比如计算两个矢量的加和,或者是在分子动力学模拟领域中的查找近邻表等等,都是可以直接并行的算法,而且实现起来难度不大。而有一种情况是,如果我们要计算的内容的线程之间互相存在依赖,比方说最常见的,计算一个矩阵所有元素的和。

CUDA的atomic运算

正如前面所提到的问题,如何去计算一个矩阵所有元素之和呢?具体问题可以表述为:

\[S=\sum_{i,j}A_{i,j}
\]

对于此类的问题,如果我们像普通的CUDA并行操作一样,直接创建一个S变量,然后直接在线程和分块上直接把每一个矩阵元素加到这个S变量中,那么会出现一种情况:在线程同步时,存在冲突的线程是无法同时加和成功的,也就是说,这种情况下虽然程序不会报错,但是得到的结果是完全错误的。对于此类情况,CUDA官方给出了atomic运算这样的方案,可以保障线程之间不被干扰:

import numpy as np
from numba import cuda
from numba import vectorize
cuda.select_device(1) @cuda.jit
def ReducedSum(arr, result):
i, j = cuda.grid(2)
cuda.atomic.add(result, 0, arr[i][j]) if __name__ == '__main__':
import time
np.random.seed(2)
data_length = 2**10
arr = np.random.random((data_length,data_length)).astype(np.float32)
print (arr)
arr_cuda = cuda.to_device(arr)
np_time = 0.0
nb_time = 0.0
for i in range(100):
res = np.array([0],dtype=np.float32)
res_cuda = cuda.to_device(res)
time0 = time.time()
ReducedSum[(data_length,data_length),(1,1)](arr_cuda,res_cuda)
time1 = time.time()
res = res_cuda.copy_to_host()[0]
time2 = time.time()
np_res = np.sum(arr)
time3 = time.time()
if i == 0:
print ('The error rate is: ', abs(np_res-res)/res)
continue
np_time += time3 - time2
nb_time += time1 - time0
print ('The time cost of numpy is: {}s'.format(np_time))
print ('The time cost of numba is: {}s'.format(nb_time))

这里需要重点关注的就是用CUDA实现的简单函数ReducedSum,这个函数中调用了CUDA的atomic.add方法,用这个方法直接替代系统内置的加法,就完成了所有的操作。我们将这个函数的运行时间去跟np.sum函数做一个对比,结果如下:

$ python3 cuda_reduced_sum.py
[[0.4359949 0.02592623 0.5496625 ... 0.3810055 0.6834749 0.5225032 ]
[0.62763107 0.3184925 0.5822277 ... 0.89322233 0.7845663 0.4595605 ]
[0.9666947 0.16615923 0.6931703 ... 0.29497907 0.63724256 0.06265242]
...
[0.96224505 0.36741972 0.6673239 ... 0.3115176 0.7561843 0.9396167 ]
[0.781736 0.28829736 0.38047555 ... 0.15837361 0.00392629 0.6236886 ]
[0.03247315 0.3664344 0.00369871 ... 0.0205253 0.15924706 0.8655231 ]]
The error rate is: 4.177044e-06
The time cost of numpy is: 0.027491092681884766s
The time cost of numba is: 0.01042938232421875s

在GPU的计算中,会有一定的精度损失,比如这里的误差率就在1e-06级别,但是运行的速度要比numpy的实现快上2倍!

总结概要

我们知道GPU加速在可并行化程度比较高的算法中,能够发挥出比较大的作用,展示出明显的加速效果,而对于一些线程之间存在依赖这样的场景就不一定能够起到很大的加速作用。CUDA官方针对此类问题,提供了atomic的内置函数解决方案,包含有求和、求最大值等常用函数。而这些函数的特点就在于,线程与线程之间需要有一个时序的依赖关系。就比如说求最大值的函数,它会涉及到不同线程之间的轮询。经过测试,CUDA的这种atomic的方案,实现起来非常方便,性能也很乐观,相比于自己动手实现一个不断切割、递归的规约函数,还是要容易快捷的多。

版权声明

本文首发链接为:https://www.cnblogs.com/dechinphy/p/gpu-sum.html

作者ID:DechinPhy

更多原著文章请参考:https://www.cnblogs.com/dechinphy/

打赏专用链接:https://www.cnblogs.com/dechinphy/gallery/image/379634.html

腾讯云专栏同步:https://cloud.tencent.com/developer/column/91958

用CUDA写出比Numpy更快的规约求和函数的更多相关文章

  1. 1. 写出一个能创建多级目录的 PHP 函数(新浪网技术部)

    function create_dir($path,$mode){ if (is_dir($path)){ echo "该目录已经存在"; }else{ if(mkdir($pat ...

  2. 面试官:如何写出让 CPU 跑得更快的代码?

    前言 代码都是由 CPU 跑起来的,我们代码写的好与坏就决定了 CPU 的执行效率,特别是在编写计算密集型的程序,更要注重 CPU 的执行效率,否则将会大大影响系统性能. CPU 内部嵌入了 CPU ...

  3. webstorm注释写出的提示

    写出这种代码提示的方法是 在一个函数上方打出 /** 然后敲回车就出出来 没达到上面的效果,自己手动写上即可. 这样的好处是: 当你写代码用到此方法的时候会有参数类型提示,如图

  4. php--------使用 isset()判断字符串长度速度比strlen()更快

    isset()速度为什么比strlen()更快呢? strlen()函数函数执行起来相当快,因为它不做任何计算,只返回在zval 结构(C的内置数据结构,用于存储PHP变量)中存储的已知字符串长度.但 ...

  5. 扯扯淡,写个更快的memcpy

    写代码有时候和笃信宗教一样,一旦信仰崩溃,是最难受的事情.早年我读过云风的一篇<VC 对 memcpy 的优化>,以及<Efficiency geek 2: copying data ...

  6. 使用Groovy+Spock轻松写出更简洁的单测

    当无法避免做一件事时,那就让它变得更简单. 概述 单测是规范的软件开发流程中的必不可少的环节之一.再伟大的程序员也难以避免自己不犯错,不写出有BUG的程序.单测就是用来检测BUG的.Java阵营中,J ...

  7. [label][翻译][JavaScript-Translation]七个步骤让你写出更好的JavaScript代码

    7 steps to better JavaScript 原文链接: http://www.creativebloq.com/netmag/7-steps-better-javascript-5141 ...

  8. QT就是别人好心帮你做一些枯燥,并且很重复的代码编写工作,让你更好的把精力投入到你界面的逻辑和功能的实现的功能库(否则写了上万行代码了,才写出个BUG一大堆的毛坯)

    好了,现在开始记录我学习QT的学习历程 . 本人也不是计算机专业出来的,自学了一点,但还是不好找工作,于是参加了培训,虽然感觉没多学到什么 编程的学习生涯就是不断的看别人的源码,然后自己参考着写写自己 ...

  9. 如何在 ASP.NET Core 中写出更干净的 Controller

    你可以遵循一些最佳实践来写出更干净的 Controller,一般我们称这种方法写出来的 Controller 为瘦Controller,瘦 Controller 的好处在于拥有更少的代码,更加单一的职 ...

随机推荐

  1. 期望长度P1365,CF235B,P1654

    期望长度 定义 这里期望长度表示一段序列连续长度的期望.具体来说,对于一段序列,每个点都有一个概率连续和断开.求所有连续序列和的期望. 当然,对于以上期望长度的定义,我们只需要求出每个点存在的期望的和 ...

  2. 搭建kerberos和NTP服务器以及安全的NFS服务

    说明:这里是Linux服务综合搭建文章的一部分,本文可以作为单独搭建Kerberos和NTP时钟服务的参考. 注意:这里所有的标题都是根据主要的文章(Linux基础服务搭建综合)的顺序来做的. 如果需 ...

  3. JAVA学习笔记之基础概念(一)

    一.Java 简介: Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称. 由 James Gosling和同 ...

  4. C++第三十四篇 -- 安装Windows Driver后,编译以前项目出现打不开lib文件

    VS2017默认是没有安装WDK的,但是我们写驱动文件的话需要用到WDK.不过安装了WDK后,发现以前一些正常的项目在Release模式下编译会报LINK1104,无法打开.lib的错误 针对这个错误 ...

  5. Apache OfBiz 反序列化命令执行漏洞(CVE-2020-9496)

    影响版本 - Apache Ofbiz:< 17.12.04 访问 https://192.168.49.2:8443/webtools/control/xmlrpc 抓包 进行数据包修改 pa ...

  6. MERCY靶机

    仅供个人娱乐 靶机信息 下载地址:https://drive.google.com/uc?id=1YzsW1lCKjo_WEr6Pk511DXQBFyMMR14y&export=downloa ...

  7. 规模化敏捷 LeSS(三):LeSS Huge 是怎样炼成的?

    上篇文章< LeSS 团队实践指南>中讲到 LeSS 框架中的团队数量不要超过8个,但8个以上的团队要如何实践敏捷呢? 为了应对8个以上团队实践敏捷的情况,Bas 以及 Carig 还提出 ...

  8. appium的安装和环境配置教程

    模拟器安装 夜神模拟器下载地址:https://www.yeshen.com/ 无脑安装 jdk环境 安装jdk 安装教程:https://www.cnblogs.com/yhoil/p/148086 ...

  9. PXE高效装机

    目录 一.PXE概述 二.PXE的优点 三.搭建PXE的前提 四.部署PXE远程安装服务 1.安装TFTP服务 2.修改TFTP服务的配置文件,并开启服务 关闭防火墙 3.安装DHCP服务 4.修改D ...

  10. 抄书抄博客毒害社区的Writer几时休?

    曾几何时,博客园用户也渐渐分成了两类人:Writer和Coder. 何为Coder?就是认认真真写代码,平时分享工作中的一些问题.好的解决方案,或者写一些实用的原理技术文.答疑解惑的教程技术文. 何为 ...