c – CUDA流销毁和CudaDeviceReset

我使用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与课程的不良使用有关吗?

解决方法:

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

首先,让我们从main()的一个版本开始,它将正常工作:

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

    cudaDeviceReset();

    return 0;
}

这是正确的,因为流的析构函数只调用一次(当流超出范围时),并且在调用cudaDeviceReset之前.

你原来的main()(或者它的可编译版本,但后来有更多关于它……)失败的原因有两个.让我们再看一遍:

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

    cudaDeviceReset();

    return 0;
}

在这里你显式地调用了析构函数的流(你几乎不应该这样做),然后是cudaDeviceReset,然后当流超出范围时,在return语句中再次调用析构函数.在销毁上下文后自动调用析构函数是段错误/异常的来源. cudaStreamDestroy调用正在尝试在没有有效CUDA上下文的流上工作.所以解决方案是没有任何类在没有上下文时使CUDA API调用超出范围(或显式调用它们的析构函数).

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

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

    cudaDeviceReset();

    return 0;
}

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

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

上一篇:c-从命令行编译CUDA代码


下一篇:Java Encog 3中的OpenCL后端