第一个CUDA程序-addVector

本文通过对比CPU与GPU在处理大规模浮点数数组加法运算时的性能差异,展示了GPU并行计算的优势。实验中,使用了CUDA编程模型在GPU上实现了向量加法,并与CPU版本进行了对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要通过对两个浮点数组中的数据进行相加,并将其结果放入第三个数组中。其算法分别在CPU、GPU上分别执行,并比较了所需时间,强烈感受到GPU的并行计算能力。这里,每个数组的元素大小为30000000个。

一、实现代码

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <time.h>  
  4. #include <math.h>  
  5.   
  6. // For the CUDA runtime routines (prefixed with "cuda_")  
  7. #include <cuda_runtime.h>  
  8.   
  9. //该函数声明为了__global__,表示由GPU调用执行.  
  10. //其功能为将数组pA、pB中对应位置的数据相加,并将结果放入数组pC的对应位置上  
  11. //每个数组的索引大小为size  
  12. __global__  
  13. void add(const float * pA, const float * pB, float * pC, unsigned int size)  
  14. {  
  15.     int index = blockIdx.x * blockDim.x + threadIdx.x;      //计算当前数组中的索引  
  16.     if (index < size)                                        //确保是一个有效的索引  
  17.         pC[index] = pA[index] + pB[index];  
  18.   
  19. }  
  20.   
  21. int main()  
  22. {  
  23.     unsigned int numElement = 30000000;  
  24.     int totalSize = sizeof(float)* numElement;  
  25.   
  26.     //init  
  27.     float *pA = (float*)malloc(totalSize);  
  28.     float *pB = (float*)malloc(totalSize);  
  29.     float *pC = (float*)malloc(totalSize);  
  30.   
  31.     for (int i = 0; i < numElement; ++i)  
  32.     {  
  33.         *(pA + i) = rand() / (float)RAND_MAX;;  
  34.         *(pB + i) = rand() / (float)RAND_MAX;  
  35.     }  
  36.   
  37.     //cpu segment  
  38.   
  39.     //begin use cpu comput  
  40.     clock_t startTime, endTime;  
  41.     startTime = clock();  
  42.     for (int i = 0; i < numElement; ++i)  
  43.     {  
  44.         *(pC + i) = *(pA + i) + *(pB + i);  
  45.     }  
  46.     endTime = clock();  
  47.     //end use cpu comput  
  48.   
  49.     printf("use cpu comput finish!\n");  
  50.     printf("use total time = %fs\n", (endTime - startTime) / 1000.f);  
  51.     printf("\n\n");  
  52.   
  53.   
  54.     //gpu segment  
  55.     float *pD, *pE, *pF;  
  56.     cudaError_t err = cudaSuccess;  
  57.   
  58.     //malloc memory  
  59.     err = cudaMalloc(&pD, totalSize);  
  60.     if (err != cudaSuccess)  
  61.     {  
  62.         printf("call cudaMalloc fail for pD.\n");  
  63.         exit(1);  
  64.     }  
  65.   
  66.     err = cudaMalloc(&pE, totalSize);  
  67.     if (err != cudaSuccess)  
  68.     {  
  69.         printf("call cudaMalloc fail for pE.\n");  
  70.         exit(1);  
  71.     }  
  72.   
  73.     err = cudaMalloc(&pF, totalSize);  
  74.     if (err != cudaSuccess)  
  75.     {  
  76.         printf("call cudaMalloc fail for pF.\n");  
  77.         exit(1);  
  78.     }  
  79.   
  80.     //copy data  from pA pB pC to pD pE pF  
  81.     err = cudaMemcpy(pD, pA, totalSize, cudaMemcpyHostToDevice);  
  82.     if (err != cudaSuccess)  
  83.     {  
  84.         printf("call cudaMemcpy fail for pA to pD.\n");  
  85.         exit(1);  
  86.     }  
  87.   
  88.     err = cudaMemcpy(pE, pB, totalSize, cudaMemcpyHostToDevice);  
  89.     if (err != cudaSuccess)  
  90.     {  
  91.         printf("call cudaMemcpy fail for pB to pE.\n");  
  92.         exit(1);  
  93.     }  
  94.   
  95.   
  96.     //begin use gpu comput  
  97.     startTime = clock();  
  98.     int threadPerBlock = 1024;  
  99.     int numBlock = (numElement - 1) / threadPerBlock + 1;  
  100.     add << <numBlock, threadPerBlock >> >(pD, pE, pF, numElement);  
  101.   
  102.     err = cudaGetLastError();  
  103.     if (err != cudaSuccess)  
  104.     {  
  105.         printf("use gpu comput fail!\n");  
  106.         exit(1);   
  107.     }  
  108.   
  109.     endTime = clock();  
  110.     printf("use gpu comput finish!\n");  
  111.     printf("use time : %fs\n",(endTime - startTime) / 1000.f);  
  112.     //end use gpu comput  
  113.   
  114.   
  115.     //copu data from device to host  
  116.     err = cudaMemcpy(pC, pF, numElement, cudaMemcpyDeviceToHost);  
  117.     if (err != cudaSuccess)  
  118.     {  
  119.         printf("call cudaMemcpy form pF to pC fail.\n");  
  120.         exit(1);  
  121.     }  
  122.   
  123.     //check data  
  124.     for (int i = 0; i < numElement; ++i)  
  125.     {  
  126.         if (fabs(pA[i] + pB[i] - pC[i]) > 1e-5)  
  127.         {  
  128.             printf("%f + %f != %f\n",pA[i],pB[i],pC[i]);  
  129.         }  
  130.     }  
  131.   
  132.     //释放设备上的内存  
  133.     cudaFree(pD);  
  134.     cudaFree(pE);  
  135.     cudaFree(pF);  
  136.   
  137.     //在程序退出前,调用该函数重置该设备,使驱动去清理设备状态,并且在程序退出前所有的数据将被刷出。  
  138.     err = cudaDeviceReset();  
  139.     if (err != cudaSuccess)  
  140.     {  
  141.         printf("call cudaDeviceReset fail!\n");  
  142.         exit(1);  
  143.     }  
  144.   
  145.     free(pA);  
  146.     free(pB);  
  147.     free(pC);  
  148.   
  149.     getchar();  
  150.     return 0;  
  151. }  


