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

rqui 发布于 2018-05-23 c++ 最后更新 2018-05-23 17:37 138 浏览

我现在开始使用CUDA,不得不承认我对C API有点失望。我理解选择C的原因,但是它的语言是基于C++的,而几个方面会比较简单,例如,设备内存分配(通过cudaMalloc)。 我的计划是自己做,使用new和RAII(两种替代方案)的operator new超载。我想知道是否有任何我迄今尚未注意到的警告。该代码似乎工作,但我仍然想知道潜在的内存泄漏。 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++的支持,无论它们是什么意思)。 所以,我的问题实际上是三重的:
  1. 我的展示位置new过载是否在语义上正确?它会泄漏内存吗?
  2. 是否有人有关于未来CUDA开发的信息,这些信息将沿着这个总体方向发展(让我们面对它:C++中的C接口* ck)?
  3. 我怎样才能以一致的方式进一步处理这个问题(还有其他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&);
};
关于这里雇用的单身人士:是的,我知道它的缺点。但是,这些在这方面并不相关。我所需要的只是一个不可复制的小型标签。其他一切(即多线程考虑,初始化时间)都不适用。
已邀请:

rqui

赞同来自:

You seem to have charged in telling us what you are planning to do with explain your use cases for the data (this is probably because it is obvious to you).
你的意思是“没有”?对,对不起。 CUDA是来自NVIDIA的GPGPU编程语言,它基于C/C++构建,为GCC提供前端。我的问题主要针对那些已经知道它的细节的人。我的用法非常随意,这个问题真的更关心CUDA,因为CUDA只提供给你一个C接口,因此即使你用C++工作,也会迫使你放弃很多有用的C++功能。
The only C API I see so far are cudaMalloc and cudaFree. … Can't you just wrap these inside the constructor/destructor of your CudoClass.
是的,没有。这或多或少是我目前所做的,但我对此并不满意。我的问题实际上是三重的(我会相应地更新问题):
  1. 我的展示位置new超载的语义正确吗?它会泄漏内存吗?
  2. 是否有人有关于未来CUDA开发的信息,这些信息将沿着这个总体方向发展(让我们面对它:C++中的C接口* ck)?
  3. 我怎样才能以一致的方式进一步处理这个问题(还有其他API需要考虑,例如,不仅有设备内存,而且还有一个常量内存存储和纹理内存)?
Apart from the Malloc and Free what other API's are there? I am presuming they allocate memory and copy data from the device into the newly allocated memory?
是的......实际上,我刚刚想到了一种封装cudaMemcpy功能的方法。 ;-)
Do you just want to see the raw data as arrays of some specific type? Or are the other operations you want to perform?
实际上,一旦内存初始化并将一些数据复制到其中(参见上面的cudaMemcpy),我就完成了很多工作。其余的动作是GPU的一部分,我只需要一些基本的数组访问。这里非常基本的工作流程是:
  1. 分配设备内存,
  2. 将您的数据复制到设备内存中,
  3. 调用处理内存的(并行)GPU动作,
  4. 将数据复制回RAM。
第3步几乎是一成不变的。

jsed

赞同来自:

我会选择放置新方法。然后我将定义一个符合std :: allocator<>接口的类。理论上讲,你可以将这个类作为模板参数传递给std :: vector<>和std :: map<>等等。 当心,我听说做这样的事情是困难的,但至少你会以这种方式学习更多关于STL的知识。而且你不需要重新发明你的容器和算法。

rqui

赞同来自:

已经有两个项目尝试类似:

  • <stdcuda>
  • CUDPP 然而,与此同时,我已经实现了我的分配器,它工作得非常好,并且完全直截了当(> 95%的样板代码)。

pnon

赞同来自:

与此同时,还有一些进一步的发展(对于CUDA API而言,并非如此,但至少在尝试类似STL的CUDA数据管理方法的项目中)。 最值得注意的是NVIDIA研究项目:thrust