两种方法使用零拷贝内存做简单的向量加和,并评估 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. CTF之常见的两种关于word的信息隐藏技术

    一.利用word本身自带的文字隐藏功能 1.在word中输入文字 2.选中文字,单击右键,选择字体选项 3.单击字体选项后,单击隐藏,确定 查找隐藏信息 1.单击左上角WPS文字后,选择选项按钮单击 ...

  2. .hex文件和.bin文件的区别

    博客转之于:  http://mini.eastday.com/a/160627003502858.html HEX文件和BIN文件是我们经常碰到的2种文件格式.下面简单介绍一下这2种文件格式的区别: ...

  3. day9 python学习 文件的操作 读 写 seek

    文件的操作 1   文件的打开操作: 文件句柄 = open('文件路径', '模式') f=open('wangyakun','a+',encoding='utf-8') #文件名, 如果是绝对路径 ...

  4. 4.图像sensor的特性和驱动解析

    修改 摄像头SDK中支持的sensor需要做的事 例如:ar0130 --> ov9712 1.修改加载load3518e脚本的参数 vi /etc/profile ./load3518e -i ...

  5. 如何生成SPFILE文件

    1.spfile是Oracle9i之后引入的,目的是提高系统安全性.在Oracle8i下初始化参数文件为文本文件,可以使用文本编辑器进行编辑,当需要修改初始化参数时,需要在init.ora文件中修改, ...

  6. Cropperjs 插件知识点

    cropper.js 通过 canvas 实现图片裁剪,最后再通过 canvas 获取裁剪区域的图片base64串. 1. Container 容器 2. canvas 图片 3. crop 裁剪框

  7. CRM 2011 Plugin 知识的总结加代码解释

    1.??的使用,就是判断值是否为null,为null的话,给赋初值,否则就直接取值. decimal new_amount = 0; if (targetEntity.Contains("字 ...

  8. Spring Boot/Spring Cloud、ESB、Dubbo

    如何使用Spring Boot/Spring Cloud 实现微服务应用spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现. ...

  9. Openwrt TTL线刷

    1.接通串口,网线: 2.打开串口软件SecureCRT: 3.按复位键,不断地出现信息: 4.2秒内按任意键停下来,出现uboot> 5.输入httpd 6.打开网页,输入ip 7.开始更新, ...

  10. bzoj3932 任务查询系统

    Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分. 超级计算机中的任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si ...