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就是好,在大前提下可以想说什么写什么,不像投稿那 ...
随机推荐
- 如何修改JSONObject 的值
问 题 { "result": { "total": "3", "shops": [ { "shopId&qu ...
- QT5笔记:24. 自定义对话框以及模态 调用
创建窗口时 窗口对象为QDialog 调用方法为exec(); int res = setSizeDialog->exec();//模态窗口 (不必要)exec可以获取到调用的是对话框的 QDi ...
- 项目愿景 (Product Vision)、产品目标 (Product Goal) 、Sprint目标 (Sprint Goal) 及 示例
愿景(Vision) 是制定业务目标(Business Goal)的基础,后者为确定正确的产品目标 (Product Goal) 创造了环境.同样,每个产品目标作为识别有用的冲刺目标的基础.换句话说, ...
- 解决黑群晖 Docker 日志八小时时间差的有效方法
步骤一:登录黑群晖控制台 首先,我们需要登录到黑群晖控制台.可以通过SSH登录,或是直接在黑群晖控制台界面上操作. 步骤二:停止相关的Docker容器 在解决时间差问题之前,我们需要停止相关的Dock ...
- springboot+vue项目:工具箱
常用账号管理:工作相关账号.游戏账号.各平台账号 加班调休管理:公司没有对应的系统,需要自己记录加班调休情况. 待办事项:方便记录待办,以提醒还有哪些事情没有办理. 待实现功能: 1.点击侧边栏菜单, ...
- Git错误合集 | git工作上遇到的那些报错
前言 我总是在git提交的时候,遇到一些奇奇怪怪的问题.有时候居然还会碰上第二次. 记住这些"绊脚石",下回不摔跤. 目录 git index损坏 一.git index损坏 报错 ...
- 【Abaqus热分析】热膨胀系数设置
来源:帮助文档
- Vulnhub-sundown
总结:该靶机是一个wordpress管理系统,需要信息收集得到插件信息,然后搜索插件漏洞,得到一个文件包含exp,利用其得到一个普通用户,利用hydra爆破密码然后ssh连接,信息收集得到一个数据库配 ...
- Manus,没有秘密「注解版」
近来Manus走红,「争论」不断,我也在前文<Manus爆火,是硬核还是营销?>中阐述过自Manus发布后,行业讨论以及开源复刻的信息,以及我们如何结合蚂蚁图计算(TuGraph)技术,实 ...
- 【Azure Fabric Service】分享使用Visual Studio 2022发布中国区Service Fabric服务应用的办法
问题描述 使用Visual Studio 2022如何发布Service Fabric到中国区云服务呢? 因为使用VS2022中的插件无法创建Service Fabric Cluster服务. 那么, ...