python - 什么是 numba.cuda.local.array() 的有效替代方案,它们不像通过 to_device() 传递许多参数那么麻烦?
问题描述
cuda.local.array()
在与 numba.cuda.to_device() 相比,使用 numba.cuda.local.array() 对性能有何影响?简单快速排序算法的基准测试表明,使用to_device
传递预分配的数组可以提高约 2 倍的效率,但这需要更多的内存。
单独排序 2,000,000 行,每行包含 100 个元素的基准测试结果如下:
2000000 Elapsed (local: after compilation) = 4.839058876037598 Elapsed (device: after compilation) = 2.2948694229125977 out is sorted Elapsed (NumPy) = 4.541851282119751
虚拟示例使用to_device()
cuda.local.array()
如果你有一个有很多调用的复杂程序,等效to_device
版本可能开始看起来像这样并且变得相当麻烦:
def foo2(var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, out):
for i in range(len(var1)):
out[i] = foo(var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, out)
def foo3(var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, out):
idx = cuda.grid(1)
foo(var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, out[idx])
在真实的代码库中,可能有 3-4 级函数嵌套,跨越数十个函数和数百到数千行代码。这两种方法的替代方法是什么?
解决方案
备择方案
cuda.local.array()
以下是通过 单独传递参数的一些替代方法cuda.to_device()
:
- 分配一个串联的向量/矩阵(称为 eg
local_args
),它实际上代表了 15 个变量。这样做的缺点是需要不断地对其进行切片,并希望您不会意外使用来自不同“子变量”的索引,或者通过稍后添加新变量、更改大小等来破坏排序。 - 将操作拆分为按顺序调用的 Numba/CUDA 内核,或Numba
cuda.jit()
、CuPycupy.fuse()
调用和/或其他 CUDA 实现的组合。例如,如果您对一组向量进行操作,否则这些操作将在成对距离矩阵计算中重复多次(昂贵且冗余)(例如 10,000 2而不是 10,000 次),则考虑预先执行这些操作并传递它们in 作为参数(可以与 1. 或 3. 结合使用) - 我遇到的一个方便的替代方法是定义一个自定义 NumPy
dtype
,尽管这可能会导致NVCC 编译器出现问题(希望永久修复)。GitHub问题有一个示例如下:
import numpy as np
np_int = np.int32
np_float = np.float32
cuda_const_arrays_type = np.dtype([
('a1', (np_int,(7776, 13))),
('a2', (np_int,(7776, 2, 5))),
('a3', (np_int,(16494592))),
('a4', (np_int,13)),
('a5', (np_float,(22528, 64))),
('a6', (np_int,(522523, 64))),
('a7', (np_int,(32,5))),
('a8', (np_int,(66667))),
('a9', (np_int,(252, 64, 3, 2, 2, 2, 2, 2, 2, 13))),
('a10', (np_int,(7776)))
])
cuda_const_arrays = np.zeros(1, dtype=cuda_const_arrays_type)
for txt in cuda_const_arrays_type.names: # i.e. ("a1", "a2", ...)
cuda_const_arrays[0][txt] = np.loadtxt(open(txt+".csv", "rb"), delimiter=",", skiprows=1)
gpu_const_arrays = cuda.to_device(cuda_const_arrays[0])
@cuda.jit(device=True)
def cuda_doSomething(gpu_const_arrays,...):
gpu_const_arrays.a1
可以在Gitlab上找到来自同一用户的示例(确定删除该import keras as ks
行)。虽然这会导致以前的 Numba 版本出现零星错误,但它适用于numba 0.53.1
and cudatoolkit 11.2.2
,这表明“自定义 dtype”方法现在可能还可以。
为了防止将大量数据不必要地传递给堆栈跟踪中较低的函数,在这个 custom 中只传递参数的一个子集可能是合适的dtype
,但我不知道如何做到这一点。
其他一般有用的例子
当我们等待 CuPy 或 NumPy 对 Numba/CUDA 7 9 10 11的支持时,以下是我发现在编写 Numba/CUDA 脚本的工作流程中相关/有用的示例。
- 为什么 numba cuda 召回几次后运行缓慢?
- 从 Python Numba CUDA 内核调用的加速 FFT
- Numba Discourse:进一步优化代码,CUDA Jit?(Graham Markall 的好建议和例子)
- Cuda Optimize Jaro Distance(Graham Markall 实施的好例子和解释)
- Numba 卷积和用户在 NumPy、CuPy 和 Numba 中的实现
- 如何使用 numba 在 GPU 上推广快速矩阵乘法(扩展/纠正 Numba Docs matmul 示例)
其中一些示例非常好,因为您可以看到原始的、低效的方法以及如何对其进行修改以变得更加高效,类似于Numba 文档:CUDA: Matrix Multiplication example 并查看其他人如何处理数组分配和在 Numba 中传递参数/CUDA。
推荐阅读
- c# - Visual Studio 2017 - 无法在 PC 上使用模板 API 编译新创建的 Asp.Net core 2.0 项目?
- javascript - 动画 SVG 未正确加载
- android - 用于检查是否显示 SnackBar 的 Robolectric 测试?
- sql - 添加 CASE 函数时,工作中的 Oracle SQL 查询停止工作 - 给出“右括号缺失”错误
- java - JAva SOAP:试图以不正确的命名空间方式创建或更改对象
- java - 我可以在 JavaFx 的 AnchorPane 上画圆圈吗?
- excel - 基于多桩标准的合并和求和
- android - SharedPreference - 如何?
- oop - 覆盖接收类范围类型作为 Ada 中的参数的过程
- android - gradle build 安装错误