二、运行结果






三、部分CUDA函数说明


1、cudaMalloc

[cpp]  view plain  copy
  1. __host__  cudaError_t  cudaMalloc(void **devPtr, size_t size);  
        该函数主要用来分配设备上的内存(即显存中的内存)。该函数被声明为了__host__,即表示被host所调用,即在cpu中执行的代码所调用。
        返回值:为cudaError_t类型,实质为cudaError的枚举类型,其中定义了一系列的错误代码。如果函数调用成功,则返回cudaSuccess。
        第一个参数,void ** 类型,devPtr:用于接受该函数所分配的内存地址
        第二个参数,size_t类型,size:指定分配内存的大小,单位为字节

2、cudaFree

[cpp]  view plain  copy
  1. __host__  cudaError_t  cudaFree(void *devPtr);  
        该函数用来释放先前在设备上申请的内存空间(通过cudaMalloc、cudaMallocPitch等函数),注意,不能释放通过标准库函数malloc进行申请的内存。
        返回值:为错误代码的类型值
        第一个参数,void**类型,devPtr:指向需要释放的设备内存地址

3、cudaMemcpy

[cpp]  view plain  copy
  1. __host__ cudaError_t  cudaMemcpy(void *dst, const void *src, size_t count, enum cudaMemcpyKind kind);  
        该函数主要用于将不同内存段的数据进行拷贝,内存可用是设备内存,也可用是主机内存
        第一个参数,void*类型,dst:为目的内存地址
        第二个参数,const void *类型,src:源内存地址
        第三个参数,size_t类型,count:将要进行拷贝的字节大小
        第四个参数,enum cudaMemcpyKind类型,kind:拷贝的类型,决定拷贝的方向
cudaMemcpyKind类型如下:
[cpp]  view plain  copy
  1. enum __device_builtin__ cudaMemcpyKind  
  2. {  
  3.     cudaMemcpyHostToHost          =   0,      /**< Host   -> Host */  
  4.     cudaMemcpyHostToDevice        =   1,      /**< Host   -> Device */  
  5.     cudaMemcpyDeviceToHost        =   2,      /**< Device -> Host */  
  6.     cudaMemcpyDeviceToDevice      =   3,      /**< Device -> Device */  
  7.     cudaMemcpyDefault             =   4       /**< Default based unified virtual address space */  
  8. };  
cudaMemcpyKind决定了拷贝的方向,即是从主机的内存拷贝至设备内存,还是将设备内存拷贝值主机内存等。cudaMemcpy内部根据拷贝的类型(kind)来决定调用以下的某个函数:
[cpp]  view plain  copy
  1. ::cudaMemcpyHostToHost,   
  2. ::cudaMemcpyHostToDevice,   
  3. ::cudaMemcpyDeviceToHost,  
  4. ::cudaMemcpyDeviceToDevice  

4、cudaDeviceReset

[cpp]  view plain  copy
  1. __host__ cudaError_t  cudaDeviceReset(void);  
        该函数销毁当前进程中当前设备上所有的内存分配和重置所有状态,调用该函数达到重新初始该设备的作用。应该注意,在调用该函数时,应该确保该进程中其他host线程不能访问该设备!


Last:源程序下载地址
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值