我目前开始使用CUDA,不得不承认我对C API有点失望。我理解选择C的原因,但如果语言基于C++,例如通过cudaMalloc
分配设备内存等方面将会更加简单。
我的计划是自己完成这个任务,使用重载的operator new
和RAII(两种替代方案)进行放置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++ 支持,无论他们指的是什么)。
因此,我的问题实际上有三个:
- 我的放置
new
重载语义上正确吗?是否会泄漏内存? - 是否有关于未来 CUDA 开发朝着这个方向发展的信息(让我们面对现实:C 接口在 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&);
};
关于此处使用的单例模式:是的,我知道它的缺点。但是,在这种情况下这些缺点并不相关。我所需要的只是一个无法复制的小型类型标记。其他方面(例如多线程考虑和初始化时间)并不适用。