Numba CUDA kernel 必须无返回值,输出全靠参数数组;输入输出数组需在设备端;禁止 kernel 内 return、打印、类型转换等主机操作;线程配置须覆盖全部数据且不越界,推荐 threads_per_block=256/512 并向上取整计算 blocks_per_grid,kernel 内必须加 tid < n 边界检查。

@cuda.jit 装饰器必须作用于无返回值函数
Numba CUDA kernel 不能用 return 返回结果,所有输出必须写入传入的数组参数中。这是硬性限制,不是风格建议——如果函数体里写了 return c 或类似语句,@cuda.jit 编译会直接失败,报错信息通常是 Kernel function must not return a value。
常见错误现象:
- 误把 CPU 版本函数直接加
@cuda.jit,保留return逻辑 - 在 kernel 内调用含
return的辅助函数(Numba 不支持 kernel 内函数调用,除非用@cuda.jit(device=True)显式声明)
正确做法:
立即学习“Python免费学习笔记(深入)”;
- 输入输出全部通过参数传递,例如
gpu_add(a, b, c),结果写进c - 确保所有数组已在设备端:用
cuda.to_device()拷贝,或用cuda.device_array()直接分配 - 不要在 kernel 里做类型转换、打印、异常抛出等主机端操作
线程配置不匹配数据长度会导致越界或漏算
blocks_per_grid 和 threads_per_block 的组合必须覆盖全部数据索引,但又不能让线程数远超数据量——否则大量线程空转,浪费资源;若配置不足,则部分元素根本不会被计算。
典型错误写法:
-
blocks_per_grid = n // 256(当n % 256 != 0时,尾部元素被跳过) -
blocks_per_grid = 1+threads_per_block = 1000000(超出 GPU 单块最大线程数,如 V100 是 1024,P100 是 2048)
安全写法(一维场景):
- 固定常用块大小:
threads_per_block = 256或512 - 向上取整算网格:
blocks_per_grid = (n + threads_per_block - 1) // threads_per_block - kernel 内始终加边界检查:
if tid ,其中 <code>tid = cuda.grid(1)
host/device 内存混用会静默失败或崩溃
Numba 不会自动把 NumPy 数组传给 GPU kernel——它只认设备端数组。如果你直接把 np.array 传给 @cuda.jit 函数,运行时会报错 CudaAPIError: [xxx] Call to cuLaunchKernel results in CUDA_ERROR_INVALID_VALUE,或者更隐蔽地:kernel 看似执行了,但输出全为零或随机值。
必须显式完成三步:
- 用
cuda.to_device(arr)把 host 数组拷到 device(返回cuda.CudaArray) - kernel 调用时传入 device 数组,例如
gpu_add[blocks_per_grid, threads_per_block](d_a, d_b, d_c) - 结果从 device 拷回 host:
c = d_c.copy_to_host()(不能用np.array(d_c))
容易忽略的细节:device 数组不支持切片赋值(如 d_c[100:] = ...),也不支持 .shape 外的多数 NumPy 方法;它的 dtype 必须是明确的(如 np.float32),np.float64 在某些旧卡上可能不被支持。
实际跑通的关键往往不在 kernel 逻辑本身,而在于这三步是否严格闭环——少一步,整个加速链就断了。


















