CUDA Python 中支持的 Python 特性
本页列出了 CUDA Python 中支持的 Python 功能。这包括使用 @cuda.jit 编译的所有内核和设备函数,以及其他面向 CUDA GPU 的高级 Numba 装饰器。
语言
执行模型
CUDA Python 直接映射到 CUDA 的 单指令多线程 执行 (SIMT) 模型。每条指令都隐式地由多个线程并行执行。在这种执行模型下,数组表达式的作用较小,因为我们不希望多个线程执行相同的任务。相反,我们希望线程以协作的方式执行任务。
详情请参阅 CUDA 编程指南。
浮点错误模型
默认情况下,CUDA Python 内核使用 NumPy 错误模型执行。在此模型中,除以零不会引发异常,而是产生 inf、-inf 或 nan 的结果。这与正常的 Python 错误模型不同,在正常模型中,除以零会引发 ZeroDivisionError。
当启用调试时(通过向 @cuda.jit 装饰器传递 debug=True),将使用 Python 错误模型。这允许在核函数执行期间识别除以零的错误。
构造
以下 Python 结构不受支持:
异常处理 (
try .. except,try .. finally)上下文管理(
with语句)推导式(列表、字典、集合或生成器推导式)
生成器 (任何
yield语句)
raise 和 assert 语句是被支持的,但有以下限制:
它们只能在内核中使用,不能在设备函数中使用。
只有在向
@cuda.jit装饰器传递debug=True时,它们才会生效。这与 CUDA C/C++ 中的assert关键字的行为类似,除非在启用设备调试的情况下编译,否则该关键字将被忽略。
支持字符串、整数和浮点数的打印,但打印是一个异步操作——为了确保在内核启动后所有输出都被打印,需要调用 numba.cuda.synchronize()。省略对 synchronize 的调用是可以接受的,但内核的输出可能会在其他后续驱动操作期间出现(例如后续内核启动、内存传输等),或者在程序执行完成之前未能出现。最多可以向 print 函数传递 32 个参数——如果传递的参数更多,则会发出格式字符串并生成警告。这是由于 CUDA 打印中的一般限制,如 CUDA C++ 编程指南中 关于打印限制的章节 所述。
递归
支持自递归设备函数,但递归调用的参数类型必须与函数初始调用的参数类型相同。例如,以下形式的递归是支持的:
@cuda.jit("int64(int64)", device=True)
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
(fib 函数总是有一个 int64 参数), 而以下是不支持的:
# Called with x := int64, y := float64
@cuda.jit
def type_change_self(x, y):
if x > 1 and y > 0:
return x + type_change_self(x - y, y)
else:
return y
外部调用 type_change_self 提供了 (int64, float64) 参数,但内部调用使用了 (float64, float64) 参数(因为 x - y / int64 - float64 结果是一个 float64 类型)。因此,此函数不受支持。
函数之间的相互递归(例如,函数 func1() 调用 func2(),而 func2() 又调用 func1())是不支持的。
备注
CUDA 中的调用堆栈大小通常非常有限,因此在 CUDA 设备上通过递归调用更容易使其溢出,相比之下在 CPU 上则不容易。
栈溢出将导致内核执行期间发生未指定的启动失败(ULF)。为了确定ULF是否由栈溢出引起,可以在 Compute Sanitizer 下运行程序,它会明确指出何时发生了栈溢出。
内置类型
以下内置类型的支持继承自CPU nopython模式。
整数
浮动
复杂
布尔
无
元组
Enum, IntEnum
请参阅 nopython 内置类型。
对于NumPy数组中使用的字符序列(字节和Unicode字符串),也有一些非常有限的支持。请注意,这种支持只能用于CUDA 11.2及更高版本。
内置函数
支持以下内置函数:
标准库模块
cmath
以下是 cmath 模块中支持的函数:
math
以下是 math 模块中支持的函数:
operator
以下 operator 模块中的函数是被支持的:
NumPy 支持
由于CUDA编程模型,内核内部的动态内存分配是低效的,通常并不需要。Numba禁止任何内存分配功能。这禁用了大量的NumPy API。为了获得最佳性能,用户应编写代码,使得每个线程一次只处理一个元素。
支持的 NumPy 功能:
访问 ndarray 属性 .shape、.strides、.ndim、.size 等。
索引和切片功能正常。
支持ufuncs的一个子集,但输出数组必须作为位置参数传入(参见 调用 NumPy UFunc)。请注意,ufuncs在每个线程中顺序执行——输入数组的元素之间没有自动的ufuncs并行化。
以下ufuncs受支持:
numpy.sin()numpy.cos()numpy.tan()numpy.arcsin()numpy.arccos()numpy.arctan()numpy.arctan2()numpy.hypot()numpy.sinh()numpy.cosh()numpy.tanh()numpy.arcsinh()numpy.arccosh()numpy.arctanh()numpy.deg2rad()numpy.radians()numpy.rad2deg()numpy.degrees()numpy.greater()numpy.greater_equal()numpy.less()numpy.less_equal()numpy.not_equal()numpy.equal()numpy.log()numpy.log2()numpy.log10()numpy.logical_and()numpy.logical_or()numpy.logical_xor()numpy.logical_not()numpy.maximum()numpy.minimum()numpy.fmax()numpy.fmin()numpy.bitwise_and()numpy.bitwise_or()numpy.bitwise_xor()numpy.invert()numpy.bitwise_not()numpy.left_shift()numpy.right_shift()
不支持的 NumPy 功能:
数组创建API。
数组方法。
返回新数组的函数。
CFFI 支持
cffi.FFI 对象的 from_buffer() 方法是被支持的。这对于获取可以传递给外部 C / C++ / PTX 函数的指针非常有用(参见 CUDA FFI 文档)。