超过Numpy的速度有多难?试试Numba的GPU加速
技术背景
Numpy是在Python中非常常用的一个库,不仅具有良好的接口文档和生态,还具备了最顶级的性能,这个库很大程度上的弥补了Python本身性能上的缺陷。虽然我们也可以自己使用Cython或者是在Python中调用C++的动态链接库,但是我们自己实现的方法不一定有Numpy实现的快,这得益于Numpy对于SIMD等技术的深入实现,把CPU的性能发挥到了极致。因此我们只能考虑弯道超车,尝试下能否用自己实现的GPU的算法来打败Numpy的实现。
矩阵的元素乘
为了便于测试,我们这里使用矩阵的元素乘作为测试的案例。所谓的矩阵元素乘,就是矩阵每一个位置的元素对应相乘,注意区分于矩阵乘法,而我们这里为了节省内存,使用的是计算自身的平方这个案例。
# cuda_test.py
import numpy as np
import time
from numba import cuda
cuda.select_device(1)
@cuda.jit
def CudaSquare(x):
i, j = cuda.grid(2)
x[i][j] *= x[i][j]
if __name__ == '__main__':
np.random.seed(1)
array_length = 2**10
random_array = np.random.rand(array_length, array_length)
random_array_cuda = cuda.to_device(random_array)
square_array = np.square(random_array)
CudaSquare[(array_length,array_length),(1,1)](random_array_cuda)
square_array_cuda = random_array_cuda.copy_to_host()
print (np.sum(square_array-square_array_cuda))
这个案例主要是通过numba的cuda.jit这一装饰器来实现的GPU加速,在这个装饰器下的函数可以使用CUDA的语法,目前来看应该是最Pythonic的CUDA实现方案,相比于pycuda来说。这个被CUDA装饰的函数,只是将矩阵的每一个元素跟自身相乘,也就是取了一个平方,跟numpy.square的算法实现的是一样的,这里我们可以看看运行结果:
$ python3 cuda_test.py
0.0
这个打印的结果表示,用numba的cuda方案与用numpy的square函数计算出来的结果差值是0,也就是得到了完全一样的结果。需要注意的是,在GPU上的向量是不能够直接打印出来的,需要先用copy_to_host的方法拷贝到CPU上再进行打印。
numba.cuda加速效果测试
在上一个测试案例中,为了展示结果的一致性,我们使用了内存拷贝的方法,但是实际上我们如果把所有的运算都放在GPU上面来运行的话,就不涉及到内存拷贝,因此这部分的时间在速度测试的过程中可以忽略不计。
# cuda_test.py
import numpy as np
import time
from tqdm import trange
from numba import cuda
cuda.select_device(1)
@cuda.jit
def CudaSquare(x):
i, j = cuda.grid(2)
x[i][j] *= x[i][j]
if __name__ == '__main__':
numpy_time = 0
numba_time = 0
test_length = 1000
for i in trange(test_length):
np.random.seed(i)
array_length = 2**10
random_array = np.random.rand(array_length, array_length)
random_array_cuda = cuda.to_device(random_array)
time0 = time.time()
square_array = np.square(random_array)
time1 = time.time()
CudaSquare[(array_length,array_length),(1,1)](random_array_cuda)
time2 = time.time()
numpy_time += time1-time0
numba_time += time2-time1
print ('The time cost of numpy is {}s for {} loops'.format(numpy_time, test_length))
print ('The time cost of numba is {}s for {} loops'.format(numba_time, test_length))
在这个案例中,我们循环测试1000次的运行效果,测试对象是1024*1024大小的随机矩阵的平方算法。之所以需要这么多次数的测试,是因为numba的即时编译在第一次执行时会消耗一定的编译时间,但是编译完成后再调用,时间就会被大大的缩减。
$ python3 cuda_test.py
100%|██████████████████████████████████████| 1000/1000 [00:13<00:00, 76.83it/s]
The time cost of numpy is 1.4523804187774658s for 1000 loops
The time cost of numba is 0.46444034576416016s for 1000 loops
可以看到这个运行效果,我们自己的numba实现相比numpy的实现方案要快上2倍左右。但是我们需要有一个这样的概念,就是对于GPU来说,在显存允许的范围内,运算的矩阵维度越大,加速效果就越明显,因此我们再测试一个更大的矩阵:
# cuda_test.py
import numpy as np
import time
from tqdm import trange
from numba import cuda
cuda.select_device(1)
@cuda.jit
def CudaSquare(x):
i, j = cuda.grid(2)
x[i][j] *= x[i][j]
if __name__ == '__main__':
numpy_time = 0
numba_time = 0
test_length = 1000
for i in trange(test_length):
np.random.seed(i)
array_length = 2**12
random_array = np.random.rand(array_length, array_length)
random_array_cuda = cuda.to_device(random_array)
time0 = time.time()
square_array = np.square(random_array)
time1 = time.time()
CudaSquare[(array_length,array_length),(1,1)](random_array_cuda)
time2 = time.time()
numpy_time += time1-time0
numba_time += time2-time1
print ('The time cost of numpy is {}s for {} loops'.format(numpy_time, test_length))
print ('The time cost of numba is {}s for {} loops'.format(numba_time, test_length))
这里我们测试了一个4096*4096大小的矩阵的平方算法,可以看到最终的效果如下:
$ python3 cuda_test.py
100%|████████████████████████████████████████| 100/100 [00:22<00:00, 4.40it/s]
The time cost of numpy is 4.878739595413208s for 100 loops
The time cost of numba is 0.3255774974822998s for 100 loops
在100次的测试中,numba的实现比numpy的实现快了将近15倍!!!
最后,我们可以一起看下中间过程中显卡的使用情况:

