代码之家  ›  专栏  ›  技术社区  ›  meguli

使用模板实现CUDA设备阵列

  •  1
  • meguli  · 技术社区  · 7 年前

    我正在尝试实现一个固定尺寸的推力装置矢量。我编码了一些初始版本,但是我得到了一个奇怪的模板错误。

    代码如下:

    #include <iostream>
    #include <array>
    
    enum class memcpy_t {
        host_to_host,
        host_to_device,
        device_to_host,
        device_to_device
    };
    
    template <typename T, std::size_t N>
    struct cuda_allocator {
        using pointer = T*;
    
        static void allocate(T *dev_mem) {
            cudaMalloc(&dev_mem, N * sizeof(T)); 
        }
    
        static void deallocate(T *dev_mem) {
            cudaFree(dev_mem); 
        }
    
        template <memcpy_t ct>
        static void copy (T *dst, T *src) {
            switch(ct) {
            case memcpy_t::host_to_host:
                cudaMemcpy(dst, src, N * sizeof(T), cudaMemcpyHostToHost);
                break;
            case memcpy_t::host_to_device:
                cudaMemcpy(dst, src, N * sizeof(T), cudaMemcpyHostToDevice);
                break;
            case memcpy_t::device_to_host:
                cudaMemcpy(dst, src, N * sizeof(T), cudaMemcpyDeviceToHost);
                break;
            case memcpy_t::device_to_device:
                cudaMemcpy(dst, src, N * sizeof(T), cudaMemcpyDeviceToDevice);
                break;
            default:
                break;
            }
        }
    };
    
    template <typename T, std::size_t N>
    struct gpu_array {
        using allocator = cuda_allocator<T, N>;
        using pointer = typename allocator::pointer;
        using value_type = T;
        using iterator = T*;
        using const_iterator = T const*;
    
        gpu_array() {
            allocator::allocate(data);
        }
    
        gpu_array(std::array<T, N> host_arr) {
            allocator::allocate(data);
            allocator::copy<memcpy_t::host_to_device>(data, host_arr.begin());
        }
    
        gpu_array& operator=(gpu_array const& o) {
            allocator::allocate(data);
            allocator::copy<memcpy_t::device_to_device>(data, o.begin());
        }
    
        operator std::array<T, N>() {
            std::array<T, N> res;
            allocator::copy<memcpy_t::device_to_host>(res.begin(), data);
            return res;
        }
    
        ~gpu_array() {
            allocator::deallocate(data);
        }
    
        __device__ iterator begin() { return data; }
        __device__ iterator end() { return data + N; }
        __device__ const_iterator begin() const { return data; }
        __device__ const_iterator end() const { return data + N; }
    
    private:
        T* data;
    };
    
    template <typename T, std::size_t N>
    __global__ void add_kernel(gpu_array<T,N> &r,
                               gpu_array<T,N> const&a1,
                               gpu_array<T,N> const&a2) {
        int i = blockIdx.x*blockDim.x + threadIdx.x;
        r.begin()[i] = a1.begin()[i] + a2.begin()[i];
    }
    
    template <typename T, std::size_t N>
    gpu_array<T, N> operator+(gpu_array<T,N> const&a1,
                              gpu_array<T,N> const&a2)
    {
        gpu_array<T, N> res;
        add_kernel<<<(N+255)/256, 256>>>(res, a1, a2);
        return res;
    }
    
    const int N = 1<<20;
    
    int main() {
        std::array<float, N> x,y;
    
        for (int i = 0; i < N; i++) {
            x[i] = 1.0f;
            y[i] = 2.0f;
        } 
    
        gpu_array<float, N> dx{x};
        gpu_array<float, N> dy{y};
    
        std::array<float, N> res = dx + dy;
    
        for(const auto& elem : res) {
            std::cout << elem << ", ";
        }
    }
    

    可能还有很多其他的错误,但我却陷入了一个奇怪的错误中。 nvcc 给出以下错误:

    error: no match for 'operator<' (operand types are '<unresolved overloaded function    type>' and 'memcpy_t')
    allocator::copy<memcpy_t::host_to_device>(data, host_arr.begin());
    

    出于某种原因,它是否将我的枚举类模板参数视为 operator< ?顺便说一下,这是用选项编译的 -arch=sm_70 -std=c++14 . 我没有受过良好的教育,关于C++和CUDA如何交互,所以我无法解决这个问题。

    1 回复  |  直到 7 年前
        1
  •  2
  •   talonmies    7 年前

    它有点头疼,但这里的根本问题是有缺陷的语法根据C++标准。它是生成错误的主机编译器,据我所见,它这样做是完全正确的。参考 here 所有血淋淋的细节。

    您的代码使用 copy 应该如下所示:

    gpu_array(std::array<T, N> host_arr) {
        allocator::allocate(data);
        allocator::template copy<memcpy_t::host_to_device>(data, host_arr.begin());
    }
    
    gpu_array& operator=(gpu_array const& o) {
        allocator::allocate(data);
        allocator::template copy<memcpy_t::device_to_device>(data, o.begin());
    }
    
    operator std::array<T, N>() {
        std::array<T, N> res;
        allocator::template copy<memcpy_t::device_to_host>(res.begin(), data);
        return res;
    }
    

    这可能是有史以来最奇怪的语法,但它是使编译器获得荣誉所必需的。 < 作为模板令牌而不是运算符。修复代码中的所有地方,这个特定的编译器错误应该会消失。