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就是好,在大前提下可以想说什么写什么,不像投稿那 ...
随机推荐
- c#securityexception不允许所请求的注册表访问权
开机自启动程序如下: if (!System.IO.File.Exists(filename)) throw new Exception("该文件不存在 ...
- php获取详细访客信息,获取访客IP,IP归属地,访问时间,操作系统,浏览器,移动端/PC端,环境语言,访问URL等信息
问题描述:需要获取访客访问网站信息 1.代码示例与说明: <?php header("Content-Type: text/html; charset=utf-8"); ...
- Vue3+TS项目无法识别自动导入提示
遇到问题 在写 Vue3 + TS 项目的时候,经常遇到写完一个新方法后,在组件使用的时候无法自动识别. 解决方案 Volar: Restart Vue Server 重新启动 Vue 服务
- k8s dashboard token 生成/获取
创建示例用户 在本指南中,我们将了解如何使用 Kubernetes 的服务帐户机制创建新用户.授予该用户管理员权限并使用与该用户绑定的承载令牌登录仪表板. 对于以下每个和的代码片段ServiceAcc ...
- K8S组件详解
K8S的控制平面.和工作节点是集群正常运行的核心,通过这两部分的协同工作,K8S才能够实现高效的容器编排.管理.和自动化运维. K8S Kubernetes(简称K8s),是一个开源的容器编排平台,用 ...
- MySQL-事务中的一致性读和锁定读的具体原理
前言 上一篇文章MySQL-InnoDB行锁中,提到过一致性锁定读和一致性非锁定读,这篇文章会详细分析一下在事务中时,具体是如何实现一致性的. 一致性读原理 start transaction和beg ...
- 选择排序--java进阶day06
1.选择排序 https://kdocs.cn/l/ciMkwngvaWfz?linkname=150996881 了解了选择排序之后,我们来找其中的规律 2.规律 选择排序就是一个元素和数组后续元素 ...
- Solana编译失败探讨(OpenEuler RISC-V版)
Solana 是 2017 年由 Anatoly Yakovenko 创立的开源项目,旨在打造高性能.去中心化且低成本的区块链平台2.它采用独特的 Proof of History(PoH)共识机 ...
- while(bug)
while(bug) { // 加了班也不一定写的完代码 // 写完了代码也不一定编译的过 // 编译过了也不一定没bug // 有了bug也不一定找的到 // 找到bug也不一定改的了 // 改了这 ...
- FDMemtable如何增加一条自身复制的记录
procedure TFrame_Bill.CopyARecord; var lAFDmemtable : TFDMemTable; begin {$REGION '增加一条复制的记录'} try l ...