多GPU的cudaDeviceReset

8

我目前正在使用一台配备了4张 Tesla T10 GPU 的GPU服务器。在测试内核时,我经常需要使用 ctrl-C 来终止进程,因此我在一个简单的设备查询代码末尾添加了几行代码。以下是代码:

#include <stdio.h>

 // Print device properties
 void printDevProp(cudaDeviceProp devProp)
{
    printf("Major revision number:         %d\n",  devProp.major);
    printf("Minor revision number:         %d\n",  devProp.minor);
    printf("Name:                          %s\n",  devProp.name);
    printf("Total global memory:           %u\n",  devProp.totalGlobalMem);
    printf("Total shared memory per block: %u\n",  devProp.sharedMemPerBlock);
    printf("Total registers per block:     %d\n",  devProp.regsPerBlock);
    printf("Warp size:                     %d\n",  devProp.warpSize);
    printf("Maximum memory pitch:          %u\n",  devProp.memPitch);
    printf("Maximum threads per block:     %d\n",  devProp.maxThreadsPerBlock);
    for (int i = 0; i < 3; ++i)
    printf("Maximum dimension %d of block:  %d\n", i, devProp.maxThreadsDim[i]);
    for (int i = 0; i < 3; ++i)
    printf("Maximum dimension %d of grid:   %d\n", i, devProp.maxGridSize[i]);
    printf("Clock rate:                    %d\n",  devProp.clockRate);
    printf("Total constant memory:         %u\n",  devProp.totalConstMem);
    printf("Texture alignment:             %u\n",  devProp.textureAlignment);
    printf("Concurrent copy and execution: %s\n",  (devProp.deviceOverlap ? "Yes" : "No"));
    printf("Number of multiprocessors:     %d\n",  devProp.multiProcessorCount);
    printf("Kernel execution timeout:      %s\n",  (devProp.kernelExecTimeoutEnabled ? "Yes" : "No"));
    return;
}

 int main()
{
    // Number of CUDA devices
    int devCount;
    cudaGetDeviceCount(&devCount);
    printf("CUDA Device Query...\n");
    printf("There are %d CUDA devices.\n", devCount);

    // Iterate through devices
    for (int i = 0; i < devCount; ++i)
    {
        // Get device properties
        printf("\nCUDA Device #%d\n", i);
        cudaDeviceProp devProp;
        cudaGetDeviceProperties(&devProp, i);
        printDevProp(devProp);
    }

    printf("\nPress any key to exit...");
    char c;
    scanf("%c", &c);

    **for (int i = 0; i < devCount; i++) {
        cudaSetDevice(i);
        cudaDeviceReset();
    }**

    return 0;
}

我的问题与主函数结束前的for循环有关,我在其中逐个设置每个设备,然后使用cudaResetDevice命令。虽然这段代码没有产生任何错误,但我感到奇怪的是我无法重置所有设备。相反,程序每次只重置默认设备即设备0。有人能告诉我应该怎么做才能重置4个设备中的每一个吗?
谢谢。
3个回答

5

看起来你可以在GPU程序中添加一个函数来捕获ctrl+c信号(SIGINT),并调用cudaDeviceReset()函数重置程序所使用的每个设备。

当捕获到SIGINT时调用函数的示例代码可以在这里找到:

https://dev59.com/NkbRa4cB1Zd3GeqPzlsH#482725

似乎对于你编写的每个GPU程序都包含这样的代码是一个好的实践,我也会这样做 :-)。
我没有时间撰写完整详细的答案,所以也请阅读其他答案及其评论。

3

这可能有点晚了,但如果您编写一个信号处理函数,您可以摆脱内存泄漏并以可靠的方式重置设备:

// State variables for 
extern int no_sigint;
int no_sigint = 1;
extern int interrupts;
int interrupts = 0;

/* Catches signal interrupts from Ctrl+c.
   If 1 signal is detected the simulation finishes the current frame and
   exits in a clean state. If Ctrl+c is pressed again it terminates the
   application without completing writes to files or calculations but
   deallocates all memory anyway. */
void
sigint_handler (int sig)
{
  if (sig == SIGINT)
    {
      interrupts += 1;
      std::cout << std::endl
                << "Aborting loop.. finishing frame."
                << std::endl;

      no_sigint = 0;

      if (interrupts >= 2)
        {
          std::cerr << std::endl
                    << "Multiple Interrupts issued: "
                    << "Clearing memory and Forcing immediate shutdown!"
                    << std::endl;

          // write a function to free dynamycally allocated memory
          free_mem ();

          int devCount;
          cudaGetDeviceCount (&devCount);

          for (int i = 0; i < devCount; ++i)
            {
              cudaSetDevice (i);
              cudaDeviceReset ();
            }
          exit (9);
        }
    }
}

....

int main(){ 
.....
for (int simulation_step=1 ; simulation_step < SIM_STEPS && no_sigint; ++simulation_step)
{
   .... simulation code
}
free_mem();
... cuda device resets
return 0;
}

如果您使用此代码(甚至可以在外部头文件中包含第一个片段,它有效),您可以控制ctrl+c的两个级别:第一次按下将停止您的模拟并正常退出,但应用程序完成呈现步骤,这对于优雅地停止和获得正确结果非常有用。如果再次按下ctrl+c,则关闭应用程序并释放所有内存。

1

cudaDeviceReset 旨在销毁与其运行的进程中给定 GPU 上下文相关联的资源。一个 CUDA 进程无法重置或以其他方式影响另一个进程的上下文。因此,当您修改的设备查询调用 cudaDeviceReset 时,它仅释放了它分配的资源,而不是任何其他进程正在使用的资源。


感谢您的及时回复。那么我应该如何逐个重置每个设备呢?为了获取devcount,我必须调用cudaGetDeviceCount。这会在默认设备0上启动一个上下文吗?您能否建议一种明确为每个GPU设备清除内存的方法?谢谢。 - Abhinav
在CUDA 4.0中,在执行任何其他操作之前,您需要调用cudaSetDevice。但我要重申的是,cudaDeviceReset仅会重置程序执行过的操作。如果由于其他进程的原因,GPU处于“不良”状态,则无法重置或以其他方式影响该状态。驱动程序中实现了彼此之间的基本上下文保护,明确防止了这种情况的发生。 - talonmies
1
那么,如果我需要使用ctrl-C在中途终止内核,然后如果我使用上面提到的程序以清除资源为目的,那么你的意思是说这将是徒劳的,并且由于突然终止而未被清除的全局设备内存将不会被清理? - Abhinav
请问您能否针对这个问题提供一个解决方案?因为在开发阶段,我肯定会使用ctrl-C来有意中断我的内核。目前,我只能想到重新启动机器作为一种确定的解决方案,尽管这非常令人烦恼。还要非常感谢您的及时回复。 - Abhinav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接