Exception 如果我的类抛出一个被捕获的异常,它是否正在分配GPU内存泄漏?

Exception 如果我的类抛出一个被捕获的异常,它是否正在分配GPU内存泄漏?,exception,error-handling,cuda,resource-leak,Exception,Error Handling,Cuda,Resource Leak,我正在用is编写一个“error check”函数,每当遇到错误时就会抛出std::runtime\u error(下面的代码示例)。 情况: 假设在foo构造函数中(在下面的文件:fooClass.h示例中),我能够成功地设置cuda设备并分配gpu内存,但是我对cudaMemcpy(***)的调用返回了一个错误,无论出于何种原因。这意味着我的errchk(***)函数正在抛出一个异常,并将控制权转移到main()函数(在文件中:Start.cpp)。 由于foo构造函数没有完成它的

我正在用is编写一个“error check”函数,每当遇到错误时就会抛出
std::runtime\u error
(下面的代码示例)。

情况:
假设在
foo
构造函数中(在下面的文件:
fooClass.h
示例中),我能够成功地设置cuda设备并分配gpu内存,但是我对
cudaMemcpy(***)
的调用返回了一个错误,无论出于何种原因。这意味着我的
errchk(***)
函数正在抛出一个异常,并将控制权转移到
main()
函数(在文件中:
Start.cpp
)。

由于
foo
构造函数没有完成它的作业,因此没有创建
foo
对象,并且没有调用
~foo()
析构函数,因此以前分配的gpu资源没有释放。

我知道
try
main()中的
block应该按照与其初始化相反的顺序取消分配所有资源(每当抛出异常时)。


问题1:
这是否意味着我遇到了gpu内存泄漏?或者
throw
try
catch
可以处理这种情况吗?

问题#2(?基于意见的?):
我正在学习C++异常系统,为此我想知道这是不是一个好主意,<强>抛出异常,只在主()/<代码>函数> <强>中捕获它们。
我认为这是值得考虑的,因为这样我就能够以“自然”的方式结束我的程序-
返回EXIT\u FAILURE与退出相反(退出失败)


ErrorCheck.h:(整个文件)

Start.cpp:

#include <iostream>
#include "fooClass.h"
using namespace std;

