阅读1020 返回首页    go 阿里云 go 技术社区[云栖]


关于Numba你可能不了解的七个方面

更多深度文章,请关注:https://yq.aliyun.com/cloud


我最喜欢的事情之一是与人们谈论GPU计算和Python。 Python的生产力和互动性与GPU的高性能结合是科学和工程中许多问题的杀手。 有几种使用GPU加速Python的方法,但我最熟悉的是Numba,它是Python函数的即时编译器。 Numba在标准的Python翻译器中运行,因此您可以直接以Python语法编写CUDA内核,并在GPU上执行它们。 NVIDIA开发者博客最近推出了一篇Numba的介绍我建议阅读那篇文章,对GPU上的Numba有一个简要的了解

当我和人们谈论Numba时,我发现他们很快就在Python中编写了CUDA内核的基础知识。 但是我们经常没有时间使用Numba为GPU程序员提供的一些更先进的功能。 在这篇文章中,我想深入了解一下,并展示了在GPU上使用Numba的几个往往被忽视方面。我会快速讲述一些主题,但是我会提供链接以供阅读。

1.Numba

Pyculib是围绕标准CUDA算法的Python包装器的新名称。 它包括Python包装器,用于:


import pyculib.fft
import numba.cuda
import numpy as np
 
@numba.cuda.jit
def apply_mask(frame, mask):
    i, j = numba.cuda.grid(2)
    frame[i, j] *= mask[i, j]
 
# … skipping some array setup here: frame is a 720x1280 numpy array
 
out = np.empty_like(mask, dtype=np.complex64)
gpu_temp = numba.cuda.to_device(out)  # make GPU array
gpu_mask = numba.cuda.to_device(mask)  # make GPU array
 
pyculib.fft.fft(frame.astype(np.complex64), gpu_temp)  # implied host->device
apply_mask[blocks, tpb](gpu_temp, gpu_mask)  # all on device
pyculib.fft.ifft(gpu_temp, out)  # implied device->host

你可以在其文档中了解有关Pyculib功能的更多信息。 现在Pyculib是开源的,我们正在积极尝试扩展Pyculib,以包括其他CUDA库,如cuSOLVER和nvGRAPH。

2.Numba + Jupyter =

将Numba看作“用Python语法编写CUDA”是很容易的,但是Numba与Python数据科学生态系统中的其他工具的结合可以改变GPU计算的经验。 我们特别喜欢用Jupyter Notebook(和JupyterLab,下一代笔记本)使用Numba。 图1所示的Jupyter Notebook提供了一个基于浏览器的文档创建环境,允许将Markdown文本,可执行代码和图形和图像的图形输出相结合。Jupyter已经变得非常受欢迎,用于教学,记录科学分析和交互式原型。 事实上,这篇博文中的所有例子都是在Jupyter笔记本中创建的,你可以在这里找到。

6e4436c437c29e1c0cd609bec495c41989151944

  • Jupyter在其“魔术”命令集中包含了一个基准测试工具。通过用带有%timeit的行前缀,Jupyter会自动运行该命令多次,以准确测量运行时间。 (不要忘记在运行内核后同步设备以获得准确的时间!)


ssh -L 9999:localhost:9999 me@gpu-server.example.com


jupyter notebook --port 9999 --no-browser

3.Numba


__host__ __device__ float clamp(float x, float xmin, float xmax) {
    if (x < xmin){
        return xmin;
    } else if (x > xmin) {
        return xmax;
    } else {
        return x;
    }
}


@numba.jit
def clamp(x, xmin, xmax):
    if x < xmin:
        return xmin
    elif x > xmax:
        return xmax
    else:
        return x


@numba.cuda.jit
def clamp_array(x, xmin, xmax, out):
    # Assuming 1D array
    start = numba.cuda.grid(1)
    stride = numba.cuda.gridsize(1)
    for i in range(start, x.shape[0], stride):
        out[i] = clamp(x[i], xmin, xmax)  # call "CPU" function here

4.Numba


import numba
import math
import numpy as np
 
SQRT_TWOPI = np.float32(math.sqrt(2 * math.pi))
 
@numba.vectorize(['float32(float32, float32, float32)'], target='cuda')
def gaussian(x, x0, sigma):
    return math.exp(-((x - x0) / sigma)**2 / 2) / SQRT_TWOPI / sigma


x = np.linspace(-3, 3, 10000, dtype=np.float32)
g = gaussian(x, 0, 1)  # 1D result
 
x2d = x.reshape((100,100))
g2d = gaussian(x2d, 0, 1) # 2D result

要了解有关ufuncs和Numba的更多信息,请查看有关广播的NumPy文档和ufuncs上的Numba文档

5.Numba

要调用CUDA模拟器,你必须在启动Python应用程序之前将NUMBA_ENABLE_CUDASIM环境变量设置为1。 这将迫使所有内核通过解释器代码路径。 你可以在CUDA模拟器的Numba文档中找到更多信息。

