将 C 程序转换为 CUDA(最大缩减)

Converting C Program To CUDA (Max Reduction)

我是 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): warning: a host variable "arr" cannot be directly read in >a device function

cudabasic.cu(23): warning: a host variable "max" cannot be directly read in >a device function

cudabasic.cu(24): warning: a host variable "arr" cannot be directly read in >a device function

cudabasic.cu(24): warning: a host variable "max" cannot be directly written >in a device function

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

#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): error: operand types are incompatible ("int" and "int *")

cudabasic.cu(25): error: a value of type "int" cannot be assigned to an >entity of type "int *"

有人可以指出正确的方向,告诉我如何最好地做到这一点吗?

我是 CUDA 的新手,正在努力掌握基础知识,所以如果我问或说的问题听起来过于简单,我深表歉意。

我能提供的最佳建议是学习一些入门级 CUDA 编程 material,例如 this。您的代码显示出不仅缺乏对 CUDA 的理解,而且缺乏对基本 C 概念的理解(例如变量必须在表达式中使用之前定义。)作为 CUDA 程序员,"flush" 您不了解如何编写正确的 C 或 C++ 代码。如果你 google 像 "gtc cuda intro" 或 "gtc cuda optimization" 这样的东西,你会发现很好的 CUDA 学习 material.

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

要使您提供的最后一个代码起作用,还需要几个步骤:

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

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

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

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

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

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

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

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

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

以下代码解决了上述问题,似乎 return 一个理智的结果:

#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 学习。到那时,如果您想重新访问最大值查找,那么 "good" 方法就是使用适当的并行缩减技术。 "reduction" 是一种采用(大)数据集和 return 单个数字或一小组数字作为结果的算法。求一个数组中的最大值就是一个 "reduction" 的例子。您可以通过研究 this and working through the CUDA parallel reduction sample code.

了解更多关于适当的 CUDA 并行缩减的信息