【问题标题】:Converting C Program To CUDA (Max Reduction)将 C 程序转换为 CUDA(最大缩减)
【发布时间】:2015-05-05 00:46:03
【问题描述】:

我是 CUDA 的新手,并试图掌握基本知识,所以如果我问或说的事情听起来过于简单,我深表歉意。我用 C 语言编写了一些串行代码,用于生成带有随机数的数组,然后在该数组中找到最大值。

    #include <stdio.h>
    #include <stdlib.h> /* srand, rand */
    #include <time.h> /* time */

    #define num 100000

    int *arr,max = -1;

    int getRand() {
        double r1=rand()/(double)RAND_MAX; // Generates value between 0 & 1
        return (r1 * num) + 1;
    }
    void generateRandom(int M) {
        int i;
        for(i=0;i<M;i++) {
            arr[i] = getRand();
        }
    }
    void getMax(int M) {
        int i;
        for(i=0;i<M;i++) {
            if(arr[i] > max)
                max = arr[i];
            }
    }

    int main(int argc, char *argv[] ){
        if (argc == 2) {
            int M;
            /* initialize random seed: */
            srand (time(NULL));
            M = atoi(argv[1]);
            //int arr[M];
            arr = (int*)calloc(M,sizeof(int));;

            //printf("M = %d MAX = %d\n", M, RAND_MAX);

            generateRandom(M);

            getMax(M);

            printf("Max value: %d",max);

        }

        else
            printf("Invalid arguments.");

        return 0;
    }

我现在正在尝试将此代码转换为简单的 CUDA 程序。我尝试让 generateRandom 函数作为内核运行,但内存管理出现问题。

#include <stdio.h>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
#include <cuda.h>

#define num 100000

int *arr,max = -1;

int getRand() {
    double r1=rand()/(double)RAND_MAX; // Generates value between 0 & 1
    return (r1 * num) + 1;
}
void generateRandom(int M) {
    int i;
    for(i=0;i<M;i++) {
        arr[i] = getRand();
    }
}
__global__ void getMax(int M) {
    int i;
    for(i=0;i<M;i++) {
        if(arr[i] > max)
            max = arr[i];
        }
}

int main(int argc, char *argv[] ){
    if (argc == 2) {
        int M;
        /* initialize random seed: */
        srand (time(NULL));
        M = atoi(argv[1]);
        //int arr[M];
        arr = (int*)calloc(M,sizeof(int));

        //printf("M = %d MAX = %d\n", M, RAND_MAX);

        generateRandom(M);

        getMax<<<1,1>>>(M);

        printf("Max value: %d",max);

    }

    else
        printf("Invalid arguments.");

    return 0;
}

该代码导致以下错误。

cudabasic.cu(23): 警告:主机变量“arr”不能直接在>设备函数中读取

cudabasic.cu(23):警告:主机变量“max”不能直接在>设备函数中读取

cudabasic.cu(24): 警告:主机变量“arr”不能直接在>设备函数中读取

cudabasic.cu(24):警告:主机变量“max”不能直接写入 > 在设备函数中

我用谷歌搜索了这个错误,发现问题在于我将全局变量传递给内核,因此设备无法读取它。根据在线建议,我尝试通过使用指针而不是传递实际变量来解决这个问题,但我仍然遇到错误。

#include <stdio.h>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
#include <cuda.h>

#define num 100000

int *arr,max = -1;

int getRand() {
    double r1=rand()/(double)RAND_MAX; // Generates value between 0 & 1
    return (r1 * num) + 1;
}
void generateRandom(int M) {
    int i;
    for(i=0;i<M;i++) {
        arr[i] = getRand();
    }
}
__global__ void getMax(int M, int *dArr, int *dMax) {
    int i = threadIdx.x;
    int a = dArr[i];
    for(i=0;i<M;i++) {
        if(a > dMax)
            dMax = a;
        }
}

int main(int argc, char *argv[] ){
    if (argc == 2) {
        int M;
        /* initialize random seed: */
        srand (time(NULL));
        M = atoi(argv[1]);
        //int arr[M];
        arr = (int*)calloc(M,sizeof(int));
        devArr = (int*)cudaMalloc(M,sizeof(int));

        //printf("M = %d MAX = %d\n", M, RAND_MAX);

        generateRandom(M);

        getMax<<<1,1>>>(M, arr, max);

        printf("Max value: %d",max);

    }

    else
        printf("Invalid arguments.");

    return 0;
}

cudabasic.cu(24):错误:操作数类型不兼容(“int”和“int *”)

cudabasic.cu(25):错误:“int”类型的值不能分配给“int *”类型的>实体

有人能给我指出如何最好地做到这一点的正确方向吗?

我是 CUDA 的新手,并试图掌握基本知识,所以如果我问或说的事情听起来过于简单,我深表歉意。

