CUDA Python 中支持的 Python 特性

本页列出了 CUDA Python 中支持的 Python 功能。这包括使用 @cuda.jit 编译的所有内核和设备函数,以及其他面向 CUDA GPU 的高级 Numba 装饰器。

语言

执行模型

CUDA Python 直接映射到 CUDA 的 单指令多线程 执行 (SIMT) 模型。每条指令都隐式地由多个线程并行执行。在这种执行模型下,数组表达式的作用较小,因为我们不希望多个线程执行相同的任务。相反,我们希望线程以协作的方式执行任务。

详情请参阅 CUDA 编程指南

浮点错误模型

默认情况下,CUDA Python 内核使用 NumPy 错误模型执行。在此模型中,除以零不会引发异常,而是产生 inf-infnan 的结果。这与正常的 Python 错误模型不同,在正常模型中,除以零会引发 ZeroDivisionError

当启用调试时(通过向 @cuda.jit 装饰器传递 debug=True),将使用 Python 错误模型。这允许在核函数执行期间识别除以零的错误。

构造

以下 Python 结构不受支持:

  • 异常处理 (try .. except, try .. finally)

  • 上下文管理(with 语句)

  • 推导式(列表、字典、集合或生成器推导式)

  • 生成器 (任何 yield 语句)

raiseassert 语句是被支持的,但有以下限制:

  • 它们只能在内核中使用,不能在设备函数中使用。

  • 只有在向 @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 文档)。