首页 > 解决方案 > 需要帮助了解 GPU 上的内核传输速度(numba、cupy、cuda)

问题描述

虽然 GPU 加速了数学计算,但将内核移到 GPU 上执行的固定开销很高。

我正在使用cupy和numba。我第一次执行使用cupy的GPU版本的numpy的函数调用时非常慢。但是第二次很快。

我意识到我不明白内核或 GPU 代码是如何通过 GPU 运行的。从操作上讲,我想更好地理解这一点,以便我可以知道我所做的事情何时会由于某些内核传输而意外创建一个缓慢的步骤。所以我需要一些规则或经验法则来理解这个概念。

例如,如果我将已经存储在 GPU 上的两个 Cupy 数组相乘,我可能会写成 C= A*B

在某些时候,*乘法的cupy重载必须在GPU上编码出来,并且它自动需要也会被循环包裹,将它分解成块和线程。所以大概这段代码是一些内核,它被传输到 GPU 上。我猜下次我调用 C*D 时,GPU 不再需要学习 * 是什么意思,所以它会很快。

但是在某些时候,我会想象 GPU 需要清除旧代码,因此 * 或当时未使用的其他操作可能会从内存中刷新,因此稍后当再次调用 A*B 时,将会出现及时惩罚以在 GPU 上重新编译它。

或者我想象的那样。如果我是对的,我怎么知道这些内核何时会留下或消失?

如果我错了,这不是它的工作方式,或者还有其他一些缓慢的步骤(我假设数据已经传输到 GPU 上的数组),那么这个缓慢的步骤是什么以及如何组织事情所以一个人付费尽可能少吗?

我试图避免像在 cuda++ 中那样编写显式的 numba 线程管理内核,但只使用标准的 numba @njit、@vectorize、@stencil 装饰器。同样在 Cupy 中,我只想在 numpy 语法级别上工作,而不是深入线程管理。

我已经阅读了很多关于此的文档,但它们只是指内核的开销,而不是当这些得到报酬时以及如何控制它,所以我很困惑。

标签: numpygpunumbacupy

解决方案


我还没有一个完整的答案。但到目前为止,我得到的最大线索来自阅读当前未记录的函数@cupy.fuse(),这比@numba.jit支付内核启动成本的文档更清楚。我还没有找到@talonmies 推荐的与上下文的连接。

https://gist.github.com/unnonouno/877f314870d1e3a2f3f45d84de78d56c

关键的例子是这个

c = cupy.arange(4)
#@cupy.fuse()
def foo(x):
    return x+x+x+x+x

foo(.) 将在 @cupy.fuse() 注释掉的情况下慢三倍,因为每个“+”都涉及内核加载和内核空闲。Fusion 将所有添加内容合并到一个内核中,因此启动和免费内容只需支付一次。对于典型的 2018 GPU 上大小小于 100 万的矩阵,add() 非常快,以至于启动和免费是主要时间。

我希望我能在@fuse 上找到一些文档。例如,它是否像 @jit 那样展开内部函数。我可以通过堆叠@jit 和@fuse 来实现吗?

然而,我仍然很大程度上不知道何时以 numba 支付费用。


推荐阅读