CUDA扩展std :: vector来管理主机和设备数据

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并返回.

Ste*_*ows 8

你可能想看看Thrust.它为CUDA代码提供了一些STL容器.

  • 您不必使用所有Thrust.只需#include <thrust/device_vector.h>和<thrust/host_vector.h>并仅使用它们.它们与std :: vector的工作相似,你正在寻找扩展:你可以使用operator =从主机复制到设备. (5认同)
  • 关于推力的第二个建议.标题唯一性质意味着它根本不重.它也可以很好地处理主机/设备的位置等.重新发明轮子毫无意义.Thrust唯一的缺点是它似乎没有利用CUDA 4.0中引入的统一内存模型.这应该随着下一个版本而改变. (3认同)

tal*_*ies 5

我看到的最大问题是,它并没有真正帮助管理GPU方面的事情,并且它在这个过程中混淆了许多非常重要的信息.

虽然容器类包含有关是否已分配设备指针的信息,但无法知道主机容器的内容是否已被复制到它所拥有的GPU内存,或者GPU内存是否已被复制回设备.因此loadToDevice(),loadFromDevice()每次要在主机或设备代码中使用容器时,都必须调用和方法.这可能意味着至少在某些时候不必要的PCI-e内存传输.并且因为您选择仅包装同步CUDA内存复制例程,所以每次执行此操作时都会有主机阻塞.

最终,我没有看到这个想法在一个精心设计的辅助例程集中获得了很多净收益,这些例程抽象掉了CUDA API中最丑陋的部分并在标准STL类型上运行.