调试#
打印值#
通常,最好的调试方法之一是简单地从内核中打印值。Warp 支持使用 print()
函数打印所有内置类型,例如:
v = wp.vec3(1.0, 2.0, 3.0)
print(v)
[1.0, 2.0, 3.0]
此外,对于标量类型,可以通过 wp.printf()
函数进行格式化的 C 风格打印,例如:
@wp.kernel
def mykernel():
x = 1.0
i = 2
wp.printf("A float value %f, an int value: %d\n", x, i)
详细模式和打印启动#
对于复杂的应用程序,可能很难理解导致 bug 的操作顺序。为了帮助诊断这些问题,Warp 支持一个简单的选项,可以将所有启动和参数打印到控制台
wp.config.print_launches = True
详细模式也可以通过以下方式启用:
wp.config.verbose = True
在详细模式下,关于程序进度和代码生成(例如操作何时可能不可微)的附加消息将被打印到标准输出。
详细警告可以通过以下方式启用:
wp.config.verbose_warnings = True
这有助于识别特定的 Warp UserWarning
消息是从哪里发出的。
调试模式编译#
在调试模式下,Warp 内核将执行以下附加检查:
如果存在超出定义形状的数组访问,则引发断言。
如果
wp.tid()
在大网格上返回溢出值,则发出警告。(仅限 GPU) 如果 CUDA 网格维度因块数溢出而被限制,则发出警告。
(仅限 GPU) 为设备代码生成行号信息。
在调试模式下启用 Warp 内核编译的最简单方法是设置:
wp.config.mode = "debug"
作为前述全局设置的替代方案,可以通过按模块设置以下内容来开启调试模式:
wp.set_module_options({"mode": "debug"})
断言#
assert
语句可以插入到 Warp 内核和用户定义函数中,以便在提供的布尔表达式评估为假时中断程序执行。断言仅在模块以调试模式编译时对其内核有效。
以下示例将在运行内核时引发断言,因为该模块是以调试模式编译的,并且 assert
语句期望传递给 expect_ones
内核的数组是一个全一数组,但我们传递了一个单元素全零数组:
import warp as wp
wp.config.mode = "debug"
@wp.kernel
def expect_ones(a: wp.array(dtype=int)):
i = wp.tid()
assert a[i] == 1, "Array element must be 1"
input_array = wp.zeros(1, dtype=int)
wp.launch(expect_ones, input_array.shape, inputs=[input_array])
wp.synchronize_device()
程序的输出将包含如下一行语句:
default_program:49: void expect_ones_133f9859_cuda_kernel_forward(wp::launch_bounds_t, wp::array_t<int>): block: [0,0,0], thread: [0,0,0] Assertion `("assert a[i] == 1, \"Array element must be 1\"",var_3)` failed.
单步调试#
可以将 Visual Studio 等 IDE 调试器附加到 Warp 进程,以便单步执行生成的内核代码。用户应首先通过设置以下内容在调试模式下编译内核:
wp.config.mode = "debug"
此设置确保正确生成行号和调试符号。启动 Python 进程后,应附加调试器并在生成的代码中插入断点。
注意
生成的内核代码与原始 Python 代码并非一一对应,但仍然可以重播单个操作并检查变量。
另请参阅 warp/tests/walkthrough_debug.py,了解如何在 CPU 上调试 Warp 内核代码的示例。
生成的代码#
有时,检查生成的代码以进行调试或性能分析可能会很有用。默认情况下,内核生成的代码存储在用户主目录的中心缓存位置。调用 wp.init()
时会在启动时打印缓存位置,例如:
Warp 0.8.1 initialized:
CUDA Toolkit: 11.8, Driver: 11.8
Devices:
"cpu" | AMD64 Family 25 Model 33 Stepping 0, AuthenticAMD
"cuda:0" | NVIDIA GeForce RTX 3090 (sm_86)
"cuda:1" | NVIDIA GeForce RTX 2080 Ti (sm_75)
Kernel cache: C:\Users\LukasW\AppData\Local\NVIDIA Corporation\warp\Cache\0.8.1
内核缓存包含以 wp_
开头的文件夹,其中包含生成的 C++/CUDA 代码以及运行时编译的每个模块的编译二进制文件。每个文件夹的名称都以由模块内容构建的十六进制哈希值结尾,以避免在使用多个进程时发生潜在冲突,并支持运行时定义内核的缓存。
如果怀疑 Warp 的内核缓存逻辑存在 bug,可以通过设置以下内容来禁用内核缓存:
wp.config.cache_kernels = True
CUDA 错误验证#
通过格式不正确的内核代码或输入,可能会产生越界内存访问违规。在这种情况下,CUDA 运行时将检测到违规并将 CUDA 上下文置于错误状态。随后的内核启动可能会静默失败,这可能导致难以诊断的问题。
如果怀疑存在 CUDA 错误,一个简单的验证方法是启用:
wp.config.verify_cuda = True
此设置将在每次 wp.launch()
后检查 CUDA 上下文,以确保其仍然有效。如果遇到错误,将引发异常,这通常有助于缩小问题内核的范围。
在捕获 CUDA 图时,不能使用 CUDA 错误验证。
注意
每次启动时验证 CUDA 状态需要同步 CPU 和 GPU,这会产生显着的开销。用户应确保仅在调试期间使用此设置。
检测非有限值#
wp.config.verify_fp = True
有助于识别计算何时产生非有限值,例如 NaN 或无穷大。单独使用此标志时,消息将被打印到标准输出流,指示正在检测无效值的函数。
如果与调试模式编译结合使用,则在检测到无效值时将引发断言。
CUDA 工具包调试工具#
Compute Sanitizer 工具(如 initcheck 和 memcheck)也可用于检测 Warp 应用程序中细微的内存访问问题,例如:
compute-sanitizer --tool initcheck python sim.py
Compute Sanitizer 套件可通过 CUDA 工具包获得。