两种方法使用零拷贝内存做简单的向量加和,并评估 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. java正则表达式——Greedy、Reluctant和Possessive

    测试1: package jichu; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Mai ...

  2. MySQL主从复制报错及解决方法

    mysql> show slave status \G *************************** 1. row *************************** Slave_ ...

  3. 【vue】创建一个vue前端项目,编译,发布

    npm: Nodejs下的包管理器. webpack: 它主要的用途是通过CommonJS的语法把所有浏览器端需要发布的静态资源做相应的准备,比如资源的合并和打包. vue-cli: 用户生成Vue工 ...

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

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

  5. 引用,引用形參,指针形參与指向指针的引用形參,内存泄露及free相关

    (另:关于"引用"更具体的讨论.见此.) 由做UVa133引发的一系列问题及讨论 1.引用类型    C++ Primer P51 引用就是对象的还有一个名字,使用多个变量名指向同 ...

  6. windows10密钥激活方法

    软件设计开发文档模板(国家标准)v1.1.rar 以上就是今天所分享Win10系统各个版本免费激活的windows密钥,希望win10专业版密钥可以帮助大家. 专业版:W269N-WFGWX-YVC9 ...

  7. 如何取出word文档里的图片

    在生活当中,Word办公是必不可少的.但是在工作中也会遇到一些麻烦,比如说如何取出word文档里的图片呢?有的人会通过复制粘贴,通过画图保存,可是这种方法未免太繁琐了吧.下面我就来分享一下我的经验. ...

  8. bat中的“多线程”处理代码

    大家都知道,批处理中运行的都是一步步单进程执行, 但如果进程执行比较慢,如PING一个不通的IP地址,那就会大大影响批处理程序的执行效率. 如下内容将简单举例,在WINDOWS下使用批处理做多进程并发 ...

  9. 【转】每天一个linux命令(40):wc命令

    原文网址:http://www.cnblogs.com/peida/archive/2012/12/18/2822758.html Linux系统中的wc(Word Count)命令的功能为统计指定文 ...

  10. Telnet 工具远程连接服务器

    1.启动tomcat 2. 使用telnet命令  打开CMD,输入telnet  localhost 8080 3.显示如下界面,说明已经连接成功 4. 复制如下代码到连接成功的界面 HEAD / ...