当然,这不是Numba中唯一可用的CUDA调试选项。 Numba还允许使用标准的Python打印函数/语句从GPU(常量字符串和标量)进行有限的打印。 另外,你可以使用nvprof(CUDA命令行剖析器),NVIDIA Visual Profilercuda-memcheck来运行Numba应用程序。 传递debug = True到@ numba.cuda.jit装饰器将允许cuda-memcheck显示检测到的内存错误的Python源代码行号。

6.

用于Python的分布式计算系统(如DaskSpark Python API)通过分散许多工作人员的数据并将代码带到数据所在的位置来实现高性能。 这需要将代码序列化并通过网络进行传输的能力。 在Python中,分布式框架通常使用cloudpickle库(Python pickle模块的增强版),将对象(包括函数)转换为字节流。 这些字节可以从用户输入的功能的客户端发送到远程工作进程,并将它们转回到可执行功能中。

fc289e38c611dc35f0bd15893cb0f758c30135c0

这里有一个简短的例子,这里有一些启动本地Dask群集的代码,并使用dask.distributed futures API执行一个简单的CUDA内核:


@numba.cuda.jit
def gpu_cos(x, out):
    # Assuming 1D array
    start = numba.cuda.grid(1)
    stride = numba.cuda.gridsize(1)
    for i in range(start, x.shape[0], stride):
        out[i] = math.cos(x[i])
        
def do_cos(x):
    out = numba.cuda.device_array_like(x)
    gpu_cos[64, 64](x, out)
    return out.copy_to_host()
 
# check if works locally first
test_x = np.random.uniform(-10, 10, 1000).astype(np.float32)
result = do_cos(test_x)
 
# now try remote
from dask.distributed import Client
client = Client() # starts a local cluster
 
future = client.submit(do_cos, test_x)
gpu_result = future.result()

虽然此示例执行的工作量很小,但它显示了使用分布式系统的Numba的一般模式。 提交到集群的函数是一个常规的Python函数,它内部调用一个CUDA函数。 包装器函数提供了一个分配GPU内存并确定CUDA内核启动配置的地方,分布式框架无法实现。 当do_cos提交到群集时,cloudpickle还会检测到对gpu_cos函数的依赖性并将其序列化。 这确保do_cos具有在远程工作器上运行所需的一切。 通常在使用Dask时,我们倾向于更高级别的API来构建计算图,如dask.delayed,但是对于一些迭代算法,直接使用 future是最直接的方法。

7.Numba

在2017年的GTC 大会上,与H2OMapDBlazingDBGraphistryGunrock合作的Anaconda公司(Numba开发的主要赞助商)宣布成立GPU开放分析计划(简称“GOAI”)。我们都认识到需要在应用程序和库之间进行GPU数据交换,因为数据科学工作负载越来越需要多种工具的组合。GPU计算已经无处不在,所以我们不能再把GPU当作一个隐藏的实现细节。现在是更多应用程序和库公开允许直接在组件之间传递GPU内存的接口的时候了。想要对GOAI的深入了解,请查看GOAI项目中的NVIDIA Developer Blog文章。

团队成员自2017年3月起就一直在工作,最近还与Apache Arrow项目的Wes McKinney合作,共同创建了可以在应用程序和库之间共享的GPU DataFrame。 GPU DataFrame实现使用Arrow格式来表示GPU上的表格数据,我们希望将来将大部分实现直接转移到Arrow代码库中。作为该软件堆栈的一部分,Numba开发人员已经创建了PyGDF,这是一个用于使用Pandas API子集来操作GPU DataFrames的Python库。该库支持过滤,排序,列数学运算,缩减,加入,按组合运算,以及与其他进程零拷贝共享GPU DataFrames。为了实现这一点,PyGDF使用Numba to JIT编译CUDA内核以进行自定义分组,缩减和过滤操作。此外,PyGDF列可以传递给Numba CUDA函数,以执行不能表示为DataFrame操作的自定义转换。

到目前为止,GOAI已经取得了很大的进展,但为时尚早,我们还有更多的工作要做。 还有一些令人兴奋的未来! 要了解GOAI活动的信息,请加入GOAI Google Group

Numba

此外,如果你想提问或获取Numba的帮助,最好的地方是Numba Users Google Group


本文由北邮@爱可可-爱生活老师推荐,阿里云云栖社区组织翻译。

文章原标题《Seven Things You Might Not Know about Numba》,作者:Stanley SeibertAnaconda社区创新总监,曾是Mobi的首席数据科学家。译者:董昭男,审校:

文章为简译,更为详细的内容,请查看原文


最后更新:2017-10-09 22:35:03

  上一篇:go  负载均衡进阶:SLB常见问题解决方法
  下一篇:go  数据库DevOps:我们如何提供安全、稳定、高效的研发全自助数据库服务-iDB/DMS企业版