int main() {
    try {
        foo bar; // constructor of "foo" Class is called.
    }
    catch (std::runtime_error error) {
        cout << error.what() << endl;
        getchar();
        return EXIT_FAILURE;
    }
}
#包括
#包括“fooClass.h”
使用名称空间std;
int main(){
试一试{
foo-bar;//调用了“foo”类的构造函数。
}
捕获(标准::运行时错误){
库特
这是否意味着我遇到了gpu内存泄漏

是的,确实如此。如果不确保资源最终会被释放,就不能分配资源;如果
cudaMemCpy()
失败,则抛出异常,这不是在保证

还是说“扔、试、抓”可以解决这种情况

事实上,是的,有点..正如@Jean BaptisteYunès所建议的,RAII是关键。请阅读以下内容:

因此,如果您可以将内存分配和反分配推到
foo
类的RAII成员中,您就已经完成了它的构造,因此它的析构函数(deallocations)将在退出
foo()
范围时运行,即使出现异常

在这一点上,我要说的是,你正在用你正在编写的一些代码重新发明轮子。你可以找到一种将CUDA错误包装为异常的机制,以及一个独特的指针,如RAII holder,用于我的库*中分配的内存。因此,你会有如下情况:

class foo {
public:
    using element_type = float;
    enum : size_t { num_elements };
protected:
    struct {
        cuda::memory::device::unique_ptr<element_type> device;
        cuda::memory::host::unique_ptr<element_type>   host;
    } data;

public:
    foo() : data( {
        cuda::memory::device::make_unique<element_type[]>(
            cuda::device::default_device_id, 
            num_elements
        ),
        cuda::memory::host::make_unique(num_elements)
    } )
    {
        // Do something...
        cuda::memory::copy(
            data.host.get(), data.device.get(), num_elements * sizeof(element_type)
        );
        // Do something...
    }
    ~foo() {
        // Do something...

        // ERRCHK(cudaFree(dev_floatArray));
        // No need to free anything! It's magic!

        // ERRCHK(cudaDeviceReset());
        // Don't reset your device you really need to - and
        // you don't need to.
    }
}
class-foo{
公众:
使用元素类型=浮动;
enum:size_t{num_elements};
受保护的:
结构{
cuda::内存::设备::唯一的ptr设备;
cuda::内存::主机::唯一的\u ptr主机;
}数据;
公众:
foo():数据({
cuda::内存::设备::使_唯一(
cuda::设备::默认设备id,
num_元素
),
cuda::内存::主机::使_唯一(num_元素)
} )
{
//做点什么。。。
cuda::内存::复制(
data.host.get()、data.device.get()、num_elements*sizeof(element_类型)
);
//做点什么。。。
}
~foo(){
//做点什么。。。
//ERRCHK(cudaFree(dev_floatArray));
//不需要释放任何东西!这是魔法!
//ERRCHK(cudaDeviceReset());
//不要重置您真正需要的设备-并且
//你不需要。
}
}

你可以考虑的另一种方法,而不是RAII类来保存内存,是Andrei Alexandrescu的“范围守护”机制。他在视频中解释了它(最新版本):

在任何地方抛出异常,并且只在main()函数中捕获它们,这是一个好主意吗

把它作为一个单独的问题,因为答案不是简单的是/否。事实上,这里有足够的问题和答案,我认为这些问题和答案涵盖了这一点


*-其他库也可能提供类似的功能,例如,可能是推力;但有了这个库,您不必进行复杂的抽象,只需要CUDA运行时API包装。)

您听说过RAII吗?这是处理此类问题的一种非常常见的模式。@Jean BaptisteYunè有点不好意思承认,但从未听说过它……但我们赢得我们的一生。:)谢谢。我想你的回答解决了我的两个问题。别不好意思,你不知道,但现在你知道了。。。
#include "ErrorCheck.h"
class foo
{
private:
    float * dev_floatArray;
    float * host_floatArray;

public:
    foo() {
        // Do something...
        ERRCHK(cudaSetDevice(0));
        ERRCHK(cudaMalloc(&dev_floatArray, 10000 * sizeof(float)));
        // Do something...
        ERRCHK(cudaMemcpy(host_floatArray, dev_floatArray, 10000 * sizeof(float), cudaMemcpyDeviceToHost));
        // Do something...
    }
    ~foo() {
        // Do something...
        ERRCHK(cudaFree(dev_floatArray));
        ERRCHK(cudaDeviceReset());
    }
}
#include <iostream>
#include "fooClass.h"
using namespace std;

int main() {
    try {
        foo bar; // constructor of "foo" Class is called.
    }
    catch (std::runtime_error error) {
        cout << error.what() << endl;
        getchar();
        return EXIT_FAILURE;
    }
}
class foo {
public:
    using element_type = float;
    enum : size_t { num_elements };
protected:
    struct {
        cuda::memory::device::unique_ptr<element_type> device;
        cuda::memory::host::unique_ptr<element_type>   host;
    } data;

public:
    foo() : data( {
        cuda::memory::device::make_unique<element_type[]>(
            cuda::device::default_device_id, 
            num_elements
        ),
        cuda::memory::host::make_unique(num_elements)
    } )
    {
        // Do something...
        cuda::memory::copy(
            data.host.get(), data.device.get(), num_elements * sizeof(element_type)
        );
        // Do something...
    }
    ~foo() {
        // Do something...

        // ERRCHK(cudaFree(dev_floatArray));
        // No need to free anything! It's magic!

        // ERRCHK(cudaDeviceReset());
        // Don't reset your device you really need to - and
        // you don't need to.
    }
}