我已经使用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();
}
一切正常。偷窥与某类的不良使用有关吗?
最佳答案
这里有两个问题,都与您的类和作用域的析构函数有关。
首先,让我们从可以正常运行的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
超出范围时在return语句中再次调用该析构函数。上下文被破坏后自动调用析构函数是segfault / exception的来源。 cudaStreamDestroy
调用尝试在没有有效CUDA上下文的情况下在流上工作。因此,解决方案是在没有上下文的情况下,没有任何使CUDA API调用超出范围(或显式调用其析构函数)的类。如果我们制作了第三个这样的版本:
int main( int argc, char** argv)
{
{
CudaStreams streams;
streams.InitStreams(1);
streams.~CudaStreams();
}
cudaDeviceReset();
return 0;
}
您将收到CUDA运行时错误。因为析构函数被调用两次。第一次(明确)它将起作用。第二个(隐式,超出范围)将产生运行时错误:您具有有效的上下文,但是现在正尝试销毁不存在的流。
作为最后的评论/问题:发布和显示原始问题中显示的代码的实际可编译版本有多困难?它实际上需要5条额外的行才能使其成为其他人可以实际编译并运行的适当复制实例。如果您不愿意付出类似的努力来提供有用的代码和信息,而这又使每个人的生活变得更加轻松,那么我期望别人努力回答基本上是调试问题的想法有点不合理。想一想。 [结束语]