CUDA扩展std::vector来管理主机和设备数据
CUDA extending std::vector to manage host and device data
我了解到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("%in", 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;
}
[1] 在CUDA中,它区分了主机和设备内存,其中主机内存是GPU可访问的内存,设备内存是GPU上的内存。程序员必须将内存从主机移动到GPU,然后再移动回来。
您可能需要了解Thrust。它为CUDA代码提供了一些STL容器。
我看到的最大问题是,它并不能真正帮助管理GPU方面的事情,并且在这个过程中混淆了许多非常重要的信息。
虽然容器类包含关于设备指针是否已被分配的信息,但无法知道主机容器的内容是否已被复制到其所持有的GPU存储器,或者GPU存储器是否已被拷贝回设备。因此,每次希望在主机或设备代码中使用容器时,都必须调用loadToDevice()
和loadFromDevice()
方法。这可能意味着至少在某些时候不必要的PCI-e内存传输。因为您选择只包装同步CUDA内存复制例程,所以每次执行此操作时都会出现主机阻塞。
最终,与一组设计良好的辅助例程相比,我看不出这个想法有多大的净收益,这些例程抽象掉了CUDA API中最丑陋的部分,并在标准STL类型上操作。
我会扩展一下David Rodríguez-dribeas的评论:
为什么你应该更喜欢组合而不是继承(尽管它需要额外的外观工作)这个问题已经被问了很多次,也被回答了很多次。一个好的答案是:更喜欢组合而不是继承?
决定因素是接口:您想要底层类的全部或部分方法吗
在您的情况下,修改向量大小的std::vector
方法,如resize
、push_back
、pop_back
、erase
、insert
等,如果在loadToDevice
和loadFromDevice
的调用之间调用,可能会造成混乱。
在您的问题中,您指出您需要一个原始数组的包装器。那些是固定尺寸的!因此,您可以在包装器类内部使用std::vector
(composition!),但您需要隐藏它的所有动态大小
您最好将allocateDevice
和loadToDevice
等函数作为自由函数,而不是从std::vector
继承的类成员。它可以节省大量将其他库/类与您的东西集成在一起的时间。整体看起来不错。
- 防止主数据类型C++的隐式转换
- 用于访问容器<T>数据成员的正确 API
- 嵌套在类中时无法设置成员数据
- 使用流处理接收到的数据
- Cuda C++:设备上的Malloc类,并用来自主机的数据填充它
- 来自复杂主机数据的阵列火力阵列
- 通过套接字一致地丢失数据(但在使用本地主机连接时不会)
- C++ 如何接收从互联网站点到本地主机的流数据包
- 将数据保存到空闲主机上的文件中
- 将二进制数据从Qt/C++DLL传递到Delphi主机应用程序中
- 通过Linux套接字接收多个主机的数据
- 如何将设备内存中分配的结构化数据从设备复制到主机
- 在将复杂数据从主机传输到设备的简单 CUDA 代码中出现问题
- MPI_Gather-向主机发送数据
- UDP发送()到本地主机在Winsock下丢弃数据包
- 保持主机数据完整,同时传输到CUDA GPU
- 从Oracle PL/SQL中获取数据到主机阵列
- Gstreamer:为什么我不能在本地主机上通过UDP发送数据?
- 从主机内部通过网络发送midi数据
- CUDA扩展std::vector来管理主机和设备数据