c++ - CUDA 流破坏和 CudaDeviceReset

标签 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();
}

一切正常。或许与类(class)的不当使用有关?

最佳答案

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

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

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

    cudaDeviceReset();

    return 0;
}

这可以正常工作,因为 streams 的析构函数只被调用一次(当 streams 超出范围时),之前 调用 cudaDeviceReset

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

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

    cudaDeviceReset();

    return 0;
}

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

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

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

    cudaDeviceReset();

    return 0;
}

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

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

关于c++ - CUDA 流破坏和 CudaDeviceReset,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16979982/

相关文章:

c - 为什么在查找倾斜内存地址的行时需要 char* 强制转换?

SoA/AoS 内存布局的 C++ 零成本抽象

c++ - 将 C++ 方法和变量并排绑定(bind)到一个表

c++ - 我应该使用单例进行硬件管理吗?

php - get_class_vars() 未显示变量,但在同一类上运行的 property_exists() 返回 true

java - 从另一个类访问变量

python - 如何在 Python 中激活(执行)类的方法?

lisp - CUDA 的任何 Lisp 扩展?

ubuntu - 为 Linux : Could NOT find CUDA 安装 CUDA

c++ - fabsf 是 C++11 中 std 命名空间的一部分吗?