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

时间:2015-05-05 00:46:04

标签: c arrays 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):警告:主变量&#34; arr&#34;无法直接读取&gt;设备功能

     

cudabasic.cu(23):警告:主变量&#34; max&#34;无法直接读取&gt;设备功能

     

cudabasic.cu(24):警告:主变量&#34; arr&#34;无法直接读取&gt;设备功能

     

cudabasic.cu(24):警告:主变量&#34; max&#34;不能直接写入&gt;在设备功能

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

#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):错误:操作数类型不兼容(&#34; int&#34;和&#34; int *&#34;)

     

cudabasic.cu(25):错误:类型&#34; int&#34;的值无法分配到&gt;类型&#34; int *&#34;

的实体

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

我是CUDA的新手并试图掌握基本知识,所以如果我要求或说的话听起来过于简单,我会道歉。

1 个答案:

答案 0 :(得分:4)

我能提供的最好的建议是学习一些介绍性的CUDA编程材料,例如this。你的代码不仅缺乏对CUDA的理解,而且缺乏对基本C概念的理解(比如变量必须在表达式中使用之前定义。)作为一名CUDA程序员,不要&#34;冲洗&#34;您对如何编写正确的C或C ++代码的了解。如果你谷歌像&#34; gtc cuda intro&#34;或者&#34; gtc cuda优化&#34;你会找到好的CUDA学习材料。

您遵循的方法,即采用单线程C / C ++代码,并使用单个CUDA线程将其转换为运行,可能会给您一些关于&#34;学习CUDA& #34;但是你并没有真正解决任何重要的概念 - 它会在你现在正在努力解决的代码中显示出来。

要获得您提供的最后一个代码,需要执行以下几个步骤:

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

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

    您正在使用arr修复devArray问题(尽管您的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总是一个好主意。我已将自己的版本添加到以下代码中。

  4. 以下代码解决了上述问题,似乎返回了一个明智的结果:

    #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,那么&#34; good&#34;这样做的方法是使用适当的平行减少技术。 A&#34;减少&#34;是一种算法,它采用(大)数据集并返回一个数字或一小组数字作为结果。在数组中查找最大值是&#34;减少&#34;的示例。通过研究this并完成CUDA并行缩减sample code,您可以了解有关正确的CUDA并行缩减的更多信息。