我正在尝试编写一些代码以确保所有GPU活动(特别是所有正在运行的线程)都已停止。我需要这样做以卸载带有dlclose的模块,所以我需要确保所有线程都在主机和设备上停止。
根据CUDA documentation,cudaDeviceSynchronize:
阻止设备完成所有先前请求的任务...如果为此设备设置了cudaDeviceScheduleBlockingSync标志,则主机线程将阻塞,直到设备完成其工作。
但是,当我设置阻塞同步标志并调用cudaDeviceSynchronize时,会生成一个新的主机线程,该线程在cudaDeviceSynchronize返回后仍在运行。这与我想要达到的目标相反。
在示例程序中演示了此行为:
#include <iostream>
void initialiseDevice()
{
cudaError result = cudaSetDeviceFlags(cudaDeviceScheduleBlockingSync);
if (cudaSuccess == result)
std::cout << "Set device flags." << std::endl;
else
std::cout << "Could not set device flags. (" << result << ")"
<< std::endl;
}
void synchroniseDevice()
{
cudaError result = cudaDeviceSynchronize();
if (cudaSuccess == result)
std::cout << "Device synchronise returned success." << std::endl;
else
std::cout << "Device synchronise returned error. (" << result << ")"
<< std::endl;
}
int main()
{
initialiseDevice();
sleep(1);
synchroniseDevice(); // new thread is spawned here
sleep(1); // new thread is still running here!
return 0;
}
如果我用nvcc -g main.cu
编译这个程序,并在gdb中运行它,对info threads
的调用表明在cudaDeviceSynchronize返回后有两个线程正在运行。
在gdb中运行cudaDeviceSynchronise后输出信息线程:
(gdb) info threads
Id Target Id Frame
2 Thread 0x7ffff5b8b700 (LWP 28458) "a.out" 0x00007ffff75aa023 in select
() at ../sysdeps/unix/syscall-template.S:82
* 1 Thread 0x7ffff7fd4740 (LWP 28255) "a.out" main () at cuda_test.cu:30
有没有人可以帮助我理解为什么cudaDeviceSynchronize会产生一个新线程,以及为什么线程在调用返回后仍在运行?
有没有人能指出我正确的方向来帮助我找到一种方法来阻止所有设备和主机活动/线程完成?
答案 0 :(得分:1)
CUDA 4.2及更高版本具有中间工作线程,用于调解应用程序线程和操作系统之间的阻塞调用。我的测试表明,为应用程序使用的每个GPU创建一个线程(每个CUDA上下文一个?)。我怀疑这些工作线程被引入以使流事件回调的实现更容易(我认为这些线程可能会执行回调);虽然,我在这个技术原因上完全错了。
我真的希望NVIDIA提供一个环境变量来禁用这些中间线程。如果要将程序作为SCHED_FIFO运行,则会引入问题。在调用任何CUDA例程之前,必须确保转换到SCHED_FIFO。否则,在主线程为SCHED_FIFO时,在SCHED_FIFO转换之前生成的任何工作线程将被调度为常规线程。这导致优先级反转,其中主线程被阻塞,等待以较低优先级调度工作线程。在任何线程生成之前转换到SCHED_FIFO允许将来的线程继承父级的SCHED_FIFO策略和优先级。
至于你的问题的解决方案:你可以在你的应用程序的上下文中调用cudaDeviceReset()吗?这应该有希望重新初始化系统中的任何CUDA运行时状态并终止任何工作线程。否则,总会有pthread_cancel()(或Windows等价物),但这可能会使CUDA处于不一致状态。