两种方法使用零拷贝内存做简单的向量加和,并评估 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. 关于self和super在oc中的疑惑与分析 (self= [super init])

    这个问题貌似很初级,但很容易让人忽略,me too .直到在一次面试时被问到,稀里糊涂的回答了下.实在惭愧, 面试一定都是很注重 基础的,不管高级还是初级. 虽然基础好跟基础不好都可以写代码,网上那么 ...

  2. POJ 3311 Hie with the Pie 先用floyd预处理,再状态压缩

    下面是别人的解题报告链接: http://blog.csdn.net/accry/article/details/6607703 下面是我的代码,我觉得链接中的代码有一点小问题,也许是我想错了吧. # ...

  3. Thrift 个人实战--Thrift 网络服务模型(转)

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

  4. 实习第二天-java参数传递-精华在文章最后2句话

    对于基本类型的传递,我们很容易理解,而对于对象,总让人感觉是按引用传递,看下面的程序: public class ObjectRef { //基本类型的参数传递 public static void ...

  5. nexage video asset tag

    video  ad can't show InLine  must  match   the example ,and   xml content is  Case Sensitive https:/ ...

  6. 使用 C# 编写简易 ASP.NET Web 服务器 ---- 模拟IIS的处理过程

    如果你想获得更好的阅读体验,可以前往我在 github 上的博客进行阅读,http://lcomplete.github.io/blog/2013/07/16/use-csharp-write-asp ...

  7. 充电 IC 对 0V 电池充电问题

    只讨论锂聚合物电池,由于设置漏电流原因,有些锂电池会放电到 0V(正常是不应该的). 但是在时候就是会发电到 0V,这里就要考虑充电 IC 是否有对 0V 电池充电的功能,还有保护 IC 是否允许 0 ...

  8. python 网络编程要点

    From http://www.zhihu.com/question/19854853 Python网络编程是一个很大的范畴,个人感觉需要掌握的点有:1. 如何使用Python来创建socket, 如 ...

  9. Microsoft Dynamics CRM 2011 Plugin中PluginExecutionContext.InputParameters["Target"]中的Target是从哪来的?

    图 1 如图1,CRM编程是一个请求响应模型,任何操作都是通过一个Request发起,一个Response返回结果,这个模型简单实用.所有请求类都是继承OrganizationRequest,所有响应 ...

  10. ML(2): 术语及算法分类汇总

    机器学习术语 归纳总结机器学习相关的基本术语,以一批西瓜的数据为例,例如:(色泽=青绿:根蒂=蜷缩:敲声=浊响),(色泽=乌黑:根蒂=稍蜷:敲声=沉闷),(色泽=浅白:根蒂=硬挺:敲声=清脆)... ...