2015-05-05 41 views
1

我是CUDA的新手,试图掌握基本知识,所以如果我要求或说出的内容听起来过于简单,我表示歉意。我用C编写了一些序列码,用于生成一个随机数组,然后在该数组中找到最大值。将C程序转换为CUDA(最大程度减少)

#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

我一派错误和发现该问题是,我是路过的全局变量的内核,因此该设备wasnt能够读取它。遵循在线建议,我试图通过使用指针而不是传递实际变量来解决此问题,但我仍然遇到错误。

#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新手,想要掌握基本知识,所以如果我要求或说出的内容听起来过于简单,我表示歉意。

回答

4

我可以提供的最好的建议是研究一些介绍性的CUDA编程材料,例如this。您的代码不仅显示了对CUDA的理解,而且还显示了对基本C概念的理解(例如变量必须在表达式中使用之前进行定义)。作为CUDA程序员,不要“冲洗”您如何编写正确的C或C++代码。如果你谷歌的东西像“gtc cuda intro”或“gtc cuda优化”,你会发现很好的CUDA学习资料。

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

为了让你呈现功能需要几个步骤的最后代码:

  1. 在CUDA,设备指针通常不能在主机代码间接引用,通常,主机指针不能用于设备代码。这意味着,你一般不应通过主机指针到设备的内核:

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

    你是在路上与您devArray(虽然你cudaMalloc没有设置正确),我们只需要固定arr问题修复它并通过额外的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并行减少的更多信息。

+0

嗨,谢谢你的解释,他们一直很有帮助。我之所以使用threadIdx.x的原因是因为我现在想让这个代码使用多线程(我想我已经比自己领先了一点)。你能告诉我这也可以做到吗? 另外我也试过看你链接到的示例代码,但我可以找出在哪里查看代码。页面列表减少 - CUDA并行减少和版本支持,但我不明白如何查看实际的代码。 – Lesha

+0

我已更新我的原始文章,包括我的第一次尝试,但不幸的是它非常成功。我会看看我能否解决它,但如果你能提供一些非常有用的建议。 – Lesha

+0

对您的问题进行批发更改会让我的答案令未来的读者感到困惑。我建议问一个新问题。 SO不打算成聊天会话或运行对话框。你的“新”代码仍然有明显的缺陷。例如,您将启动每个线程64个块。在这种情况下,'threadIdx.x'将*仍然*总是为零。试图以这种方式组合CUDA知识是非常乏味的。为什么不使用我链接的一些材料?如果你这样做,你会理解为什么'threadIdx.x'将*仍然*在你的新代码中始终为零。 –