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

▶ 源代码

 #include <stdio.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include "device_launch_parameters.h"
#include <helper_functions.h>
#include <helper_cuda.h> #define MEMORY_ALIGNMENT 4096
#define ALIGN_UP(x,size) ( ((size_t)x+(size-1))&(~(size-1)) ) __global__ void vectorAddGPU(float *a, float *b, float *c, int N)
{
int idx = blockIdx.x*blockDim.x + threadIdx.x;
if (idx < N)
c[idx] = a[idx] + b[idx];
} int main(int argc, char **argv)
{
printf("\n\tStart.\n"); // 设备检查
bool bMac;
cudaDeviceProp deviceProp;
cudaSetDevice();
cudaGetDeviceProperties(&deviceProp, );
if (CUDART_VERSION < || !deviceProp.canMapHostMemory)// CUDART_VERSION 为 CUDA Runtime API 版本,CUDA9.0 对应 9000
{
printf("\n\t CUDA Runtime API not support MapHostMemory.\n");
getchar();
return ;
}
cudaSetDeviceFlags(cudaDeviceMapHost);// MapHostFlag 功能正常,设置标志
#if defined(__APPLE__) || defined(MACOSX)// MacOS 系统不支持将普通堆内存设置为页锁定内存
bMac = true;
#else
bMac = false;
#endif
if (CUDART_VERSION < && !bMac)// 既不是 MacOS 系统,Runtime API 版本还不够高
{
printf("\n\tCUDA Runtime API not support cudaHostRegister function.\n");
getchar();
return ;
}
// 总体逻辑:
// CUDA Runtime version < 2200,不支持 MApHostMamory,退出
// CUDA Runtime version ∈[2200, 4000),且为 MAcOS 系统,使用 cudaHostAlloc() + cudaHostAllocMapped
// CUDA Runtime version ∈[2200, 4000),且不是 MAcOS 系统,退出
// CUDA Runtime version ≥ 4000,使用 malloc() + cudaHostRegister() // 内存申请
int nelem = ;
int bytes = nelem * sizeof(float);
float *a, *b, *c;
float *a_UA, *b_UA, *c_UA;
float *d_a, *d_b, *d_c;
if (CUDART_VERSION >= || bMac)
{
a_UA = (float *) malloc(bytes + MEMORY_ALIGNMENT); // 申请时多 4KB,用于滑动对齐,释放内存时以该指针为准
b_UA = (float *) malloc(bytes + MEMORY_ALIGNMENT);
c_UA = (float *) malloc(bytes + MEMORY_ALIGNMENT);
a = (float *) ALIGN_UP(a_UA, MEMORY_ALIGNMENT); // 指针指到 4K 对齐的位置上去,用于计算
b = (float *) ALIGN_UP(b_UA, MEMORY_ALIGNMENT);
c = (float *) ALIGN_UP(c_UA, MEMORY_ALIGNMENT);
cudaHostRegister(a, bytes, CU_MEMHOSTALLOC_DEVICEMAP); // 设置页锁定内存
cudaHostRegister(b, bytes, CU_MEMHOSTALLOC_DEVICEMAP);
cudaHostRegister(c, bytes, CU_MEMHOSTALLOC_DEVICEMAP);
}
else
{
cudaHostAlloc((void **)&a, bytes, cudaHostAllocMapped); // 使用函数 cudaHostAlloc() 一步到位
cudaHostAlloc((void **)&b, bytes, cudaHostAllocMapped);
cudaHostAlloc((void **)&c, bytes, cudaHostAllocMapped);
} // 初始化和内存映射
for (int n = ; n < nelem; n++)
{
a[n] = rand() / (float)RAND_MAX;
b[n] = rand() / (float)RAND_MAX;
}
cudaHostGetDevicePointer((void **)&d_a, (void *)a, );
cudaHostGetDevicePointer((void **)&d_b, (void *)b, );
cudaHostGetDevicePointer((void **)&d_c, (void *)c, ); // 调用内核
dim3 block(, , );
dim3 grid((unsigned int)ceil(nelem / (float)block.x));
vectorAddGPU << <grid, block >> > (d_a, d_b, d_c, nelem);
cudaDeviceSynchronize(); // 检查结果
float errorNorm, refNorm, ref, diff;
errorNorm = .f;
refNorm = .f;
for (int n = ; n < nelem; n++)
{
diff = c[n] - (ref = a[n] + b[n]);// ref 为 CPU 计算的和,diff 为 GPU 计算结果与 CPU 计算结果的差
errorNorm += diff*diff; // 向量 a + b 的两种计算结果的差的平方
refNorm += ref*ref; // 向量 a 与向量 b 的和的平方
}
errorNorm = (float)sqrt((double)errorNorm);
refNorm = (float)sqrt((double)refNorm);
printf("\n\tDifference between GPU and CPU is %f, %f%%\n", errorNorm, errorNorm / refNorm); // 清理工作
if (CUDART_VERSION >= || bMac)
{
cudaHostUnregister(a);
cudaHostUnregister(b);
cudaHostUnregister(c);
free(a_UA);
free(b_UA);
free(c_UA);
}
else
{
cudaFreeHost(a);
cudaFreeHost(b);
cudaFreeHost(c);
}
printf("\n\tFinish.\n");
getchar();
return ;
}

