系统中被CPU访问的内存,分为两种类型:可分页内存(pageable memory,通常应用中默认使用)和页锁定内存(page-locked或者pinned)。api
可分页内存即为经过操做系统api(malloc(), new())分配的存储器空间;而页锁定内存始终不会被分配到低速的虚拟内存中,可以保证存在于物理内存中,而且可以经过DMA加速与设备端的通讯。缓存
为了让硬件使用DMA,操做系统容许主机内存进行页锁定,而且由于性能缘由,CUDA包含了开发者使用这些操做系统工具的API。页锁定后的且映射为cuda直接访问的锁定的内存容许如下几点:app
使用pinned memory有不少好处:能够达到更高的主机-设备端的数据传送带宽,若是页锁定内存以write-commbined方式分配,带宽更高;某些设备支持DMA功能,在执行内核函数的同时利用pinned memory进行主机端与设备端之间的通讯;在某些设备上,pinned memory还能够经过zero-copy功能映射到设备地址空间,从GPU直接访问,这样就不用在主存和显存之间进行数据传输。异步
经过cudaHostAlloc
()和cudaFreeHost
()来分配和释放pinned memory。函数
在使用cudaHostAlloc
分配页锁定内存时,加上cudaHostAllocPortable
标志,可使多个CPU线程经过共享一块页锁定内存,从而实现cpu线程间的通讯。在默认状况下,pinned memory由哪一个cpu线程分配,就只有该CPU线程才能访问这块空间。而经过portable memory则可让控制不一样GPU的几个CPU线程共享同一块pinned memory,减小CPU线程间的数据传输和通讯。工具
当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拥有两个地址:主机端地址(内存地址)和设备地址(显存地址),能够在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相同的合并访问要求,以得到最佳性能。
注意:
cudaSetDeviceFlags()
(加cudaDeviceMapHost
标志)进行锁页内存映射。不然,调用cudaHostGetDevicePointer()
函数会返回一个错误。锁页内存注册将内存分配与页锁定和主机内存映射分离。能够实现操做一个已分配的虚拟地址范围,并页锁定它。而后,将其映射给GPU,正如cudaHostAlloc()
能够根据须要让内存映射到cuda地址空间或变成可共享的(全部GPU可访问)。
函数cuMemHostRegister()/cudaHostRegister()
和cuMemHostUnregister()/cudaHostUnregister()
分别实现吧主机内存注册为锁页内存和移除注册的功能。
注册的内存范围必须是页对齐的;不管是基地址仍是大小,都必须是可被操做系统页面大小整除。
注意:
当UVA(同一虚拟寻址)有效,全部的锁页内存分配均是映射的和可共享的。这一规则的例外是写结合内存和注册的内存。对于这两者,设备指针可能不一样于主机指针,应用程序须要使用cudaHostGetDevicePointer()/cuMemHostGetDevicePointer()查询设备指针。