在使用CUDA库中的`cudaDeviceReset()`的bad_alloc之后无法恢复我的GPU

时间:2015-08-20 15:03:52

标签: cuda reset thrust

以下程序连续3次调用" test"执行某些推力操作的功能。这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,带有2GB专用VRAM的NVIDIA GeForce 820m,使用nvcc编译的CUDA 7.0,命令行是&#34; $ nvcc -arch = compute_20 test.cu -run&#34;),我得到了这个:

  • 第一次打电话,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()和{{1}清除了所有内容}}。

尽管有清洁说明,设备仍未恢复功能状态,我不明白为什么。

如果我做错了什么,在第一次失败后将GPU恢复到功能状态而不结束我的过程会是什么?

有人重现这个吗?

1 个答案:

答案 0 :(得分: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,它处理了类似的问题。答案也值得一读。