【问题标题】:Can't restore my GPU after bad_alloc with `cudaDeviceReset()` from the CUDA library使用 CUDA 库中的 `cudaDeviceReset()` 在 bad_alloc 后无法恢复我的 GPU
【发布时间】:2015-08-20 15:03:52
【问题描述】:

下面的程序对执行一些推力操作的“测试”函数进行了 3 次连续调用。这 3 个调用中的每一个都为问题提供了不同的大小:

  • 第一次通话3,000;
  • 第二次调用300,000,000;
  • 第三次呼叫又是 3,000。

第二次调用预计会失败,但如果我正确清理了 GPU 的状态,第三次调用应该会成功(与第一次一样)。不幸的是,它也失败了。此外,连续调用也会导致失败,直到我退出进程并重新开始。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include <cuda.h>
#include <thrust/system_error.h>
#include <thrust/device_vector.h>
#include <thrust/sort.h>
#include <thrust/execution_policy.h>

#define CUDA_CALL(x)do { if((x) != cudaSuccess) { return -11;}} while(0)

typedef typename thrust::device_vector<size_t>  tDevVecInt;
typedef typename thrust::device_vector<float>   tDevVecFlt;

struct modSim : public thrust::unary_function<int, int>  
{
    int szMat;
    int p;

    modSim(int in1, int in2)
    {
        this->p = in1;
        this->szMat = in2;
    }
    __host__ __device__ int operator()(const int &x) 
    {
        return (x/szMat)*p+(x%p);
    }
};

 int test(size_t szData)
{

    modSim moduloCol(3, 33);

    CUDA_CALL(cudaSetDevice(0));

    try
    {

        tDevVecFlt devRand(szData);
        tDevVecInt devIndices(szData);
        tDevVecFlt devData(szData);

        thrust::sequence(devRand.begin(), devRand.end());
        thrust::tabulate(devIndices.begin(), devIndices.end(), moduloCol); 
        thrust::sort_by_key(devIndices.begin(), devIndices.end(), devRand.begin()); 

    }
    catch(std::bad_alloc &e)
    {
        std::cout << e.what() << std::endl;
        CUDA_CALL(cudaDeviceReset());
        CUDA_CALL(cudaSetDevice(0));
        return -3;
    }
    catch(thrust::system_error &e)
    {
        std::cout << e.what() << std::endl;
        CUDA_CALL(cudaDeviceReset());
        CUDA_CALL(cudaSetDevice(0));
        return -2;
    }

    CUDA_CALL(cudaDeviceReset());
    return 0;   
}


int main(void)
{

    size_t n;
    int retVal;

    n = 3000;
    retVal = test(n);
    std::cout << retVal << std::endl;

    n = 300000000;
    retVal = test(n);
    std::cout << retVal << std::endl;

    n = 3000;
    retVal = test(n);
    std::cout << retVal << std::endl;


    return(0);
}

在我的设置中(Windows 8、NVIDIA GeForce 820m 和 2GB 专用 VRAM、使用 nvcc 编译的 CUDA 7.0,命令行是 "$nvcc -arch=compute_20 test.cu -run"),我得到这个:

  • 第一次调用 N = 3,000 成功;
  • 第二次调用 N = 300,000,000 失败,但 bad allocation: out of memory 除外;
  • 第三次调用 N = 3,000 失败并返回 thrust::system error : after cub_::DeviceRadixSort::SortPairs(1): out of memory

所以输出看起来像这样:

0
bad allocation: out of memory
-3
after cub_::DeviceRadixSort::SortPairs(1): out of memory
-2

如上所述,第三次调用不应失败,因为它与成功的第一次调用相同。

此失败似乎是上一次调用(发出bad alloc)的结果,但我在bad alloc 之后用cudaDeviceReset()cudaSetDevice() 清理了所有内容。

尽管有清洁说明,但设备并没有恢复到功能状态,我不明白为什么。

如果我做错了什么,在第一次失败后不结束我的进程的情况下,将 GPU 恢复到功能状态的正确方法是什么?

有人复制这个吗?

【问题讨论】:

  • 我不明白你为什么要在你的内部使用指向此类向量的指针testfunctions。尝试使用直接实例而不是带有实例的指针的代码。当您删除指向它们的指针时,可能没有调用向量析构函数
  • 感谢您的评论。是的,这是以前测试的残余,我在您的 cmets 之后编辑了我的帖子,不幸的是,行为保持不变。

标签: cuda reset thrust


【解决方案1】:

此行为已报告给 NVIDIA 问题列表。来自 NVIDIA 的人再现了这种行为,乍一看无法解释。

但是,他们为我提供了一种解决方法,我想与可能感兴趣的人分享。这个想法只是在检测到异常时添加对cudaGetLastError() 的调用,而不是(或者在我的情况下,之前)对cudaDeviceReset() 的调用。

catch(std::bad_alloc &e)
{
    std::cout << e.what() << std::endl;
    CUDA_CALL(cudaGetLastError());
    CUDA_CALL(cudaDeviceReset());
    CUDA_CALL(cudaSetDevice(0));
    return -3;
}

然后,经过进一步调查,他们发现这实际上不是cudaDeviceReset()函数的真正问题,并给了我以下解释:

cudaDeviceReset() 显式销毁并清理当前进程中与当前设备关联的所有资源。调用者有责任确保在调用此函数时该设备没有被进程中的任何其他主机线程访问。此外,Cuda 运行时调用的任何错误都会在内部注册,并且可以使用cudaPeekAtLastError()cudaGetLastError() 查看。第一个可以多次调用以读取相同的错误,而后者用于读取和清除错误。建议在使用cudaGetLastError() 进行后续 Cuda 运行时调用之前清除先前的错误。

然后从这一点开始,我发现了一个我以前没有接触过的讨论here,它处理了一个类似的问题。那里的答案也值得一读。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 2013-06-03
    • 2013-09-02
    • 2011-04-14
    • 1970-01-01
    • 2018-03-18
    • 2012-05-27
    相关资源
    最近更新 更多