因为本机上有2张显卡,日常使用第2张来跑计算任务,因此在代码中设置了cuda.select_device(1),也就是选择第2块显卡的意思。对于单显卡的用户,这个值应该设置为0.
总结概要
Numpy这个库在Python编程中非常的常用,不仅在性能上补足了Python语言的一些固有缺陷,还具有无与伦比的强大生态。但是即使都是使用Python,Numpy也未必就达到了性能的巅峰,对于我们自己日常中使用到的一些计算的场景,针对性的使用CUDA的功能来进行GPU的优化,是可以达到比Numpy更高的性能的。
版权声明
本文首发链接为:https://www.cnblogs.com/dechinphy/p/numba-cuda.html
作者ID:DechinPhy
更多原著文章请参考:https://www.cnblogs.com/dechinphy/
打赏专用链接:https://www.cnblogs.com/dechinphy/gallery/image/379634.html
腾讯云专栏同步:https://cloud.tencent.com/developer/column/91958
超过Numpy的速度有多难?试试Numba的GPU加速的更多相关文章
- java编写一个汽车类,有属性:品牌、型号、排量、速度,有方法:启动、加速、转弯、刹车、息火
/* * 汽车实体类 * 类里面有属性和方法 */public class Car { String brand; //汽车品牌 String modelNumber; //汽车型号 ...
- Python的GPU编程实例——近邻表计算
技术背景 GPU加速是现代工业各种场景中非常常用的一种技术,这得益于GPU计算的高度并行化.在Python中存在有多种GPU并行优化的解决方案,包括之前的博客中提到的cupy.pycuda和numba ...
- SIFT 特征点提取算法
SIFT特征点相对于ORB计算速度较慢,在没有GPU加速情况下,无法满足视觉里程计的实时性要求,或者无法运行在手机平台上,但是效果更好,精度更高.在应用时可以择优选取,了解其本质原理的动机是为了自己使 ...
- Python实现GPU加速的基本操作
技术背景 之前写过一篇讲述如何使用pycuda来在Python上写CUDA程序的博客.这个方案的特点在于完全遵循了CUDA程序的写法,只是支持了一些常用函数的接口,如果你需要自己写CUDA算子,那么就 ...
- 使用 Aircrack-ng 破解 WEP 和 WPA/WPA2 加密的 Wi-Fi 密码。(转)
1.首先请不要使用此方法去搞破坏,去蹭Wi-Fi,因为不装逼地说,我认为技术本身的价值很大,尤其是在学习这个技术的过程中解决遇到的问题,当经过重重困难最后终于成功之后的喜悦又怎么能拿去蹭网呢.我在此过 ...
- ipdb介绍及Tensor
ipdb介绍 1.现在IPython之外使用debug功能,则需要安装ipdb(pip install ipdb),而后在需要进入调试的地方加上如下代码即可: import ipdb ipdb.set ...
- Pytorch_01 Tensor,Autograd,构建网络
Tensor Tensor是PyTorch中的重要数据结构,可认为是一个高维数组,Tensor与numpy的ndarrays类似,但Tensor可以使用GPU加速 import torch as t# ...
- 关于Numba开源库(Python语法代码加速处理,看过一个例子,速度可提高6倍)
关于Numba你可能不了解的七个方面 https://yq.aliyun.com/articles/222523 Python GPU加速 (很详细,有代码练习)https://blog.csdn.n ...
- 适用于CUDA GPU的Numba 随机数生成
适用于CUDA GPU的Numba 随机数生成 随机数生成 Numba提供了可以在GPU上执行的随机数生成算法.由于NVIDIA如何实现cuRAND的技术问题,Numba的GPU随机数生成器并非基于c ...
随机推荐
- 一文搞懂一致性hash的原理和实现
在 go-zero 的分布式缓存系统分享里,Kevin 重点讲到过一致性hash的原理和分布式缓存中的实践.本文来详细讲讲一致性hash的原理和在 go-zero 中的实现. 以存储为例,在整个微服务 ...
- K8s基本概念资料
https://www.cnblogs.com/menkeyi/p/7134460.html
- Java基础之反射总结
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...
- vue(23)Vuex的5个核心概念
Vuex的核心概念 Vuex有5个核心概念,分别是State,Getters,mutations,Actions,Modules. State Vuex使用单一状态树,也就是说,用一个对象包含了所 ...
- UBUNTU 16.04 LTS SERVER 手动升级 MariaDB 到最新版 10.2
UBUNTU 16.04 LTS SERVER 手动升级 MariaDB 到最新版 10.2 1. 起因 最近因为不同软件的数据问题本来只是一些小事弄着弄着就越弄越麻烦了,期间有这么个需求,没看到有中 ...
- P5147-数学-随机数生成器
P5147-数学-随机数生成器 (洛谷第一篇题解说这是高一数学题,新高二感觉到被吊打) 我们设work(x)的期望值为\(f_x\) 注意\(f_1\)是边界.不过对下列式子没有影响.原因参照必修的数 ...
- 警告: Runner org.junit.internal.runners.ErrorReportingRunner (used on class cn.star.MybatisTest)
bug描述: 在Springboot整合Junit的时候编写测试类进行测试时, 出现以下错误: 十一月 28, 2019 2:53:48 下午 org.junit.vintage.engine.des ...
- 01_什么是BS结构,什么是CS结构?
C/S和B/S都是互联网中常见的网络结构模型. 一.什么是C/S模型? C是英文单词"Client"的首字母,即客户端的意思,C/S就是"Client/Server&qu ...
- LinuxMint 19/Ubuntu 19.10重置开始菜单以及任务栏
====================== 问题:任务栏以及开始菜单弄不见了 解决方法: 快捷键打开终端,输入重置命令: dconf reset -f /
- js中==和===的区别以及总结
js中==和===的区别以及总结 学习js时我们会遇到 == 和 === 两种符号,现做总结如下 两种符号的定义 "==" 叫做相等运算符 "===" 叫做严格 ...