爨爨爨好

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

两种方法使用零拷贝内存做简单的向量加和,并评估 GPU 计算结果与 CPU 计算结果的差。

▶ 源代码

  1 #include <stdio.h>
  2 #include <cuda.h>
  3 #include <cuda_runtime.h>
  4 #include "device_launch_parameters.h"
  5 #include <helper_functions.h>
  6 #include <helper_cuda.h>
  7 
  8 #define MEMORY_ALIGNMENT  4096
  9 #define ALIGN_UP(x,size) ( ((size_t)x+(size-1))&(~(size-1)) )
 10 
 11 __global__ void vectorAddGPU(float *a, float *b, float *c, int N)
 12 {
 13     int idx = blockIdx.x*blockDim.x + threadIdx.x;
 14     if (idx < N)
 15         c[idx] = a[idx] + b[idx];
 16 }
 17 
 18 int main(int argc, char **argv)
 19 {
 20     printf("\n\tStart.\n");
 21 
 22     // 设备检查
 23     bool bMac;
 24     cudaDeviceProp deviceProp;
 25     cudaSetDevice(0);
 26     cudaGetDeviceProperties(&deviceProp, 0);
 27     if (CUDART_VERSION < 2020 || !deviceProp.canMapHostMemory)// CUDART_VERSION 为 CUDA Runtime API 版本,CUDA9.0 对应 9000
 28     {
 29         printf("\n\t CUDA Runtime API not support MapHostMemory.\n");
 30         getchar();
 31         return 1;
 32     }
 33     cudaSetDeviceFlags(cudaDeviceMapHost);// MapHostFlag 功能正常,设置标志
 34 #if defined(__APPLE__) || defined(MACOSX)// MacOS 系统不支持将普通堆内存设置为页锁定内存
 35     bMac = true;
 36 #else
 37     bMac = false;
 38 #endif
 39     if (CUDART_VERSION < 4000 && !bMac)// 既不是 MacOS 系统,Runtime API 版本还不够高
 40     {
 41         printf("\n\tCUDA Runtime API not support cudaHostRegister function.\n");
 42         getchar();
 43         return 1;
 44     }
 45     // 总体逻辑:
 46     // CUDA Runtime version < 2200,不支持 MApHostMamory,退出
 47     // CUDA Runtime version ∈[2200, 4000),且为 MAcOS 系统,使用 cudaHostAlloc() + cudaHostAllocMapped
 48     // CUDA Runtime version ∈[2200, 4000),且不是 MAcOS 系统,退出
 49     // CUDA Runtime version ≥ 4000,使用 malloc() + cudaHostRegister()
 50 
 51     // 内存申请
 52     int nelem = 1048576;
 53     int bytes = nelem * sizeof(float);
 54     float *a, *b, *c;
 55     float *a_UA, *b_UA, *c_UA;
 56     float *d_a, *d_b, *d_c;
 57     if (CUDART_VERSION >= 4000 || bMac)
 58     { 
 59         a_UA = (float *) malloc(bytes + MEMORY_ALIGNMENT);      // 申请时多 4KB,用于滑动对齐,释放内存时以该指针为准
 60         b_UA = (float *) malloc(bytes + MEMORY_ALIGNMENT);
 61         c_UA = (float *) malloc(bytes + MEMORY_ALIGNMENT);
 62         a = (float *) ALIGN_UP(a_UA, MEMORY_ALIGNMENT);         // 指针指到 4K 对齐的位置上去,用于计算
 63         b = (float *) ALIGN_UP(b_UA, MEMORY_ALIGNMENT);
 64         c = (float *) ALIGN_UP(c_UA, MEMORY_ALIGNMENT);
 65         cudaHostRegister(a, bytes, CU_MEMHOSTALLOC_DEVICEMAP);  // 设置页锁定内存
 66         cudaHostRegister(b, bytes, CU_MEMHOSTALLOC_DEVICEMAP);
 67         cudaHostRegister(c, bytes, CU_MEMHOSTALLOC_DEVICEMAP);
 68     }
 69     else
 70     {
 71         cudaHostAlloc((void **)&a, bytes, cudaHostAllocMapped); // 使用函数 cudaHostAlloc() 一步到位
 72         cudaHostAlloc((void **)&b, bytes, cudaHostAllocMapped);
 73         cudaHostAlloc((void **)&c, bytes, cudaHostAllocMapped);
 74     }
 75     
 76     // 初始化和内存映射
 77     for (int n = 0; n < nelem; n++)
 78     {
 79         a[n] = rand() / (float)RAND_MAX;
 80         b[n] = rand() / (float)RAND_MAX;
 81     }
 82     cudaHostGetDevicePointer((void **)&d_a, (void *)a, 0);
 83     cudaHostGetDevicePointer((void **)&d_b, (void *)b, 0);
 84     cudaHostGetDevicePointer((void **)&d_c, (void *)c, 0);
 85 
 86     // 调用内核
 87     dim3 block(256, 1, 1);
 88     dim3 grid((unsigned int)ceil(nelem / (float)block.x)); 
 89     vectorAddGPU << <grid, block >> > (d_a, d_b, d_c, nelem);
 90     cudaDeviceSynchronize();
 91 
 92     // 检查结果
 93     float errorNorm, refNorm, ref, diff;
 94     errorNorm = 0.f;
 95     refNorm = 0.f;
 96     for (int n = 0; n < nelem; n++)
 97     {
 98         diff = c[n] - (ref = a[n] + b[n]);// ref 为 CPU 计算的和,diff 为 GPU 计算结果与 CPU 计算结果的差
 99         errorNorm += diff*diff;           // 向量 a + b 的两种计算结果的差的平方
100         refNorm += ref*ref;               // 向量 a 与向量 b 的和的平方
101     }
102     errorNorm = (float)sqrt((double)errorNorm);
103     refNorm = (float)sqrt((double)refNorm);
104     printf("\n\tDifference between GPU and CPU is %f, %f%%\n", errorNorm, errorNorm / refNorm);
105 
106     // 清理工作
107     if (CUDART_VERSION >= 4000 || bMac)
108     {
109         cudaHostUnregister(a);
110         cudaHostUnregister(b);
111         cudaHostUnregister(c);
112         free(a_UA);
113         free(b_UA);
114         free(c_UA);
115     }
116     else
117     {
118         cudaFreeHost(a);
119         cudaFreeHost(b);
120         cudaFreeHost(c);
121     }
122     printf("\n\tFinish.\n");
123     getchar();
124     return 0;
125 }

▶ 输出结果:

1     Start.
2     Difference between GPU and CPU is 0.000000, 0.000000%
3 
4     Finish.

 

▶ 涨姿势

● 两种使用零拷贝内存的方法,在代码的逻辑部分进行了说明

● 向上取整的宏函数,只对分母(size)为 2 的整数次幂的情况有效。

1 #define ALIGN_UP(x,size) ( ((size_t)x+(size-1))&(~(size-1)) )

  e.g. size == 4096,则 ~ (size - 1) == 11111111 11111111 11110000 000000002,将其作为模板进行按位且操作,等价于取不低于 4096 的高位。

 

posted on 2017-12-09 12:20  爨爨爨好  阅读(454)  评论(0编辑  收藏  举报