CUDA_主机内存



主机内存

系统中被CPU访问的内存,分为两种类型:可分页内存(pageable memory,通常应用中默认使用)和页锁定内存(page-locked或者pinned)。api

可分页内存即为经过操做系统api(malloc(), new())分配的存储器空间;而页锁定内存始终不会被分配到低速的虚拟内存中,可以保证存在于物理内存中,而且可以经过DMA加速与设备端的通讯。缓存

为了让硬件使用DMA,操做系统容许主机内存进行页锁定,而且由于性能缘由,CUDA包含了开发者使用这些操做系统工具的API。页锁定后的且映射为cuda直接访问的锁定的内存容许如下几点:app

  • 更快的传输性能;
  • 异步的复制操做(在必要的内存复制结束以前内存复制返回控制给调用者;GPU复制操做与cpu并行的执行);
  • 映射锁页内存能够被cuda内核直接访问

主机端锁页内存

使用pinned memory有不少好处:能够达到更高的主机-设备端的数据传送带宽,若是页锁定内存以write-commbined方式分配,带宽更高;某些设备支持DMA功能,在执行内核函数的同时利用pinned memory进行主机端与设备端之间的通讯;在某些设备上,pinned memory还能够经过zero-copy功能映射到设备地址空间,从GPU直接访问,这样就不用在主存和显存之间进行数据传输。异步

分配锁页内存

经过cudaHostAlloc()和cudaFreeHost()来分配和释放pinned memory。函数


portable memory/可共享锁页内存

在使用cudaHostAlloc分配页锁定内存时,加上cudaHostAllocPortable标志,可使多个CPU线程经过共享一块页锁定内存,从而实现cpu线程间的通讯。在默认状况下,pinned memory由哪一个cpu线程分配,就只有该CPU线程才能访问这块空间。而经过portable memory则可让控制不一样GPU的几个CPU线程共享同一块pinned memory,减小CPU线程间的数据传输和通讯。工具


write-combined memory/写结合锁页内存

当CPU对一块内存中的数据进行处理时,会将这块内存上的数据缓存到CPU的L一、L2 Cache中,而且须要监视这块内存中数据的更改,以保证缓存的一致性。性能

通常状况下,这种机制能够减小CPU对内存的访问,但在“CPU生产数据,GPU消费数据”模型中,CPU只须要写这块内存数据便可。此时不须要维护缓存一致性,对内存的监视反而会下降性能。操作系统

经过write-combined memory,就能够不使用CPU的L一、L2 Cache对一块pinned memory中的数据进行缓冲,而将Cache资源留给其余程序使用。线程

write-combined memory在PCI-e总线传输期间不会被来自CPU的监视打断,能够将主机端-设备端传输带宽提升多达40%。指针

在调用cudaHostAlloc()时加上cudaHostAllocWriteCombined标志,就能够将一块pinned memory声明为write-combined memory.

因为对write-combined memory的访问没有缓存,CPU从write-combined memory上读数据时速度会有所降低。因最好只将CPU端只写的数据存放在write-combined memory中。


mapped memory/映射锁页内存

mapped memory拥有两个地址:主机端地址(内存地址)和设备地址(显存地址),能够在kernel中直接访问mapped memory中的数据,而没必要再在内存和显存间进行数据拷贝,即zero-copy功能。若是内核程序只须要对mapped memory进行少许读写,这样作能够减小分配显存和数据拷贝的时间。

mapped memory在主机端的指针能够由cudaHostAlloc()函数得到;它的设备端指针能够经过cudaHostGetDevicePointer()得到,从kernel中访问页锁定内存,须要将设备端指针做为参数传入。

并非全部的设备都支持内存映射,经过cudaGetDeviceProperties()函数返回的cudaMapHostMemory属性,能够知道当前设备是否支持mapped memory。若是设备提供支持,能够在调用cudaHostAlloc()时加上cudaHostAllocMapped标志,将pinned memory映射到设备地址空间。

因为mapped memory能够在CPU端和GPU端访问,因此必须经过同步来保证CPU和GPU对同一块存储器操做的顺序一致性。可使用流和事件来防止读后写,写后读,以及写后写等错误。

对mapped memory的访问应该知足与global memory相同的合并访问要求,以得到最佳性能。

注意:

  • 在执行cuda操做以前,先调用cudaSetDeviceFlags()(加cudaDeviceMapHost标志)进行锁页内存映射。不然,调用cudaHostGetDevicePointer()函数会返回一个错误。
  • 多个主机端线程操做的一块portable memory同时也是mapped memory时,每个主机线程都必须调用cudaHostGetDevicePointer()得到这一块pinned memory的设备端指针。此时,这一块内存在每一个主机端线程中都有一个设备端指针。

注册锁页内存

锁页内存注册将内存分配与页锁定和主机内存映射分离。能够实现操做一个已分配的虚拟地址范围,并页锁定它。而后,将其映射给GPU,正如cudaHostAlloc()能够根据须要让内存映射到cuda地址空间或变成可共享的(全部GPU可访问)。

函数cuMemHostRegister()/cudaHostRegister()cuMemHostUnregister()/cudaHostUnregister()分别实现吧主机内存注册为锁页内存和移除注册的功能。

注册的内存范围必须是页对齐的;不管是基地址仍是大小,都必须是可被操做系统页面大小整除。

注意:

当UVA(同一虚拟寻址)有效,全部的锁页内存分配均是映射的和可共享的。这一规则的例外是写结合内存和注册的内存。对于这两者,设备指针可能不一样于主机指针,应用程序须要使用cudaHostGetDevicePointer()/cuMemHostGetDevicePointer()查询设备指针。
相关文章
相关标签/搜索