CUDA:页锁定内存(pinned memory)和按页分配内存(pageable memory )
CUDA架构而言,主机端的内存分为两种,一种是可分页内存(pageable memroy), 一种是页锁定内存(page-lock或 pinned)。
可分页内存是由操作系统API malloc()在主机上分配,页锁定内存是由CUDA函数cudaMallocHost()和cudaHostAlloc()在主机内存中分配,页锁定内存的重要属性是主机的操作系统将不会对这块内存进行分页和交换操作,确保该内存始终保留在物理内存中。
GPU知道页锁定内存的物理地址,可以通过直接内存访问(Direct Memory Access,DMA) 技术直接在主机和GPU之间复制数据,速率更快。由于每个页锁定内存都需要分配物理内存,并且这些内存不能交换到磁盘上,所以页锁定内存比使用标准malloc()分配的可分页内存更消耗内存空间。
可分页内存

数据在主机到设备的拷贝过程中,GPU驱动会先将数据拷贝到主机中的临时页锁定内存中,然后在由临时锁定内存拷贝到GPU内存中,从而完成数据从主机到设备的拷贝
页锁定内存

而页锁定内存,CUDA平台会直接申请一块页锁定内存,免去了从可分页内存中从CPU内存到临时页锁定内存的数据拷贝,从而达到数据拷贝效率。
host内存: 分为pageable memory和pinned memory
pageable memory: 通过操作系统API(malloc/new)分配的存储空间
pinned memory: 始终在物理内存中,不会被分配到低速的虚拟内存中,能够通过DMA加速与设备端通信;使用cudaMallocHost/cudaHostAlloc来分配pinned memory,cudaFreeHost来释放内存
使用Malloc分配的内存都是Pageable(交换页)的,而另一个模式就是Pinned(Page-locked),实质是强制让系统在物理内存中完成内存申请和释放的工作,不参与页交换,从而提高系统效率,需要使用cudaHostAlloc和cudaFreeHost(cudaMallocHost的内存也这样释放)来分配和释放。
使用pinned memory优点:主机端-设备端的数据传输带宽高;某些设备上可以通过zero-copy功能映射到设备地址空间,从GPU直接访问,省掉主存与显存间进行数据拷贝的工作;
使用pinned memory缺点:pinned memory 不可以分配过多:导致操作系统用于分页的物理内存变少, 导致系统整体性能下降;通常由哪个cpu线程分配,就只有这个线程才有访问权限;
页锁定内存的分配、操作和可分页内存的对比
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "iostream"
using namespace std;
float cuda_host_alloc_test(int size, bool up)
{
// 耗时统计
cudaEvent_t start, stop;
float elapsedTime;
cudaEventCreate(&start);
cudaEventCreate(&stop);
int *a, *dev_a;
// 主机上分配页锁定内存
// cudaError_t cudaStatus = cudaHostAlloc((void **)&a, size * sizeof(*a), cudaHostAllocDefault);
cudaError_t cudaStatus = cudaMallocHost((void **)&a, size * sizeof(*a));
if(cudaStatus != cudaSuccess)
{
printf("host alloc fail!\n");
return -1;
}
// 在设备上分配内存空间
cudaStatus = cudaMalloc((void **)&dev_a, size * sizeof(*dev_a));
if(cudaStatus != cudaSuccess)
{
fprintf(stderr, "cudaMalloc failed\n");
return -1;
}
cudaEventRecord(start, 0);
// 计时开始
for(int i = 0; i < 100; ++i)
{
// 主机拷贝到设备
cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(*a), cudaMemcpyHostToDevice);
if(cudaStatus != cudaSuccess)
{
fprintf(stderr, "cudaMemcpy host to device failed!!!\n");
return -1;
}
// 从设备拷贝到主机
cudaStatus = cudaMemcpy(a, dev_a, size * sizeof(*dev_a), cudaMemcpyDeviceToHost);
if(cudaStatus != cudaSuccess)
{
fprintf(stderr, "cudaMemcpy device to host failed!!!\n");
return -1;
}
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
cudaFreeHost(a);
cudaFree(dev_a);
cudaEventDestroy(start);
cudaEventDestroy(stop);
return (float)elapsedTime / 1000;
}
float cuda_host_Malloc_test(int size, bool up)
{
// 耗时统计
cudaEvent_t start, stop;
float elapsedTime;
cudaEventCreate(&start);
cudaEventCreate(&stop);
int *a, *dev_a;
// 主机上分配页锁定内存
a = (int *)malloc(size * sizeof(*a));
// 在设备上分配内存空间
cudaError_t cudaStatus = cudaMalloc((void **)&dev_a, size * sizeof(*dev_a));
if(cudaStatus != cudaSuccess)
{
fprintf(stderr, "cudaMalloc failed\n");
return -1;
}
cudaEventRecord(start, 0);
// 计时开始
for(int i = 0; i < 100; ++i)
{
// 主机拷贝到设备
cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(*a), cudaMemcpyHostToDevice);
if(cudaStatus != cudaSuccess)
{
fprintf(stderr, "cudaMemcpy host to device failed!!!\n");
return -1;
}
// 从设备拷贝到主机
cudaStatus = cudaMemcpy(a, dev_a, size * sizeof(*dev_a), cudaMemcpyDeviceToHost);
if(cudaStatus != cudaSuccess)
{
fprintf(stderr, "cudaMemcpy device to host failed!!!\n");
return -1;
}
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
free(a);
cudaFree(dev_a);
cudaEventDestroy(start);
cudaEventDestroy(stop);
return (float)elapsedTime / 1000;
}
int main()
{
float allocTime = cuda_host_alloc_test(100000, true);
cout << "页锁定内存: " << allocTime << " s" << endl;
float mallocTime = cuda_host_Malloc_test(100000, true);
cout << "可分页内存: " << mallocTime << " s" << endl;
getchar();
return 0;
}
运行结果:

