Nil*_*ils 1 c++ stl cuda software-design
我了解到std :: vector是C++中原始数组的一个很好的包装器,所以我开始用它来管理我的CUDA应用程序中的主机数据[1].由于必须手动分配和复制东西会使代码更复杂,更不易读,我想扩展std :: vector.由于我不是很有经验,所以我想知道你对它的看法.特别是天气正确完成(例如std :: vector的析构函数是隐式调用的,对吧?)如果你认为它是个好主意.
我写了一个小例子来说明这一点
#include <vector>
#include <cuda.h>
#include <cstdio>
void checkCUDAError(const char *msg)
{
cudaError_t err = cudaGetLastError();
if( cudaSuccess != err) {
fprintf(stderr, "Cuda error: %s: %s.\n", msg, cudaGetErrorString(err));
exit(EXIT_FAILURE);
}
}
// Wrapper around CUDA memory
template<class T>
class UniversalVector: public std::vector<T>
{
T* devicePtr_;
bool allocated;
public:
// Constructor
UniversalVector(unsigned int length)
:std::vector<T>(length),
allocated(false)
{}
// Destructor
~UniversalVector()
{
if(allocated)
cudaFree(devicePtr_);
}
cudaError_t allocateDevice()
{
if(allocated) free(devicePtr_);
cudaError_t err =
cudaMalloc((void**)&devicePtr_, sizeof(T) * this->size());
allocated = true;
return err;
}
cudaError_t loadToDevice()
{
return cudaMemcpy(devicePtr_, &(*this)[0], sizeof(T) * this->size(),
cudaMemcpyHostToDevice);
}
cudaError_t loadFromDevice()
{
return cudaMemcpy(&(*this)[0], devicePtr_, sizeof(T) * this->size(),
cudaMemcpyDeviceToHost);
}
// Accessors
inline T* devicePtr() {
return devicePtr_;
}
};
__global__ void kernel(int* a)
{
int i = threadIdx.x;
printf("%i\n", a[i]);
}
int main()
{
UniversalVector<int> vec(3);
vec.at(0) = 1;
vec.at(1) = 2;
vec.at(2) = 3;
vec.allocateDevice();
vec.loadToDevice();
kernel<<<1, 3>>>(vec.devicePtr());
checkCUDAError("Error when doing something");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
[1]在CUDA中,它区分主机和设备存储器,其中主机存储器是GPU可访问的存储器,而设备存储器是GPU上的存储器程序员必须将存储器从主机移动到GPU并返回.
我看到的最大问题是,它并没有真正帮助管理GPU方面的事情,并且它在这个过程中混淆了许多非常重要的信息.
虽然容器类包含有关是否已分配设备指针的信息,但无法知道主机容器的内容是否已被复制到它所拥有的GPU内存,或者GPU内存是否已被复制回设备.因此loadToDevice(),loadFromDevice()每次要在主机或设备代码中使用容器时,都必须调用和方法.这可能意味着至少在某些时候不必要的PCI-e内存传输.并且因为您选择仅包装同步CUDA内存复制例程,所以每次执行此操作时都会有主机阻塞.
最终,我没有看到这个想法在一个精心设计的辅助例程集中获得了很多净收益,这些例程抽象掉了CUDA API中最丑陋的部分并在标准STL类型上运行.