CUDA:在 C++ 中包装设备内存分配

Posted

技术标签:

【中文标题】CUDA:在 C++ 中包装设备内存分配【英文标题】:CUDA: Wrapping device memory allocation in C++ 【发布时间】:2010-09-22 22:06:29 【问题描述】:

我现在开始使用 CUDA,不得不承认我对 C API 有点失望。我理解选择 C ​​的原因,但是如果该语言是基于 C++ 的,那么几个方面会简单得多,例如设备内存分配(通过cudaMalloc)。

我的计划是自己做这件事,使用重载的 operator new 和放置 new 和 RAII(两种选择)。我想知道到目前为止是否有任何我没有注意到的警告。代码似乎可以工作,但我仍然想知道潜在的内存泄漏。

RAII代码的用法如下:

CudaArray<float> device_data(SIZE);
// Use `device_data` as if it were a raw pointer.

也许在这种情况下一个类是多余的(特别是因为你仍然必须使用cudaMemcpy,这个类只封装了RAII)所以另一种方法是放置new

float* device_data = new (cudaDevice) float[SIZE];
// Use `device_data` …
operator delete [](device_data, cudaDevice);

这里,cudaDevice 只是作为一个标签来触发过载。然而,由于在正常放置 new 这将指示放置,我发现语法奇怪地一致,甚至可能比使用类更可取。

我会很感激各种批评。有人可能知道下一个版本的 CUDA 是否计划在这个方向上做一些事情(据我所知,这将改进其对 C++ 的支持,不管他们的意思是什么)。

所以,我的问题实际上是三方面的:

    我的位置new 重载在语义上是否正确?它会泄漏内存吗? 有没有人知道未来 CUDA 发展朝着这个大方向发展的信息(让我们面对现实:C++ s*ck 中的 C 接口)? 我怎样才能以一致的方式更进一步(还有其他 API 需要考虑,例如,不仅有设备内存,还有常量内存存储和纹理内存)?

// Singleton tag for CUDA device memory placement.
struct CudaDevice 
    static CudaDevice const& get()  return instance; 
private:
    static CudaDevice const instance;
    CudaDevice()  
    CudaDevice(CudaDevice const&);
    CudaDevice& operator =(CudaDevice const&);
 const& cudaDevice = CudaDevice::get();

CudaDevice const CudaDevice::instance;

inline void* operator new [](std::size_t nbytes, CudaDevice const&) 
    void* ret;
    cudaMalloc(&ret, nbytes);
    return ret;


inline void operator delete [](void* p, CudaDevice const&) throw() 
    cudaFree(p);


template <typename T>
class CudaArray 
public:
    explicit
    CudaArray(std::size_t size) : size(size), data(new (cudaDevice) T[size])  

    operator T* ()  return data; 

    ~CudaArray() 
        operator delete [](data, cudaDevice);
    

private:
    std::size_t const size;
    T* const data;

    CudaArray(CudaArray const&);
    CudaArray& operator =(CudaArray const&);
;

关于这里使用的单例:是的,我知道它的缺点。但是,这些在这种情况下无关紧要。我在这里只需要一个不可复制的小型标签。其他所有内容(即多线程注意事项、初始化时间)均不适用。

【问题讨论】:

您的单例实现充其量是危险的。请参阅有关如何在 C++ 中创建单例的许多其他讨论。 是的,你是对的。但是,请参阅我在代码下方的新说明。 【参考方案1】:

与此同时,还有一些进一步的发展(不是在 CUDA API 方面,但至少在尝试使用类似 STL 的方法来管理 CUDA 数据管理的项目方面)。

最值得注意的是 NVIDIA 研究的一个项目:thrust

【讨论】:

【参考方案2】:

我会采用安置新方法。然后我会定义一个符合 std::allocator 接口的类。理论上,您可以将此类作为模板参数传递给 std::vector 和 std::map 等等。

当心,我听说做这样的事情充满了困难,但至少你会通过这种方式学到更多关于 STL 的知识。而且您无需重新发明容器和算法。

【讨论】:

没想过分配器。我以前实际上已经这样做过,所以应该不会太难。【参考方案3】:

有没有人知道未来 CUDA 发展朝着这个大方向发展的信息(让我们面对现实:C++ s*ck 中的 C 接口)?

是的,我做过类似的事情:

https://github.com/eyalroz/cuda-api-wrappers/

nVIDIA 的 CUDA 运行时 API 适用于 C 和 C++ 代码。因此,它使用 C 风格的 API,即较低的公分母(模板化函数重载的一些显着例外)。

这个围绕运行时 API 的包装库旨在让我们能够采用 C++ 的许多特性(包括一些 C++11)来使用运行时 API - 但不会降低表达性或增加抽象级别(如例如,在 Thrust 库中)。使用 cuda-api-wrappers,您仍然拥有您的设备、流、事件等 - 但以更多 C++ 惯用方式使用它们会更方便。

【讨论】:

【参考方案4】:

有几个项目尝试类似的事情,例如CUDPP。

不过,与此同时,我实现了自己的分配器,它运行良好且简单明了(> 95% 的样板代码)。

【讨论】:

stdcuda 链接已失效。 @einpoklum 谢谢。按理说,一个有 10 年历史的答案在某个时候会过时。我删除了链接。

以上是关于CUDA:在 C++ 中包装设备内存分配的主要内容,如果未能解决你的问题,请参考以下文章

gpuocelot 是不是支持 CUDA 设备中的动态内存分配?

无法为cufftComplex数据类型分配CUDA设备内存

分配给设备内存的 CUDA 全局(如 C 语言)动态数组

cuda 编 程简单CUDA程序的基本框架

CUDA 常量内存分配是如何工作的?

基本 CUDA 指针/数组内存分配和使用