我人生中第一个真正的CUDA程序

    技术2022-05-19  24

       奋斗了2周,终于把CUDA的内存与显存数据拷贝、pitch、以及如何对显存的数组进行引用弄明白了。很开心。

       我是初次接触CUDA,学习CUDA是因为我了解到它的并行性使得数据量很大的程序的运行效率很高。我现在做的项目恰好需要这种高性能的并行运算。

       我开始编写了几个内存和显存之间普通的int、char、float和double型数据的拷贝进行测试,然后进行了一维数组在内存和显存之间数据的拷贝,随后就是二维数组了。

       在一维数组的定义和引用中,我开始是定义的时候指定数组的大小,后来改写为动态申请内存和显存。如下:

       定义一个一维数组:

       float *cpu_data; //内存中的一维数组指针

       int width;    //数组的宽度

       cpu_data = (float*)malloc(sizeof(float) * width);

       

       float *gpu_data; //显存中的一维数组指针

       cutilSafeCall( cudaMalloc((void**) &gpu_data, sizeof(float) * width)); //为数组申请显存空间

     

       for(int c = 0; c < width; ++c)   //内存数组的初始化

           cpu_data[c] = c;

       //下面进行数据的拷贝  将内存数据拷贝到显存对应数组中

       cutilSafeCall( cudaMemcpy( gpu_data, cpu_data, sizeof(float) * width, cudaMemcpyHostToDevice));

     

       对于二维数组,在显存上申请空间是最好使用cudaMallocPitch()函数,在此函数中有一个参数pitch,这个参数是补齐时显存数组每一行占得字节数。注意它是一个传出参数,其类型是size_t,也就是unsigned int类型。对于二维数组在内存和显存之间的拷贝最好使用函数

    cudaMemcpy2D(),这样效率高些.

       float *cpu_data;  //CPU上的数组

       float *gpu_data; //GPU上的数组

       int width, height; //数组的宽度(列数)和高度(行数)

       int pitch;       //GPU上数组的pitch

      

       printf("Input the width and height/n"); //输入数组的列数和行数

       scanf("%d%d", &width, &height);

     

       cpu_data = (float*)malloc(sizeof(float)*width*height); //申请内存空间

       cutilSafeCall( cudaMallocPitch( (void**) &gpu_data, &pitch, sizeof(float) * width, height)); //申请显存空间

       cutilSafeCall( cudaMemcpy2D( gpu_data, pitch, cpu_data, sizeof(float) * width, sizeof(float) * width,  height, cudaMemcpyHostToDevice));  //内存和显存数据进行拷贝

          下面是我写的最终的程序:

       /**************************************************************************    *矩阵相加的例子    ***************************************************************************/    #include <stdio.h>    #include <stdlib.h>    #include <cutil_inline.h>

       /************************************************************************     * Init CUDA                                                                ************************************************************************/    #if __DEVICE_EMULATION__

       bool InitCUDA(void){return true;}

       #else    bool InitCUDA(void)   {       int count = 0;       int i = 0;

          cudaGetDeviceCount(&count);       if(count == 0) {        fprintf(stderr, "There is no device./n");         return false;        }

         for(i = 0; i < count; i++) {          cudaDeviceProp prop;      if(cudaGetDeviceProperties(&prop, i) == cudaSuccess) {              if(prop.major >= 1) {                  break;               }        }       }     if(i == count) {             fprintf(stderr, "There is no device supporting CUDA./n");             return false;     }     cudaSetDevice(i);

        printf("CUDA initialized./n");     return true;    }

      #endif

    /***************************************************************************************************** *kernel函数,矩阵相加 ******************************************************************************************************/ __global__  void myKernel(const float *a, const float *b, float *c, size_t pitch, int height, int width) {         int i = blockIdx.y * blockDim.y + threadIdx.y;       int j = blockIdx.x * blockDim.x + threadIdx.x;         if(i < height && j < width)       c[i * pitch/ sizeof(float) + j] = a[i * pitch / sizeof(float) + j] + b[i * pitch / sizeof(float) + j];  }

    int main(int argc, char* argv[]) {      if(!InitCUDA())          return 0;     //CPU上的3个矩阵数组     float *cpu_A;          float *cpu_B;     float *cpu_C;     //GPU上的3个矩阵数组     float *gpu_A;     float *gpu_B;     float *gpu_C;       int width = 3; //矩阵的宽度(列数)     int height = 2;//矩阵的高度(行数)     size_t pitch;   //GPU数组的pitch     //为CPU上的矩阵数组申请内存空间     cpu_A = (float*)malloc(sizeof(float) * width * height);     cpu_B = (float*)malloc(sizeof(float) * width * height);     cpu_C = (float*)malloc(sizeof(float) * width * height);     //为GPU上的矩阵数组申请显存空间     cutilSafeCall( cudaMallocPitch((void**) &gpu_A, &pitch, sizeof(float) * width, height));     cutilSafeCall( cudaMallocPitch((void**) &gpu_B, &pitch, sizeof(float) * width,  height));     cutilSafeCall( cudaMallocPitch((void**) &gpu_C, &pitch, sizeof(float) * width,  height));      //将pitch打印      printf("The pitch is: %d/n", pitch);     //为CPU上的矩阵数组初始化      for(int r = 0; r < height; ++r){        for(int c = 0; c < width; ++c){          cpu_A[r * width + c] = r * c;          cpu_B[r * width + c] = r + c;          cpu_C[r * width + c] = 0.0;       }    }    //打印CPU上的矩阵数组      printf("/nCPU_A DATA/n");      for(int r = 0; r < height; ++r){     for(int c = 0; c < width; ++c){     printf("%f/t", cpu_A[r * width + c]);   }    printf("/n");  }   printf("/nCPU_B DATA/n");   for(int r = 0; r < height; ++r){         for(int c = 0; c < width; ++c){             printf("%f/t", cpu_B[r * width + c]);       }       printf("/n");   }   printf("/nCPU_C DATA/n");   for(int r = 0; r < height; ++r){          for(int c = 0; c < width; ++c){              printf("%f/t", cpu_C[r * width + c]);         }       printf("/n");    }  

       //将CPU上的矩阵数组cpu_A、cpu_B分别拷贝到GPU上的矩阵数组gpu_A、gpu_B中    cutilSafeCall( cudaMemcpy2D( gpu_A, pitch, cpu_A, sizeof(float) * width, sizeof(float) * width, height,       cudaMemcpyHostToDevice));     cutilSafeCall( cudaMemcpy2D( gpu_B, pitch, cpu_B, sizeof(float) * width, sizeof(float) * width, height, cudaMemcpyHostToDevice));      dim3 Dg(1, 2, 1);  //定义整个grid的维度和尺寸    dim3 Db(width, 1, 1); //定义每个block的维度和尺寸    myKernel<<<Dg, Db, 0>>>(gpu_A, gpu_B, gpu_C, pitch, height, width); //调用kernel函数     

    //将显存数组gpu_C拷贝会内存数组cpu_C     cutilSafeCall( cudaMemcpy2D( cpu_C, sizeof(float) * width, gpu_C, pitch, sizeof(float) * width, height,     cudaMemcpyDeviceToHost));   

      //打印CPU_C数组    printf("/nAfter change CPU_C DATA/n");       for(int r = 0; r < height; ++r){          for(int c = 0; c < width; ++c){              printf("%f/t", cpu_C[r * width + c]);           }      printf("/n");     }    //释放内存空间    free(cpu_A);    free(cpu_B);    free(cpu_C);    //释放显存空间    cutilSafeCall( cudaFree(gpu_A));    cutilSafeCall( cudaFree(gpu_B));    cutilSafeCall( cudaFree(gpu_C));     //退出CUDA      CUT_EXIT(argc, argv);

         return 0;   }

     

          2周的编程(当然是闲暇时间,呵呵),我终于开始了我的CUDA编程之路。

          路漫漫其修远兮,吾将上下而求索。

     

     

        


    最新回复(0)