1

我已经使用 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

其中 line是在析构函数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 是否与班级使用不当有关?

4

1 回答 1

7

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

首先,让我们从main()可以正常工作的版本开始:

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

    cudaDeviceReset();

    return 0;
}

这可以正常工作,因为析构函数 forstreams只被调用一次(当streams超出范围时),并且before cudaDeviceReset被调用。

您的原始main()版本(或它的可编译版本,但稍后会详细介绍...)失败的原因有两个。让我们再看一遍:

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

    cudaDeviceReset();

    return 0;
}

在这里,您显式调用析构函数streams(您几乎不应该这样做),然后,当超出范围时,在 return 语句处再次cudaDeviceReset调用析构函数。上下文销毁后自动调用析构函数是段错误/异常的来源。这些调用试图在没有有效 CUDA 上下文的流上工作。因此,解决方案是在没有上下文的情况下,不要让任何使 CUDA API 调用超出范围(或显式调用其析构函数)的类。streamscudaStreamDestroy

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

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

    cudaDeviceReset();

    return 0;
}

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

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

于 2013-06-07T11:00:10.163 回答