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