连续异步内核启动如何避免Cuda错误6(启动超时)?

时间:2015-01-14 13:46:09

标签: cuda timeout

我使用此(简化)代码得到了Cuda错误6(也称为cudaErrorLaunchTimeoutCUDA_ERROR_LAUNCH_TIMEOUT):

for(int i = 0; i < 650; ++i)
{
    int param = foo(i); //some CPU computation here, but no memory copy
    MyKernel<<<dimGrid, dimBlock>>>(&data, param);
}

Cuda错误6表明内核花了太多时间返回。单个MyKernel的持续时间仅为~60 ms。块大小是经典的16×16。

现在,当我每次拨打cudaDeviceSynchronize(),比如50次迭代时,就不会发生错误:

for(int i = 0; i < 650; ++i)
{
    int param = foo(i); //some CPU computation here, but no memory copy
    MyKernel<<<dimGrid, dimBlock>>>(&data, param);
    if(i % 50 == 0) cudaDeviceSynchronize();
}

我想避免这种同步,因为它会大大减慢程序的速度。

由于内核启动是异步的,我猜错是因为看门狗从异步启动中测量内核的执行持续时间,而不是从实际开始执行。

我是Cuda的新手。这是错误6发生的常见情况吗?有没有办法在不改变性能的情况下避免这种错误?

2 个答案:

答案 0 :(得分:3)

看门狗本身并不测量内核的执行时间。监视程序正在跟踪发送到GPU的命令队列中的请求,并确定GPU是否在超时期限内没有确认它们中的任何一个。

正如@talonmies在评论中指出的那样,我最好的猜测是(如果您确定没有内核执行超过超时时间),这种行为是由于CUDA驱动程序WDDM批处理机制,它试图通过批处理来减少平均延迟GPU一起命令并分批发送到GPU。

您无法直接控制批处理行为,因此通常,尝试在不禁用或修改Windows TDR机制的情况下解决此问题将是一项不精确的练习。

对于命令队列的低成本“刷新”的一般(有些未记录的)建议,您可以尝试进行试验,是使用cudaEventQuery(0);(如建议here)代替cudaDeviceSynchronize();,也许每50个内核启动一次。在某种程度上,具体细节可能取决于机器配置和正在使用的GPU。

我不确定你的情况会有多么有效。我认为,如果没有更多的实验,它可以作为避免TDR事件的“保证”。您的里程可能会有所不同。

答案 1 :(得分:3)

感谢talonmies和Robert Crovella(他提议的解决方案对我不起作用),我能够找到一个可接受的解决方法。

为了防止CUDA驱动程序一起批量启动内核,必须在每次内核启动之前或之后执行另一个操作。例如。虚拟副本可以解决问题:

void* dummy;
cudaMalloc(&dummy, 1);

for(int i = 0; i < 650; ++i)
{
    int param = foo(i); //some CPU computation here, but no memory copy
    cudaMemcpyAsync(dummy, dummy, 1, cudaMemcpyDeviceToDevice);
    MyKernel<<<dimGrid, dimBlock>>>(&data, param);
}

此解决方案比包含cudaDeviceSynchronize()调用的解决方案快8秒(50秒到42秒)(请参阅问题)。 此外,它更可靠,50是一个任意的,特定于设备的时期。