▶ 输出结果:

     Start.
Difference between GPU and CPU is 0.000000, 0.000000% Finish.

▶ 涨姿势

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

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

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

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

0_Simple__simpleZeroCopy的更多相关文章

随机推荐

  1. 配置搭建与使用redis

    redis单点.redis主从.redis哨兵 sentinel,redis集群cluster配置搭建与使用 redis是如今被互联网公司使用最广泛的一个中间件,我们打开GitHub搜索redis,边 ...

  2. 多边形面积问题(hdoj2036)

    杭电oj2036http://acm.hdu.edu.cn/showproblem.php?pid=2036 计算几何,求多边形的面积 只要记住这个公式: 如果逆时针给出点坐标,值为正, 如果顺时针给 ...

  3. 2018-2019-2 20165212 《网络对抗技术》Exp3 免杀原理与实践

    2018-2019-2 20165212 <网络对抗技术>Exp3 免杀原理与实践 一.实验内容 正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion ...

  4. hdu 5312 dp(背包)、二分图或其他姿势

    题意:给出一个二分图(不一定连通),问最多能加多少边,使它仍然是二分图 BC周年庆第四题,貌似终判再终判之后数据还是有问题``` 据说貌似可以用bitset搞,而且姿势优美是正解```然而我还是用的d ...

  5. List<Map<String, Integer>> 同key的value全部累加合并

    public static void main(String[] args){ List<Map<String,Object>> list1 = new ArrayList&l ...

  6. LG1397 [NOI2013]矩阵游戏

    题意 婷婷是个喜欢矩阵的小朋友,有一天她想用电脑生成一个巨大的n行m列的矩阵(你不用担心她如何存储).她生成的这个矩阵满足一个神奇的性质:若用F[i][j]来表示矩阵中第i行第j列的元素,则F[i][ ...

  7. day02 大型互联网架构演变历程笔记 和nigix和keepalived

    PS:1.单个进程内,有多个线程,可以共享进程的内存空间2. 进程和进程之间通信比较麻烦, 会涉及 序列化和反序列化 PS :以一个交易网站看网站是如何变大的,网站的发展!!!! PS:随着请求的增加 ...

  8. stenciljs 学习九 使用jsx

    可以使用jsx 方便组件的开发 基本格式 主要是render 函数 class MyComponent { render() { return ( <div> <h1>Hell ...

  9. nyoj 数独

    数独 时间限制:1000 ms  |  内存限制:65535 KB 难度:4   描述 数独是一种运用纸.笔进行演算的逻辑游戏.玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一 ...

  10. 关于事件委托的整理 ,另附bind,live,delegate,on区别

    随着DOM结构的复杂化和Ajax等动态脚本技术的运用导致如今的js界里最火热的一项技术应该是‘事件委托(event delegation)’了,什么是事件委托呢?小七给你娓娓道来,说白了就是想给子元素 ...