簡體   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