繁体   English   中英

无法在 cuda 内核中使用我的模板类

[英]Can't use my template class in cuda kernel

我以为我知道如何编写一些干净的 cuda 代码。 直到我尝试制作一个简单的模板类并在一个简单的内核中使用它。 我这几天一直在解决问题。 我访问过的每一个线程都让我觉得有点愚蠢。

对于错误检查,我使用了这个

这是我的 class.h:

#pragma once
template <typename T>
class MyArray
{
public:
    const int size;

    T *data;

    __host__ MyArray(int size); //gpuErrchk(cudaMalloc(&data, size * sizeof(T)));

    __device__ __host__ T GetValue(int); //return data[i]
    __device__ __host__ void SetValue(T, int); //data[i] = val;
    __device__ __host__ T& operator()(int); //return data[i];

    ~MyArray(); //gpuErrchk(cudaFree(data));
};

template class MyArray<double>;

class.cu的相关内容在评论里。 如果您认为整件事是相关的,我很乐意添加它。

现在是主要课程:

__global__ void test(MyArray<double> array, double *data, int size)
{
    int j = threadIdx.x;
        //array.SetValue(1, j);  //doesn't work
        //array(j) = 1;  //doesn't work
        //array.data[j] = 1; //doesn't work
        data[j] = 1;   //This does work !
        printf("Reach this code\n");
    }
}
int main(int argc, char **argv)
{
    MyArray x(20);
    test<<<1, 20>>>(x, x.data, 20);

    gpuErrchk(cudaPeekAtLastError());
    gpuErrchk(cudaDeviceSynchronize());
}

当我说“不起作用”时,我的意思是程序停在那里(在到达 printf 之前)而没有输出任何错误。 另外,我从cudaDeviceSynchronizecudaFree收到以下错误:

遇到非法内存访问

我无法理解的是,内存管理应该没有问题,因为将数组直接发送到内核工作正常。 那么为什么当我发送一个类并尝试访问类数据时它不起作用? 为什么当我的代码明显遇到错误时却没有收到警告或错误消息?

这是nvcc --version的输出

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2017 NVIDIA Corporation
Built on Fri_Nov__3_21:07:56_CDT_2017
Cuda compilation tools, release 9.1, V9.1.85

(编辑说明:关于这个问题的评论中有相当多的虚假信息,所以我整理了一个答案作为社区维基条目。)

模板类不能作为参数传递给内核没有特别的原因。 在这样做之前需要清楚地了解一些限制:

  1. 出于所有意图和目的,CUDA 内核参数始终按值传递。 在极其有限的情况下支持通过引用传递(所讨论的参数必须存储在托管内存中)。 这不适用于此处。
  2. 作为 (1) 的结果,POD 参数可以正常工作,因为它们可以简单地复制并且不依赖于任何特殊行为
  3. 类是不同的,因为当您按值传递类时,您是在隐式调用复制构造或移动构造语义。 这意味着作为内核参数按值传递的类必须可以简单地复制构造。 作为内核启动的一部分,无法在设备上运行非平凡的复制构造函数。
  4. CUDA 进一步要求类不包含虚拟成员
  5. 虽然<<< >>>内核启动语法看起来像一个简单的函数调用,但它不是。 在您在主机代码中编写的内容与主机端工具链实际发出的内容之间存在多层抽象样板和 API 调用。 这意味着您的代码和 GPU 之间存在多个复制构造操作。 如果您执行诸如在析构函数中调用cudaFree类的操作,您应该假设它将作为函数调用序列的一部分被调用,当其中一个副本超出范围时,该函数调用序列将启动内核。 你不希望出现这种情况。

您没有说明在这种情况下类成员函数是如何实际实现的,因此除了将原始指针传递给内核之外,除了将原始指针传递给内核之外,还不可能说出为什么您的代码注释暗示的许多排列之一起作用或不起作用,因为它是一个简单可复制的 POD 值,而类几乎肯定不是。

这是一个简单而完整的示例,展示了如何进行这项工作:

$cat classy.cu
#include <vector>
#include <iostream>

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
    if (code != cudaSuccess)
    {
        fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
        if (abort) exit(code);
    }
}

template <typename T>
class MyArray
{
    public:
        int len;
        T *data;

        __device__ __host__ void SetValue(T val, int i) { data[i] = val; };
        __device__ __host__ int size() { return sizeof(T) * len; };

        __host__ void DevAlloc(int N) {
            len = N;
            gpuErrchk(cudaMalloc(&data, size()));
        };

        __host__ void DevFree() {
            gpuErrchk(cudaFree(data));
            len = -1;
        };
};

__global__ void test(MyArray<double> array, double val)
{
    int j = threadIdx.x;
    if (j < array.len)
        array.SetValue(val, j);
}

int main(int argc, char **argv)
{
    const int N = 20;
    const double val = 5432.1;

    gpuErrchk(cudaSetDevice(0));
    gpuErrchk(cudaFree(0));

    MyArray<double> x;
    x.DevAlloc(N);

    test<<<1, 32>>>(x, val);
    gpuErrchk(cudaPeekAtLastError());
    gpuErrchk(cudaDeviceSynchronize());

    std::vector<double> y(N);
    gpuErrchk(cudaMemcpy(&y[0], x.data, x.size(), cudaMemcpyDeviceToHost));
    x.DevFree();

    for(int i=0; i<N; ++i) std::cout << i << " = " << y[i] << std::endl;

    return 0;
}

像这样编译和运行:

$ nvcc -std=c++11 -arch=sm_53 -o classy classy.cu
$ cuda-memcheck ./classy
========= CUDA-MEMCHECK
0 = 5432.1
1 = 5432.1
2 = 5432.1
3 = 5432.1
4 = 5432.1
5 = 5432.1
6 = 5432.1
7 = 5432.1
8 = 5432.1
9 = 5432.1
10 = 5432.1
11 = 5432.1
12 = 5432.1
13 = 5432.1
14 = 5432.1
15 = 5432.1
16 = 5432.1
17 = 5432.1
18 = 5432.1
19 = 5432.1
========= ERROR SUMMARY: 0 errors

(Jetson Nano 上的 CUDA 10.2/gcc 7.5)

请注意,我已经包含了用于分配和释放的主机端函数,这些函数不与构造函数和析构函数交互。 否则该类与您的设计非常相似并且具有相同的属性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM