专栏首页Dechin的专栏超过Numpy的速度有多难?试试Numba的GPU加速

超过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))

这个案例主要是通过numbacuda.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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python实现GPU加速的基本操作

    之前写过一篇讲述如何使用pycuda来在Python上写CUDA程序的博客。这个方案的特点在于完全遵循了CUDA程序的写法,只是支持了一些常用函数的接口,如果你...

    DechinPhy
  • Python | 加一行注释,让你的程序提速10+倍!numba十分钟上手指南

    之前的文章《源代码如何被计算机执行》已经提到计算机只能执行二进制的机器码,C、C++等编译型语言依靠编译器将源代码转化为可执行文件后才能运行,Python、Ja...

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

    在前面的几篇博客中我们介绍了在Python中使用Numba来写CUDA程序的一些基本操作和方法,并且展示了GPU加速的实际效果。在可并行化的算法中,比如计算两个...

    DechinPhy
  • numba,让你的Python飞起来!

    python由于它动态解释性语言的特性,跑起代码来相比java、c++要慢很多,尤其在做科学计算的时候,十亿百亿级别的运算,让python的这种劣势更加凸显。

    Python进阶者
  • 教你几个Python技巧,让你的循环和运算更高效!

    在 24式加速你的Python中介绍对循环的加速方法中,一个办法就是采用 Numba 加速,刚好最近看到一篇文章介绍了利用 Numba 加速 Python ,文...

    1480
  • [Python技巧]如何加快循环操作和Numpy数组运算速度

    在 24式加速你的Python中介绍对循环的加速方法中,一个办法就是采用 Numba 加速,刚好最近看到一篇文章介绍了利用 Numba 加速 Python ,文...

    kbsc13
  • 用Numba加速Python代码

    说这句话的人也没有错。与许多其他编程语言相比,Python很慢。Benchmark game有一些比较不同编程语言在不同任务上的速度的可靠的基准。

    AiTechYun
  • GPU加速02:超详细Python Cuda零基础入门教程,没有显卡也能学!

    Python是当前最流行的编程语言,被广泛应用在深度学习、金融建模、科学和工程计算上。作为一门解释型语言,它运行速度慢也常常被用户诟病。著名Python发行商A...

    PP鲁
  • GPU加速04:将CUDA应用于金融领域,使用Python Numba加速B-S期权估值模型

    本文为英伟达GPU计算加速系列的第四篇,主要基于前三篇文章的内容,以金融领域期权估值案例来进行实战练习。前三篇文章为:

    PP鲁
  • 使用Python写CUDA程序

    使用Python写CUDA程序有两种方式: * Numba * PyCUDA

    py3study
  • 用 Numba 加速 Python 代码,变得像 C++ 一样快

    注意: 这篇文章的 Jupyter Notebook 代码在我的 Github 上:SpeedUpYourAlgorithms-Numba

    昱良
  • Python高性能计算库——Numba

    摘要: 在计算能力为王的时代,具有高性能计算的库正在被广泛大家应用于处理大数据。例如:Numpy,本文介绍了一个新的Python库——Numba, 在计算性能方...

    IT派
  • nvidia-rapids︱cuDF与pandas一样的DataFrame库

    cuDF(https://github.com/rapidsai/cudf)是一个基于Python的GPU DataFrame库,用于处理数据,包括加载、连接、...

    素质
  • 7年程序员贡献出来的10大Python开源免费工具!

    毫无疑问,Python是最流行的语言之一,其成功的原因之一是它为科学计算提供了广泛的报道。 在这里,我们仔细研究用于机器学习和数据科学的十大Python工具。学...

    一墨编程学习
  • 如何在 GPU 上加速数据科学

    数据科学家需要算力。无论您是用 pandas 处理一个大数据集,还是用 Numpy 在一个大矩阵上运行一些计算,您都需要一台强大的机器,以便在合理的时间内完成这...

    AI研习社
  • 如何在 GPU 上加速数据科学

    我们认为使用大型模型架构和相同数据在XLNet 和BERT之间进行公平的比较研究具有重要的科学价值。

    AI科技评论
  • Python3实现打格点算法的GPU加速

    在数学和物理学领域,总是充满了各种连续的函数模型。而当我们用现代计算机的技术去处理这些问题的时候,事实上是无法直接处理连续模型的,绝大多数的情况下都要转化成一个...

    DechinPhy
  • 嫌Python太慢但又不想学C/C++?来了解下JIT技术

    Python 是门多才多艺的语言,既可以写后端,也可以做数据分析,既可以智能化运维,也可以搞渗透,既可以写爬虫,又可以做机器学习深度学习。然而,Python 的...

    实验盒
  • 使用numba加速python科学计算

    python作为一门编程语言,有非常大的生态优势,但是其执行效率一直被人诟病。纯粹的python代码跑起来速度会非常的缓慢,因此很多对性能要求比较高的pytho...

    DechinPhy

扫码关注云+社区

领取腾讯云代金券