【问题讨论】:

    标签: c arrays cuda max reduction


    【解决方案1】:

    我能提供的最好建议是学习一些介绍性的 CUDA 编程材料,例如 this。您的代码不仅缺乏对 CUDA 的理解,而且还缺乏对基本 C 概念的理解(比如变量必须在用于表达式之前定义。)作为 CUDA 程序员,不要“刷新”您对如何编写正确 C 的知识或 C++ 代码。如果您搜索“gtc cuda intro”或“gtc cuda optimization”之类的内容,您会发现很好的 CUDA 学习资料。

    您采用的方法是采用单线程 C/C++ 代码,并将其转换为使用单个 CUDA 线程运行,这可能会给您一些关于“学习 CUDA”的温暖而模糊的感觉,但您是并没有真正解决任何重要的概念 - 它显示在您现在正在努力解决的代码中。

    要获得您提供的最后一个功能性代码,还需要几个步骤:

    1. 在 CUDA 中,通常不能在主机代码中取消引用设备指针,并且通常不能在设备代码中使用主机指针。这意味着您通常不应将主机指针传递给设备内核:

      getMax<<<1,1>>>(M, arr, max);
                         ^^^  ^^^
      

      您正在使用您的devArray 修复arr 问题(尽管您的cudaMalloc 设置不正确),我们只需要修复它并通过额外的cudaMemcpy 操作来完成它将主机数据复制到设备。如果你不确定如何使用像 cudaMalloc 这样的函数,不要只是猜测你的方式并使用强制类型转换来强制类型为其他类型 - 这通常表明你没有正确处理它:

      devArr = (int*)cudaMalloc(M,sizeof(int));
      

      请参阅documentation。我们还需要正确处理max - 它当前是一个主机指针,我们需要该数据的设备副本。

    2. 你的内核也有点混乱。由于您只启动一个 CUDA 线程,因此您的 threadIdx.x 变量只会(永远)为零:

      int i = threadIdx.x;
      int a = dArr[i];
      

      但是内核中的for循环会起作用,我们只需要移动一些行。

    3. 虽然您还没有得到可编译、可运行的代码,但使用proper cuda error checking 总是一个好主意。我在下面的代码中添加了我自己的版本。

    以下代码解决了上述问题,并且似乎返回了一个正常的结果:

    #include <stdio.h>
    #include <stdlib.h> /* srand, rand */
    #include <time.h> /* time */
    #include <cuda.h>
    
    #define num 100000
    
    #define cudaCheckErrors(msg) \
        do { \
            cudaError_t __err = cudaGetLastError(); \
            if (__err != cudaSuccess) { \
                fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                    msg, cudaGetErrorString(__err), \
                    __FILE__, __LINE__); \
                fprintf(stderr, "*** FAILED - ABORTING\n"); \
                exit(1); \
            } \
        } while (0)
    
    
    int *arr,my_max = -1;
    
    int getRand() {
        double r1=rand()/(double)RAND_MAX; // Generates value between 0 & 1
        return (r1 * num) + 1;
    }
    void generateRandom(int M) {
        int i;
        for(i=0;i<M;i++) {
            arr[i] = getRand();
        }
    }
    __global__ void getMax(int M, int *dArr, int *dMax) {
        for(int i=0;i<M;i++) {
            int a = dArr[i];
            if(a > *dMax)
                *dMax = a;
            }
    }
    
    int main(int argc, char *argv[] ){
        if (argc == 2) {
            int M;
            int *devArr, *devMax;
            /* initialize random seed: */
            srand (time(NULL));
            M = atoi(argv[1]);
            //int arr[M];
            arr = (int*)calloc(M,sizeof(int));
            cudaMalloc(&devArr,M*sizeof(int));
            cudaCheckErrors("cudaMalloc 1 fail");
            cudaMalloc(&devMax,sizeof(int));
            cudaCheckErrors("cudaMalloc 2 fail");
            cudaMemset(devMax, 0, sizeof(int));
            cudaCheckErrors("cudaMemset fail");
            //printf("M = %d MAX = %d\n", M, RAND_MAX);
    
            generateRandom(M);
            cudaMemcpy(devArr, arr, M*sizeof(int), cudaMemcpyHostToDevice);
            cudaCheckErrors("cudaMemcpy 1 fail");
            getMax<<<1,1>>>(M, devArr, devMax);
            cudaMemcpy(&my_max, devMax, sizeof(int), cudaMemcpyDeviceToHost);
            cudaCheckErrors("cudaMemcpy 2/kernel fail");
            printf("Max value: %d \n", my_max);
    
        }
    
        else
            printf("Invalid arguments.");
    
        return 0;
    }
    

    在您了解了上述更改后,您会想回到我最初的建议并进行一些有组织的 CUDA 学习。那时,如果您想重新访问 max-finding,那么“好”的方法是使用适当的并行缩减技术。 “减少”是一种算法,它采用(大)数据集并返回单个数字或一小组数字作为结果。在数组中查找最大值是“减少”的一个示例。您可以通过学习 this 和完成 CUDA 并行缩减 sample code 来了解有关正确 CUDA 并行缩减的更多信息。

    【讨论】:

    • 您好,感谢您的解释,他们非常有帮助。我使用 threadIdx.x 的原因是因为我现在想让这段代码使用多个线程(我想我有点超前了)。你能告诉我如何做到这一点吗?我还尝试查看您链接到的示例代码,但我可以弄清楚在哪里查看代码。该页面列出了缩减 - CUDA Parallel Reduction 和支持的版本,但我不明白如何查看实际代码。
    • 我已经更新了我的原始帖子以包括我的第一次尝试,不幸的是它不是很成功。我会看看我能不能解决它,但如果你能提供一些非常有帮助的建议。
    • 对您的问题进行全面更改会使我的回答对未来的读者感到困惑。我建议提出一个新问题。 SO 并非旨在成为聊天会话或正在运行的对话。你的“新”代码仍然有明显的缺陷。例如,您要启动 64 个块,每个块 1 个线程。在这种情况下,threadIdx.x仍然始终为零。试图以这种方式整合 CUDA 知识非常乏味。为什么不使用我链接的一些材料?如果你这样做了,你就会明白为什么 threadIdx.x 在你的新代码中仍然总是为零。
    • 关于示例,请单击与您的操作系统对应的链接。然后,您将被带到该页面的一部分,该部分指示通常安装示例的位置以及您可以查看它们的位置以及如何在您的计算机上构建它们。
    猜你喜欢
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 2015-09-16
    相关资源
    最近更新 更多