首页 > 系统相关 >CUDA:页锁定内存(pinned memory)和按页分配内存(pageable memory )

CUDA:页锁定内存(pinned memory)和按页分配内存(pageable memory )

时间:2023-11-19 17:44:40浏览次数:34  
标签:pageable 主机 按页 cudaStatus dev stop 内存 memory

CUDA架构而言,主机端的内存分为两种,一种是可分页内存(pageable memroy), 一种是页锁定内存(page-lock或 pinned)
可分页内存是由操作系统API malloc()在主机上分配,页锁定内存是由CUDA函数cudaMallocHost()cudaHostAlloc()在主机内存中分配,页锁定内存的重要属性是主机的操作系统将不会对这块内存进行分页交换操作,确保该内存始终保留在物理内存中。

GPU知道页锁定内存的物理地址,可以通过直接内存访问(Direct Memory Access,DMA) 技术直接在主机和GPU之间复制数据,速率更快。由于每个页锁定内存都需要分配物理内存,并且这些内存不能交换到磁盘上,所以页锁定内存比使用标准malloc()分配的可分页内存更消耗内存空间。

可分页内存

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

页锁定内存

image
而页锁定内存,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;
}

运行结果:
image

PS:

  • 官方文档中关于cudaMallocHost 有这样一段描述:

On systems where pageableMemoryAccessUsesHostPageTables is true, cudaMallocHost may not page-lock the allocated memory.

可通过

    cudaDeviceProp deviceProp;
    cudaGetDeviceProperties(&deviceProp, dev);

结构体cudaDeviceProppageableMemoryAccessUsesHostPageTables字段来查询是否支持

  • cudaHostAlloccudaMallocHost之间的区别
  1. cudaHostAlloc是CUDA运行时API中的一个函数,可以在主机端分配内存,并且将该内存分配为可由设备访问的内存。这意味着,如果使用cudaHostAlloc分配内存,那么可以在主机端和设备端直接传输数据,而无需使用额外的数据传输函数。 ---- 零拷贝
  2. cudaMallocHost是CUDA驱动程序API的一个函数,也可以在主机端分配内存,并且可以在主机和设备之间进行数据传输。与cudaHostAlloc不同的是,cudaMallocHost分配的内存只能由主机访问,并且必须使用额外的数据传输函数才能将数据传输到设备端。
    因此,如果需要在主机端和设备端之间频繁传输数据,可以使用cudaHostAlloc来分配内存,但如果只需要在主机端进行一些计算,然后将结果传输到设备端,可以使用cudaMallocHost来分配内存

参考:https://blog.csdn.net/lilai619/article/details/109199235
https://wenku.csdn.net/answer/2c2ff61f776e4c25948eb1fd86ad28a6

标签:pageable,主机,按页,cudaStatus,dev,stop,内存,memory
From: https://www.cnblogs.com/whiteBear/p/17842246.html

相关文章

  • 一款.NET开源的小巧、智能、免费的Windows内存清理工具 - WinMemoryCleaner
    前言我们在使用Windows系统的时候经常会遇到一些程序不会释放已分配的内存,从而导致电脑变得缓慢。今天给大家推荐一款.NET开源的小巧、智能、免费的Windows内存清理工具:WinMemoryCleaner。使用Windows内存清理工具来优化内存,这样不必浪费时间去重新启动电脑。工具主要特点......
  • org.springframework.boot.loader.jar.jarfile memory leak
       org.springframework.boot.loader.jar.jarfilememoryleak这个问题可能是因为SpringBoot应用在运行时会将jar包解压到临时文件夹中,然后从这些解压的文件中加载类。如果您在应用运行时反复加载不同的jar包,就可能导致内存泄漏的问题。解决这个问题的方法是在应用......
  • 浪潮NF5280M6扩容内存出现CPU0_C1D0 Memory Device Disabled Memory
    浪潮NF5280M6服务器32G*8根(256G)内存扩容至12根(384G)扩容后提示内存告警查看内存详情在位12根,内存总容量320G,不是384G。查看系统日志:CPU0_C5D0MemoryDeviceDisabledMemory(CPU0-CH5-DIMM0)Disabled:DisabledRankDetail(MFR:NOTFOUND,PN:NOTFOUND,SN:NOTFOUND)-AssertCPU0_C5......
  • 如何使用Microsoft.KernelMemory来快速地构建和管理你的数据索引
    Microsoft.KernelMemory是一个开源的服务和插件,专门用于通过自定义的连续数据混合管道对数据集进行高效的索引。利用先进的嵌入和LLM,系统可以使用自然语言对索引的数据进行查询,同时提供引用和链接到原始来源。Microsoft.KernelMemory可以作为SemanticKernel,MicrosoftCopil......
  • JAVA应用OOM OutOfMemoryError排查方法分享
    JAVA应用OOMOutOfMemoryError排查方法分享本地IDE场景如果OOM能在本地IDE复现,那对于调试来说是再方便不过了.添加jvm参数,帮助排查问题#限制内存不要给太大,使得有问题的代码容易暴露并调试。#HeapDumpOnOutOfMemoryError的意义为发生oom的时候,导出一份堆内存的快照。根......
  • 完蛋!我被 Out of Memory 包围了! | 京东云技术团队
    是极致魅惑、洒脱自由的Javaheapspace?是知性柔情、温婉大气的GCoverheadlimitexceeded?是纯真无邪、活泼可爱的Metaspace?如果以上不是你的菜,那还有……刁蛮任性,无迹可寻的CodeCache!性感火辣、心思细腻的DirectMemory高贵冷艳,独爱你一人的OOMKiller!总有一款,能让你钟情!BUG选择......
  • A Tour Through TREE_RCU's Grace-Period Memory Ordering (翻译)
    原文:https://docs.kernel.org/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.htmlAugust8,2017ThisarticlewascontributedbyPaulE.McKenneyIntroductionThisdocumentgivesaroughvisualoverviewofhowTreeRCU'sgrace-periodmemoryorderi......
  • influxdb报错:cache-max-memory-size exceeded
    转载请注明出处:influxdb报错日志: 该错误信息表示InfluxDB引擎超过了缓存最大内存大小。这意味着InfluxDB的缓存使用量超出了配置的限制。要解决此问题,可以采取以下步骤来定位和解决:检查配置文件:首先,请确保InfluxDB配置文件中没有设置错误。在配置文件......
  • MemoryStream
    https://www.yisu.com/ask/25446927.html和文件流不同,MemoryStream类表示的是保存在内存中的数据流,由内存流封装的数据可以在内存中直接访问。内存一般用于暂时缓存数据以降低应用程序对临时缓冲区和临时文件的需要。继承与stream类(流),它的兄弟,也就是同样由stream派生的子类,有file......
  • Memory Bist
    SRAMC主要是对SRAM进行控制对于SRAM的逻辑,根据地址将数据存储到SRAM中,然后根据地址将SRAM中的数据读取出来如何测试Memory,生产工艺原因造成的问题,采用DFT或者Bist测试方法DFT-在代码中加入测试逻辑,之后通过这部分逻辑对芯片进行测试Whatismanufacturingtest?芯......