【问题标题】:Is there a better/cleaner/more elegant way to malloc and free in cuda?在 cuda 中是否有更好/更清洁/更优雅的 malloc 和 free 方式?
【发布时间】:2017-01-16 15:44:59
【问题描述】:

我正在尝试 cudaMalloc 一堆设备指针,如果任何 malloc 不起作用,则优雅地退出。我有正常运行的代码——但是很臃肿,因为如果一个失败,我必须 cudaFree 以前 malloc'd 的所有内容。所以现在我想知道是否有更简洁的方法来实现这一点。显然我不能释放没有被 malloc 处理过的东西——那肯定会导致问题。

下面是我试图让代码更优雅的 sn-p。

    //define device pointers
    float d_norm, *d_dut, *d_stdt, *d_gamma, *d_zeta;

    //allocate space on the device for the vectors and answer
    if (cudaMalloc(&d_norm, sizeof(float)*vSize) != cudaSuccess) {
            std::cout << "failed malloc";
            return;
    };

    if (cudaMalloc(&d_data, sizeof(float)*vSize) != cudaSuccess) {
            std::cout << "failed malloc";
            cudaFree(d_norm);
            return;
    };

    if (cudaMalloc(&d_stdt, sizeof(float)*wSize) != cudaSuccess) {
            std::cout << "failed malloc";
            cudaFree(d_norm);
            cudaFree(d_data);
            return;
    };

    if (cudaMalloc(&d_gamma, sizeof(float)*vSize) != cudaSuccess) {
            std::cout << "failed malloc";
            cudaFree(d_norm);
            cudaFree(d_dut);
            cudaFree(d_stdt);
            return;
    };

    if (cudaMalloc(&d_zeta, sizeof(float)*w) != cudaSuccess) {
            std::cout << "failed malloc";
            cudaFree(d_norm);
            cudaFree(d_dut);
            cudaFree(d_stdt);
            cudaFree(d_gamma);
            return;
    };

这是一个缩短的版本,但您可以看到它是如何不断构建的。实际上,我正在尝试 malloc 大约 15 个数组。它开始变得丑陋 - 但它工作正常。

想法?

【问题讨论】:

  • 使用goto,卢克。
  • goto 无助于跟踪已分配的指针列表与未分配的指针列表
  • 一种简单的方法:将所有项目添加到矢量并在最后释放它们
  • 或者您可以使用 RAII 并返回? (使用类似 unique_ptr 的东西)
  • 我的意思有一个很好的例子:stackoverflow.com/questions/788903/… 但是如果它是 C++,当然 RAII 是更好的解决方案。很抱歉造成误导性评论。

标签: c++ cuda malloc free


【解决方案1】:

一些可能性:

  1. cudaDeviceReset() 将释放所有设备分配,而无需遍历指针列表。

  2. 如果您打算退出(应用程序),所有设备分配都会在应用程序终止时自动释放。 cuda 运行时检测到与应用程序的设备上下文关联的进程的终止,并在该点擦除该上下文。因此,如果您只是要退出,不执行任何cudaFree() 操作应该是安全的。

【讨论】:

    【解决方案2】:
    • 您可以使用自定义删除器将它们包装到 unique_ptr 中。 (c++11)

    • 或者当成功分配并释放向量中的所有指针时只添加到一个向量。

    关于 unique_ptr 的示例:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    void nativeFree(float* p);
    float* nativeAlloc(float value);
    
    class NativePointerDeleter{
    public:
       void operator()(float* p)const{nativeFree(p);}
    };
    
    
    int main(){
       using pointer_type = unique_ptr<float,decltype(&nativeFree)>;
       using pointer_type_2 = unique_ptr<float,NativePointerDeleter>;
    
       pointer_type ptr(nativeAlloc(1),nativeFree);
       if(!ptr)return 0;
    
       pointer_type_2 ptr2(nativeAlloc(2));//no need to provide deleter
       if(!ptr2)return 0;
    
       pointer_type ptr3(nullptr,nativeFree);//simulate a fail alloc
       if(!ptr3)return 0;
    
       /*Do Some Work*/
    
       //now one can return without care about all the pointers
       return 0;
    }
    
    void nativeFree(float* p){
       cout << "release " << *p << '\n';
       delete p;
    }
    float* nativeAlloc(float value){
       return new float(value);
    }
    

    【讨论】:

    • 可能是this answer 帮助您处理unique_ptr
    • 也许添加一个小例子来演示 unique_ptr ?请记住,这还需要 C++11 编译器。
    【解决方案3】:

    最初将nullptr 存储在所有指针中。 free 对空指针无效。

    int* p1 = nullptr;
    int* p2 = nullptr;
    int* p3 = nullptr;
    
    if (!(p1 = allocate()))
      goto EXIT_BLOCK;
    if (!(p2 = allocate()))
      goto EXIT_BLOCK;
    if (!(p3 = allocate()))
      goto EXIT_BLOCK;
    
    EXIT_BLOCK:
    free(p3); free(p2); free(p1);
    

    【讨论】:

      【解决方案4】:

      问题标记为 C++,所以这里有一个 C++ 解决方案

      一般的做法是在构造函数中获取资源,在析构函数中释放。这个想法是,在任何情况下,资源都可以通过调用析构函数来保证释放。一个很好的副作用是析构函数在作用域的末尾被自动调用,所以你不需要做任何事情来释放不再使用的资源。见RAII

      在资源角色中,可能有各种内存类型、文件句柄、套接字等。CUDA 设备内存也不例外。

      我也不鼓励您编写自己的资源拥有类,并建议您使用库。 thrust::device_vector 可能是使用最广泛的设备内存容器。 Thrust 库是 CUDA 工具包的一部分。

      【讨论】:

        【解决方案5】:

        是的。如果您使用(我的)CUDA Modern-C++ API wrapper library,您可以只使用唯一指针,它们将在其生命周期结束时释放。您的代码将变成以下内容:

        auto current_device = cuda::device::current::get();
        auto d_dut   = cuda::memory::device::make_unique<float[]>(current_device, vSize);
        auto d_stdt  = cuda::memory::device::make_unique<float[]>(current_device, vSize);
        auto d_gamma = cuda::memory::device::make_unique<float[]>(current_device, vSize);
        auto d_zeta  = cuda::memory::device::make_unique<float[]>(current_device, vSize);
        

        但请注意,您可以只分配一次,然后将其他指针放置在适当的偏移量处。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-01-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-23
          • 2020-05-10
          相关资源
          最近更新 更多