PS:
- 在官方文档中关于
cudaMallocHost有这样一段描述:
On systems where pageableMemoryAccessUsesHostPageTables is true, cudaMallocHost may not page-lock the allocated memory.
可通过
cudaDeviceProp deviceProp;
cudaGetDeviceProperties(&deviceProp, dev);
结构体cudaDeviceProp中pageableMemoryAccessUsesHostPageTables字段来查询是否支持
cudaHostAlloc和cudaMallocHost之间的区别
cudaHostAlloc是CUDA运行时API中的一个函数,可以在主机端分配内存,并且将该内存分配为可由设备访问的内存。这意味着,如果使用cudaHostAlloc分配内存,那么可以在主机端和设备端直接传输数据,而无需使用额外的数据传输函数。 ---- 零拷贝cudaMallocHost是CUDA驱动程序API的一个函数,也可以在主机端分配内存,并且可以在主机和设备之间进行数据传输。与cudaHostAlloc不同的是,cudaMallocHost分配的内存只能由主机访问,并且必须使用额外的数据传输函数才能将数据传输到设备端。
因此,如果需要在主机端和设备端之间频繁传输数据,可以使用cudaHostAlloc来分配内存,但如果只需要在主机端进行一些计算,然后将结果传输到设备端,可以使用cudaMallocHost来分配内存
参考:https://blog.csdn.net/lilai619/article/details/109199235
https://wenku.csdn.net/answer/2c2ff61f776e4c25948eb1fd86ad28a6
CUDA:页锁定内存(pinned memory)和按页分配内存(pageable memory )的更多相关文章
- DELPHI声明一个指针变量,什么时候需要分配内存,什么时候不需要分配内存?
DELPHI声明一个指针变量,什么时候需要分配内存,什么时候不需要分配内存?比如我定义个变量 var p:Pchar;如果这个变量声明为全局变量,需要分配内存吗?分配为局部变量,需要分为内存吗?为什么 ...
- free如何知道释放内存长度:vs与glibc分配内存时编译器内部处理
鉴于网上这个资料实在太少,将以前整理过却未完全的一篇文章贴出来,希望大牛指正vs下内存管理方式.可联系gaoshiqiang1987@163.com vs分配内存 vs没有源码,编译器在分配内存时,分 ...
- linux设备驱动归纳总结(五):1.在内核空间分配内存【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-79134.html linux设备驱动归纳总结(五):1.在内核空间分配内存 xxxxxxxxxxxx ...
- c malloc分配内存
php中的内存分配有用类似emalloc这样的函数,emalloc实际上是C语言中的malloc的一层封装,php启动后,会向OS申请一块内存,可以理解为内存池,以后的php分配内存都是在这块内存池中 ...
- Unix系统编程()在堆上分配内存
在堆上分配内存:malloc和free 一般情况下,C程序使用malloc函数族在堆上分配和释放内存.较之brk和sbrk,这些函数具备不少优点: 属于C语言标准的一部分 更易于在多线程程序中使用 接 ...
- 【Linux开发】linux设备驱动归纳总结(五):1.在内核空间分配内存
linux设备驱动归纳总结(五):1.在内核空间分配内存 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- CUDA页锁定内存(Pinned Memory)
对CUDA架构而言,主机端的内存被分为两种,一种是可分页内存(pageable memroy)和页锁定内存(page-lock或 pinned).可分页内存是由操作系统API malloc()在主机上 ...
- 工作于内存和文件之间的页缓存, Page Cache, the Affair Between Memory and Files
原文作者:Gustavo Duarte 原文地址:http://duartes.org/gustavo/blog/post/what-your-computer-does-while-you-wait ...
- 高端内存映射之vmalloc分配内存中不连续的页--Linux内存管理(十九)
1 内存中不连续的页的分配 根据上文的讲述, 我们知道物理上连续的映射对内核是最好的, 但并不总能成功地使用. 在分配一大块内存时, 可能竭尽全力也无法找到连续的内存块. 在用户空间中这不是问题,因为 ...
- 使用Memory Analyzer tool(MAT)分析内存泄漏(二)
转载自:http://www.blogjava.net/rosen/archive/2010/06/13/323522.html 前言的前言 写blog就是好,在大前提下可以想说什么写什么,不像投稿那 ...
随机推荐
- 『Python底层原理』--Python对象系统探秘
Python是一种非常灵活的编程语言,它的灵活性很大程度上来自于它的对象系统. 在Python中,一切都是对象,这意味着无论是数字.字符串,还是我们自己定义的类的实例,它们在底层都遵循相同的规则. 本 ...
- CH340区别
CH340区别 CH340G USB转串⼝,推出时间最早,需外挂晶振,应⽤最⼴SOP16 CH340C USB转串⼝,内置晶振,引脚兼容CH340G SOP16 CH340E USB转串⼝,内置 ...
- Java微信小程序登录接口获取openid
根据官方文档,wx.login()的回调函数中,需要我们传递生成的用户登录凭证到code2accessToken的接口中 小程序登录方法 code2accessToken的方法中要求传入如下参数 ...
- 【Unity】投影矩阵和线性深度推导
[Unity]投影矩阵和线性深度推导 网络上有很多投影矩阵的推导,也有很多声称是基于 Unity 的,但和我的实测都不一致(现在看来是因为这些文章并不全面),此外有一些 Unity 本身的函数我也搞不 ...
- 小米手机/红米手机解锁BL详细教程
由于MIUI解锁风控提升,全部小米默认为支持解锁BootLoader的 但需要登录小米账号后,到开发者选项将设备和账号绑定0-1000小时,一般默认为168小时,部分新机实际几百小时,具体绑定时间,我 ...
- KaFka 安装与基本使用
学了有一段时间的大数据了,学习新的组件,大部分安装可以分为三步:上传解压,配置文件,启动. 分享一下我的kafka安装包: 链接:https://pan.baidu.com/s/1fbydwZwcYy ...
- wxpython 文件重命名报错提示os.rename WinError 2]系统找不到指定的文件‘
原因:重命名需要把文件路径带上 源码: for file in files: print(file) os.rename(file, file.replace(beforename, afternam ...
- Qt 重载QComboBox,实现右侧删除键
文章目录 Qt 重载QComboBox,实现右侧删除键 前言 方案 设置下拉箭头图标 设置QComboBox内容为空 自定义showPopup函数 定位鼠标 屏蔽鼠标右键 最终控制下拉代码 效果图 代 ...
- 如何编写Kubernetes的YAML(一)
什么是API对象 作为一个集群操作系统,Kubernetes 归纳总结了 Google 多年的经验,在理论层面抽象出了很多个概念,用来描述系统的管理运维工作,这些概念就叫做"API 对象&q ...
- JavaScript 图片弹窗
html <!-- 触发弹窗 - 图片改为你的图片地址 --> <img loading="lazy" id="myImg" src=&quo ...