CUDA_LAUNCH_BLOCKING=1的作用
参考资料:
[CUDA开发文档]
今天在调试Pytorch代码的时候遇到了下面的报错,
RuntimeError: CUDA error: XXX [此处为各种cuda error]
CUDA kernel errors might be asynchronously reported at some other API call,so the stacktrace below might be incorrect.
For debugging consider passing CUDA_LAUNCH_BLOCKING=1.
理解一下这些话意思,就是在GPU上跑程序的时候抛出了报错:RuntimeError:CUDA error。但是由于代码运行时host和device的异步性,device抛出报错的时候host可能正在协助device干一些别的事情,这会导致打印的堆栈错误。
你可能觉得上面这个表述太难以理解,那我换一种更形式化的说法。
CPU是host,GPU是device。host通过Pytorch的一条语句(可能是模型的forward语句)调用了device。此时我们设身处地为host着想,模型前向在device上计算是需要时间的,如果host没有其他的活干,它自然就停在原地摸鱼,等待device返回结果,调用栈就停留在调用device的这一条语句上。此时CUDA那边发现不对啊,你CPU给我的数据没法正常计算,我只好抛出Runtime CUDA error异常,此时python程序就会打印正确的调用栈,最后通过定位能够发现是哪一条语句出现了错误(多半是device上的模型前向语句)。那么如果host有其他活干呢?此时device正在马不停蹄的计算矩阵乘法呢,host在这段时间里显然可以替device干一些其他力所能及的事情,这会大大提高host和device协同工作的效率。但如果此时device抛出异常,host又没有那么聪明,就只能打印自己正在干的其他事情的调用堆栈。
CUDA是这样运作的吗?我翻看了CUDA的官方文档,找到了这样一段话,证明了我的猜想:
理解一下,host和device是并发执行的,所谓并发执行就是在同一时间上各自完成不同的任务。该并发执行具有异步性,许多操作在device和host之间异步完成,比如kernel launches, memory copies within a single device's memory ... 如果把CUDA_LAUNCH_BLOCKING这个环境变量设置为1,1表示True,则会强制消除这种异步性。如果你想在jupyter notebook中使用,可以用下面的语句:
import os os.environ['CUDA_LAUNCH_BLOCKING'] = '1'
注意这里1是要变成字符串的,不然会报错。以上均为我个人的理解,如果有错误,还望指出。