Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >用CUDA写出比Numpy更快的规约求和函数

用CUDA写出比Numpy更快的规约求和函数

作者头像
DechinPhy
发布于 2021-09-08 07:04:08
发布于 2021-09-08 07:04:08
95200
代码可运行
举报
文章被收录于专栏:Dechin的专栏Dechin的专栏
运行总次数:0
代码可运行

技术背景

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

CUDA的atomic运算

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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函数做一个对比,结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ 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的方案,实现起来非常方便,性能也很乐观,相比于自己动手实现一个不断切割、递归的规约函数,还是要容易快捷的多。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python的GPU编程实例——近邻表计算
GPU加速是现代工业各种场景中非常常用的一种技术,这得益于GPU计算的高度并行化。在Python中存在有多种GPU并行优化的解决方案,包括之前的博客中提到的cupy、pycuda和numba.cuda,都是GPU加速的标志性Python库。这里我们重点推numba.cuda这一解决方案,因为cupy的优势在于实现好了的众多的函数,在算法实现的灵活性上还比较欠缺;而pycuda虽然提供了很好的灵活性和相当高的性能,但是这要求我们必须在Python的代码中插入C代码,这显然是非常不Pythonic的解决方案。因此我们可以选择numba.cuda这一解决方案,只要在Python函数前方加一个numba.cuda.jit的修饰器,就可以在Python中用最Python的编程语法,实现GPU的加速效果。
DechinPhy
2021/09/08
2K0
Python实现GPU加速的基本操作
之前写过一篇讲述如何使用pycuda来在Python上写CUDA程序的博客。这个方案的特点在于完全遵循了CUDA程序的写法,只是支持了一些常用函数的接口,如果你需要自己写CUDA算子,那么就只能使用非常不Pythonic的写法。还有一种常见的方法是用cupy来替代numpy,相当于一个GPU版本的numpy。那么本文要讲述的是用numba自带的装饰器,来写一个非常Pythonic的CUDA程序。
DechinPhy
2021/09/08
3.2K0
Python实现GPU加速的基本操作
超过Numpy的速度有多难?试试Numba的GPU加速
Numpy是在Python中非常常用的一个库,不仅具有良好的接口文档和生态,还具备了最顶级的性能,这个库很大程度上的弥补了Python本身性能上的缺陷。虽然我们也可以自己使用Cython或者是在Python中调用C++的动态链接库,但是我们自己实现的方法不一定有Numpy实现的快,这得益于Numpy对于SIMD等技术的深入实现,把CPU的性能发挥到了极致。因此我们只能考虑弯道超车,尝试下能否用自己实现的GPU的算法来打败Numpy的实现。
DechinPhy
2021/08/31
2.4K0
超过Numpy的速度有多难?试试Numba的GPU加速
Python3实现打格点算法的GPU加速
在数学和物理学领域,总是充满了各种连续的函数模型。而当我们用现代计算机的技术去处理这些问题的时候,事实上是无法直接处理连续模型的,绝大多数的情况下都要转化成一个离散的模型再进行数值的计算。比如计算数值的积分,计算数值的二阶导数(海森矩阵)等等。这里我们所介绍的打格点的算法,正是一种典型的离散化方法。这个对空间做离散化的方法,可以在很大程度上简化运算量。比如在分子动力学模拟中,计算近邻表的时候,如果不采用打格点的方法,那么就要针对整个空间所有的原子进行搜索,计算出来距离再判断是否近邻。而如果采用打格点的方法,我们只需要先遍历一遍原子对齐进行打格点的离散化,之后再计算近邻表的时候,只需要计算三维空间下邻近的27个格子中的原子是否满足近邻条件即可。在这篇文章中,我们主要探讨如何用GPU来实现打格点的算法。
DechinPhy
2021/09/14
6530
Python3实现打格点算法的GPU加速
使用jax加速Hamming Distance的计算
一般认为Jax是谷歌为了取代TensorFlow而推出的一款全新的端到端可微的框架,但是Jax同时也集成了绝大部分的numpy函数,这就使得我们可以更加简便的从numpy的计算习惯中切换到GPU的计算中。Jax除了支持GPU的张量运算,更重要的一个方面是Jax还支持谷歌自己的硬件TPU的张量运算。关于张量计算,可以参考前面写过的这一篇博客。
DechinPhy
2021/11/10
1.3K0
使用numba加速python科学计算
python作为一门编程语言,有非常大的生态优势,但是其执行效率一直被人诟病。纯粹的python代码跑起来速度会非常的缓慢,因此很多对性能要求比较高的python库,需要用C++或者Fortran来构造底层算法模块,再用python进行上层封装的方案。在前面写过的这篇博客中,介绍了使用f2py将fortran代码编译成动态链接库的方案,这可以认为是一种“事前编译”的手段。但是本文将要介绍一种即时编译(Just In Time,简称JIT)的手段,也就是在临近执行函数前,才对其进行编译。以下截图来自于参考链接4,讲述了关于常见的一些编译场景的区别:
DechinPhy
2021/05/21
2K0
从头开始进行CUDA编程:原子指令和互斥锁
在前三部分中我们介绍了CUDA开发的大部分基础知识,例如启动内核来执行并行任务、利用共享内存来执行快速归并、将可重用逻辑封装为设备函数以及如何使用事件和流来组织和控制内核执行。
deephub
2023/02/01
1.2K0
从头开始进行CUDA编程:线程间协作的常见技术
在前一篇文章中,我们介绍了如何使用 GPU 运行的并行算法。这些并行任务是那些完全相互独立的任务,这点与我们一般认识的编程方式有很大的不同,虽然我们可以从并行中受益,但是这种奇葩的并行运行方式对于我们来说肯定感到非常的复杂。所以在本篇文章的Numba代码中,我们将介绍一些允许线程在计算中协作的常见技术。
deephub
2023/01/18
9390
Pandas、Numpy性能优化秘籍(全)
pandas、numpy是Python数据科学中非常常用的库,numpy是Python的数值计算扩展,专门用来处理矩阵,它的运算效率比列表更高效。pandas是基于numpy的数据处理工具,能更方便的操作大型表格类型的数据集。但是,随着数据量的剧增,有时numpy和pandas的速度就成瓶颈。
算法进阶
2022/06/02
2.9K0
Pandas、Numpy性能优化秘籍(全)
从头开始进行CUDA编程:流和事件
前两篇文章我们介绍了如何使用GPU编程执行简单的任务,比如令人难以理解的并行任务、使用共享内存归并(reduce)和设备函数。为了提高我们的并行处理能力,本文介绍CUDA事件和如何使用它们。但是在深入研究之前,我们将首先讨论CUDA流。
deephub
2023/01/18
1.1K0
从头开始进行CUDA编程:Numba并行编程的基本概念
PU(图形处理单元)最初是为计算机图形开发的,但是现在它们几乎在所有需要高计算吞吐量的领域无处不在。这一发展是由GPGPU(通用GPU)接口的开发实现的,它允许我们使用GPU进行通用计算编程。这些接口中最常见的是CUDA,其次是OpenCL和最近刚出现的HIP。
deephub
2023/01/18
1.4K0
Python 提速大杀器之 numba 篇
你是不是曾经有这样的苦恼,python 真的太好用了,但是它真的好慢啊(哭死) ; C++ 很快,但是真的好难写啊,此生能不碰它就不碰它。老天啊,有没有什么两全其美的办法呢?俗话说的好:办法总是比困难多,大家都有这个问题,自然也就有大佬来试着解决这个问题,这就请出我们今天的主角: numba
OpenMMLab 官方账号
2022/01/18
3K0
Numpy应用整理
numpy是python最为常用的库,没有之一,它表示Numeric Python,从名字也可以看出来,它被用来做数值计算,常与scipy配合使用。现在几乎各种应用场合都会用到numpy,主要有以下几个原因:
猫叔Rex
2020/06/28
1.1K0
Numpy计算近邻表时间对比
所谓的近邻表求解,就是给定N个原子的体系,找出满足cutoff要求的每一对原子。在前面的几篇博客中,我们分别介绍过CUDA近邻表计算与JAX-MD关于格点法求解近邻表的实现。虽然我们从理论上可以知道,用格点法求解近邻表,在复杂度上肯定是要优于传统的算法。本文主要从Python代码的实现上来具体测试一下二者的速度差异,这里使用的硬件还是CPU。
DechinPhy
2024/01/10
2350
Numpy计算近邻表时间对比
python3使用concurrent执行多进程任务
随着计算机技术的发展,诸如GPU和超算平台等越来越发达,这些技术的本质其实并没有带来算法上的革新,之所以能够提升计算的速度和规模,很大程度上是因为分布式和并行计算的优势。这里我们介绍一个简单的python自带的多进程的代码实现,使用的是concurrent这个工具,同时我们也会介绍如何更好的配置多进程的资源。
DechinPhy
2021/05/21
9130
GPU加速04:将CUDA应用于金融领域,使用Python Numba加速B-S期权估值模型
本文为英伟达GPU计算加速系列的第四篇,主要基于前三篇文章的内容,以金融领域期权估值案例来进行实战练习。前三篇文章为:
PP鲁
2019/12/26
1.9K0
Python Numpy性能提升的利器Numa优化技巧
在数据分析和科学计算中,Python和Numpy是非常流行的工具组合。然而,随着数据量的增加,Python解释器在处理大规模数组时的性能可能无法满足需求。为了提升Python代码的执行效率,Numba成为了一个强大的工具。Numba是一个基于LLVM的即时编译器,它可以将Python代码编译为高效的机器代码,从而极大地提升Numpy数组操作的性能。
sergiojune
2024/10/21
2020
Python Numpy性能提升的利器Numa优化技巧
GPU加速02:超详细Python Cuda零基础入门教程,没有显卡也能学!
Python是当前最流行的编程语言,被广泛应用在深度学习、金融建模、科学和工程计算上。作为一门解释型语言,它运行速度慢也常常被用户诟病。著名Python发行商Anaconda公司开发的Numba库为程序员提供了Python版CPU和GPU编程工具,速度比原生Python快数十倍甚至更多。使用Numba进行GPU编程,你可以享受:
PP鲁
2019/12/26
6.8K0
GPU加速02:超详细Python Cuda零基础入门教程,没有显卡也能学!
GPU随机采样速度比较
随机采样问题,不仅仅只是一个统计学/离散数学上的概念,其实在工业领域也都有非常重要的应用价值/潜在应用价值,具体应用场景我们这里就不做赘述。本文重点在于在不同平台上的采样速率,至于另外一个重要的参数检验速率,这里我们先不做评估。因为在Jax中直接支持vmap的操作,而numpy的原生函数大多也支持了向量化的运算,两者更像是同一种算法的不同实现。所以对于检验的场景,两者的速度区别更多的也是在硬件平台上。
DechinPhy
2021/12/10
3660
[Python技巧]如何加快循环操作和Numpy数组运算速度
在 24式加速你的Python中介绍对循环的加速方法中,一个办法就是采用 Numba 加速,刚好最近看到一篇文章介绍了利用 Numba 加速 Python ,文章主要介绍了两个例子,也是 Numba 的两大作用,分别是加速循环,以及对 Numpy 的计算加速。
kbsc13
2019/08/16
10K0
相关推荐
Python的GPU编程实例——近邻表计算
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验