CUDA流销毁和CudaDeviceReset

时间:2013-06-07 08:52:31

标签: c++ class cuda gpgpu nvidia

我使用CUDA流实现了以下类

class CudaStreams
{
    private:
        int             nStreams_;
        cudaStream_t*   streams_;
        cudaStream_t    active_stream_;

    public:

        // default constructor
        CudaStreams() { }

        // streams initialization
        void InitStreams(const int nStreams = 1) {
            nStreams_ = nStreams;
            // allocate and initialize an array of stream handles
            streams_ = (cudaStream_t*) malloc(nStreams_*sizeof(cudaStream_t));
            for(int i = 0; i < nStreams_; i++) CudaSafeCall(cudaStreamCreate(&(streams_[i]))); 

            active_stream_ = streams_[0];}

        // default destructor
        ~CudaStreams() {     
            for(int i = 0; i<nStreams_; i++) CudaSafeCall(cudaStreamDestroy(streams_[i])); }

}; 

如果我现在运行这个简单的代码

void main( int argc, char** argv) 
{
    streams.InitStreams(1);
    streams.~CudaStreams();

    cudaDeviceReset();
}

cudaDeviceReset()电话之后,我收到以下消息:

test.exe中未处理的异常0x772f15de:0x00000000。

在使用cudaDeviceReset()时,在调用析构函数以避免此问题之前,我应该怎么做?

修改

如果我在析构函数中添加free(streams_);,即

~CudaStreams() {     
    for(int i = 0; i<nStreams_; i++) CudaSafeCall(cudaStreamDestroy(streams_[i])); // * 
    free(streams_); }

我收到以下错误消息

cudaSafeCall() failed at C:\Users\Documents\Project\Library\CudaStreams.cuh:79 : unknown error

其中行79是析构函数中*表示的行。

此外,如果我在代码中直接使用构造函数和析构函数的相同指令,即

void main( int argc, char** argv) 
{
    int nStreams_ = 3;
    cudaStream_t* streams_ = (cudaStream_t*) malloc(nStreams_*sizeof(cudaStream_t));
    for(int i = 0; i < nStreams_; i++) CudaSafeCall(cudaStreamCreate(&(streams_[i]))); 
    for(int i = 0; i<nStreams_; i++) CudaSafeCall(cudaStreamDestroy(streams_[i])); 
    free(streams_);

cudaDeviceReset();
}
一切顺利。 Perheps与课堂上的错误使用有关吗?

1 个答案:

答案 0 :(得分:7)

这里有两个问题,都与你的类和范围的析构函数有关。

首先,让我们从您的main()版本开始,它将正常运行:

int main( int argc, char** argv) 
{
    {
        CudaStreams streams;
        streams.InitStreams(1);
    }

    cudaDeviceReset();

    return 0;
}

这是正常的,因为streams的析构函数只调用一次(当streams超出范围时),而 cudaDeviceReset调用之前。 / p>

您原来的main()(或其可编辑版本,但后来有更多内容......)因两个原因而失败。让我们再看一遍:

int main( int argc, char** argv) 
{
    CudaStreams streams;
    streams.InitStreams(1);
    streams.~CudaStreams();

    cudaDeviceReset();

    return 0;
}

在这里显式调用streams的析构函数(你几乎不应该这样做),然后是cudaDeviceReset,然后在返回语句时,析构函数再次被称为 { {1}}超出范围。在销毁上下文后自动调用析构函数是段错误/异常的来源。 streams调用正在尝试在没有有效CUDA上下文的情况下处理流。因此,当没有上下文时,解决方案不是让任何使CUDA API调用超出范围(或显式调用其析构函数)的类。

如果我们制作了这样的第三个版本:

cudaStreamDestroy

您将收到CUDA运行时错误。因为析构函数会调用两次。第一次(显式)它将起作用。第二个(隐含,超出范围)将产生运行时错误:您有一个有效的上下文,但现在正在尝试销毁不存在的流。

作为最终评论/问题:您在原始问题中显示的代码和实际可编辑版本的发布有多难?它确实需要5个额外的行来使它成为一个其他人可以实际编译和运行的适当的repro案例。我觉得如果你不愿意在提供有用的代码和信息方面做出类似的努力来让每个人的生活变得更加容易,那么期望其他人努力回答基本上调试问题是有点不合理的。想一想。 [咆哮结束]