我使用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与课堂上的错误使用有关吗?
答案 0 :(得分:7)
这里有两个问题,都与你的类和范围的析构函数有关。
首先,让我们从您的main()
版本开始,它将正常运行:
int main( int argc, char** argv)
{
{
CudaStreams streams;
streams.InitStreams(1);
}
cudaDeviceReset();
return 0;
}
这是正常的,因为 您原来的 在这里显式调用 如果我们制作了这样的第三个版本: 您将收到CUDA运行时错误。因为析构函数会调用两次。第一次(显式)它将起作用。第二个(隐含,超出范围)将产生运行时错误:您有一个有效的上下文,但现在正在尝试销毁不存在的流。 作为最终评论/问题:您在原始问题中显示的代码和实际可编辑版本的发布有多难?它确实需要5个额外的行来使它成为一个其他人可以实际编译和运行的适当的repro案例。我觉得如果你不愿意在提供有用的代码和信息方面做出类似的努力来让每个人的生活变得更加容易,那么期望其他人努力回答基本上调试问题是有点不合理的。想一想。 [咆哮结束] streams
的析构函数只调用一次(当streams
超出范围时),而 cudaDeviceReset
调用之前。 / p>
main()
(或其可编辑版本,但后来有更多内容......)因两个原因而失败。让我们再看一遍:int main( int argc, char** argv)
{
CudaStreams streams;
streams.InitStreams(1);
streams.~CudaStreams();
cudaDeviceReset();
return 0;
}
streams
的析构函数(你几乎不应该这样做),然后是cudaDeviceReset
,然后在返回语句时,析构函数再次被称为 { {1}}超出范围。在销毁上下文后自动调用析构函数是段错误/异常的来源。 streams
调用正在尝试在没有有效CUDA上下文的情况下处理流。因此,当没有上下文时,解决方案不是让任何使CUDA API调用超出范围(或显式调用其析构函数)的类。cudaStreamDestroy