3.8_主机内存

3.8 主机内存

主机内存就是CPU内存——那个在我们听说CUDA之前使用malloc()/free()和new[]delete[]函数操作了好多年的东西。在所有运行CUDA的操作系统上,主机内存是虚拟化的。硬件实施的内存保护使进程在没有特殊规定的情况下无法互相读写对方的内存[1]。内存中的“页”,通常为4KB或8KB大小,可在不改变它们虚拟地址的情况下被重定位。特别的是,它们可被交换到磁盘上,有效地使电脑中有比物理内存更多的虚拟内存。当一个页被标记为“非驻留的”,一次访问这个页的操作会发送给操作系统“页错误”的信号,这会提示操作系统寻找一块可用的内存,复制磁盘上的数据到内存中,使虚拟的内存页指向新的物理位置从而恢复请求操作。

操作系统中管理虚拟内存的部件叫做“虚拟内存管理器”或VMM。除了别的之外,VMM监视内存活动并使用启发式方法决定什么时候从磁盘驱换出一个页,以及在换出页发生引用时解决“缺页故障”。

VMM作为提供给硬件驱动的服务,使硬件直接访问主机内存变得容易。在现代计算机中,许多外设,包括控制器、网络控制器和GPU,可以通过DMA读写主机内存。DMA的前两个优点:它避免了数据复制[2]并且使硬件可以与CPU并发操作。第三个优点是硬件通过DMA可以得到更好的总线表现。

为了便利DMA,操作系统VMM提供一个页面锁定(page-locking)功能。被VMM标记为锁页的内存就不能被换出了,所以物理内存地址不能被修改。一旦内存被锁页,驱动程序便可以使用DMA硬件引用内存的物理地址。这一硬件设置是与锁页操作本身独立且不同的操作。因为页面锁定使其低层物理地址不再为其他分配所用,太多的锁页反而会影响计算机性能。

没被锁页的内存称作可换页的(pageable)。锁页后的内存被称作锁页内存(pinned),由于其物理内存不能被操作系统改变(它被固定在那个位置)。

3.8.1 锁页主机内存

锁页主机内存由CUDA使用函数cuMemHostAlloc()/CUDAHostAlloc()分配。该内存是页锁定的并且由当前CUDA上下文为DMA设置[3]。

CUDA跟踪分配的内存范围并且自动加速引用锁页内存的内存复制操作。异步内存复制操作只在锁页内存上工作。应用程序可以通过cuMemHostGetFlags()函数判断一个给定的主机内存地址范围是否被锁定。

在操作系统文档中,页面锁定(page-locked)和锁页(pinned)是同义的。但是对CUDA,锁页内存(pinned memory)是经过页锁定后且为硬件访问而映射后的主机内存,而页面锁定(page-locking)只是指操作系统的一种不允许换页的机制。

3.8.2 可分享的锁页内存

可分享锁页内存在页锁定后映射给所有的CUDA上下文。这一操作的底层机制十分复杂:当一个可分享锁页内存分配操作执行,它会在返回前映射到所有的CUDA上下文。此外,无论何时CUDA上下文被创建,所有的可分享锁页内存分配都会被映射到新的CUDA上下文中。无论是对可分享内存分配还是上下文创建,任何映射的失败都会引发内存分配错误或上下文创建错误。可喜的是,在CUDA 4.0中,如果UVA有效,所有的锁页内存分配全部可分享。

3.8.3 映射锁页内存

映射锁页内存映射到CUDA上下文中的地址空间中,所以内核可能读写这块内存。默认的,锁页内存不被映射到CUDA地址空间,所以它不能被内核的不合法写入毁坏。对集成GPU,映射的锁页内存带来“零复制”:由于主机(CPU)和设备(GPU)共享一块内存池,它们可以不用显式复制来交换数据。

对于独立GPU,映射锁页内存使主机内存可被内核直接读写。对于小型数据,这一特点可以消除显式内存复制命令的花销。映射的锁页内存在写入方面尤其有优势,因为这不会有延时存在。在CUDA 4.0中,如果UVA(统一虚拟编址)有效,所有的锁页分配都会被映射。

3.8.4 主机内存注册

由于开发者(尤其是库开发者)有时无法分配他们想要访问的内存,CUDA 4.0添加了注册已存在的虚拟内存地址范围的能力。

cuMemHostRegister()/CUDAHostRegister()函数会接受虚拟内存范围,之后将页面锁定,并为当前GPU(或为所有的GPU,如果

CU_MEMHOSTREGISTER_PORTABLE或cudaHostRegisterPortable被指定)

映射它。主机内存注册与UVA有着反常关系,由于任何的适合注册的地址范围必须不包含在当CUDA驱动程序被初始化时为UVA保留的虚拟地址空间里。

[1] 实现进程间共享的API包括Windows的MapView()或Linux的mmap()。
[2] 这一额外复制对使用GPU的开发者来说很显眼,它会比磁盘或网络控制器这样的设备占用更多的带宽。无论设备是什么类型的,没有DMA,驱动程序就必须使用CPU或特殊的硬件缓冲区来复制数据。

[3] CUDA开发者经常会有疑问:页锁定内存与CUDA的锁页内存是否有区别?答案是:有!锁页内存由CUDA进行分配或注册,进行映射之后可为GPU直接访问,普通的页锁定内存则不是。

3.8_主机内存 - CUDA专家手册 | OpenTech