我们最近推出的世界级嵌入式图形处理器 ARM®Mali™-T604 GPU ,该处理器拥有出色的内存带宽、令人惊叹的像素填充率以及Gigaflops级的可编程着色处理能力。
我们需要让该图形处理器发挥强大的数据处理性能,鉴于它的大部分数据都来自内存,我们花了很多时间和精力来设计它的内存管理单元(MMU)。我将为您介绍它的主要功能,并说明为什么合理设计的MMU是如此重要。
在大多数嵌入式图形系统所使用的统一内存架构中,内存由CPU和GPU共享,并充当传输场景数据高带宽信道的角色。
依靠CPU运行的应用程序在驱动程序堆栈的协同工作下获得所分配的内存,并设置好渲染场景所需的全部数据。好的应用程序会按硬件能够直接读取的格式准备数据,这样驱动程序堆栈的开销就会比较低。(如果GPU能够读取多种不同类型的数据,出现这种情况的可能性会大大增加,但这不是我们这次所要讨论的话题。)
现在我们需要确保这种场景数据能够确实为GPU所用。
过去,尤其在采用非统一架构的年代,往往只有一小部分的实际内存可以由GPU寻址,这通常可称为内存“窗口”问题。如今这种情况不再普遍,因为GPU及其他外设通常有权限访问整个地址空间。然而,随着移动领域 64 位寻址技术的出现,我们不得不开始未雨绸缪了。
所有的现代CPU都拥有内置的内存管理器,以便操作系统能够为每个进程分配一个明显连续的虚拟地址空间,即使实际物理内存已严重地支离破碎。如果 GPU通过物理方式为内存寻址,这种碎片现象就会显现,此时我们就需要确保CPU获得无论从物理角度还是虚拟角度看都具有连续性的内存。
在上述两种情形中,都可以使用物理寻址的GPU和某些软件解决方法,但对内存共享问题而言,在GPU自身上采用MMU却是一种更加出色的解决方案。软件解决方法是使用一种特殊的内存分配程序来创建一个合适的共享区域,然后将数据复制到其中。除了复制过程所需耗费的显著成本外,我们还要进行分配工作,如果操作系统不直接支持,我们还要费尽心思解决操作系统的问题,而且还要密切注意内存何时才能得以释放。
换一个角度来看,如果我们在GPU上采用MMU,就可以非常轻松地从CPU 的地址空间向GPU的地址空间进行内存映射。如果使用与CPU相同的页表布局,此信息即可非常快速地得以共享或复制。
Mali-T604 MMU使用与ARM CPU(例如 ARMCortex™-A15 MPCore™ 处理器)相同的页表布局,支持大型地址空间。这就降低了 GPU构建页表的开销,而且是开发人员早已熟知的一种格式。因为Mali-T604 MMU也支持大塊页面,所以无需分解大塊页面,从而进一步简化了从CPU中进行的内存映射。此外,这样还降低了页面管理开销,并且可以更好地利用GPU上的TLB 资源。
MMU 带来的一大好处是,我们可以在CPU和GPU上设置一些拥有相同虚拟地址的内存区。如此一来,我们就无需手动转换地址,因为地址会传递到GPU中,这样进程的效率就会高得多。
就 CPU 这一方面而言,这样也可以更加轻松地操作此类结构,使得我们能够利用更复杂的数据结构将更多工作转移到GPU自身。如果不是这样,在更新数据结构时,我们要么就需要做备份,要么就需要记录CPU和GPU两方面的所有指针。
当然,一旦拥有 MMU,我们也可以获得进程间保护、快速上下文切换以及故障处理等所有传统优势。我们甚至可以将CPU上早已采用的传统方法用在GPU上,例如按需载入页面。
此外还有其他一些优势。通过这种方法,您可以从一定程度上操作内存,而如果采用基于软件的方案,基本上不可能实现这一点。对于不同的数据结构,可以做到利用不同的缓存特性来映射内存。如果不希望因为那些只读取了一次的数据而浪费了GPU上宝贵的缓存空间,这就显得尤为重要。
通常,对移动平台而言,即使物理内存很大,也只有相对较小的有限虚拟地址空间。随着移动 3D资产不断增多,如果我們让CPU不从自己的地址空间中映射某些特定类别的内存,而仍然允许GPU访问这些内存,这种也是合理的。纹理内存是这一方面的典型范例,因为大多数纹理数据都由CPU放置在内存中,在此之后就只能由GPU 读取。如果MMU使用独立于CPU的页表,这也很容易安排。
Mali T-604 MMU也允许将页面标记为共享,从而使我们能够利用与CPU之间的一致性。驱动程序充分利用所有这些功能,从而实现性能和CPU地址占用情况两方面的优化。
CPU上的线程模型是非常粗略的,每个线程运行数千周期,然后才通过上下文切换进入另一线程。进程间上下文切换要求我们更改MMU设置,以确保实现适当的保护,而这通常涉及更改页表并清空所有缓存的转换数据。
在GPU上,任何一次都可以同时处理数百个线程;例如,四核Mali-T604 拥有 1024 个活动线程。由于线程之间的切换在非常精细的级别进行,采用切换页表的CPU策略代价高昂。如果让GPU的MMU能够同时访问多个地址空间,我们就可以彻底解决绝大多数情况下的上下文切换瓶颈问题。
尽管我们需要让每个进程能够访问它自己的内存,以此对其进行保护,但我们仍然可以同时将不同应用程序的线程混合在同一个GPU中。每个线程与一个虚拟地址空间关联,分别对应于一个进程,而每个地址空间又拥有自己的页表。这样,GPU就可以根据需要在每个线程的每个指令之间有效地进行上下文切换。
Mail-T604 MMU 实现了四个独立的地址空间,这样四个进程的线程可以同时在GPU上运行,而无需进行上下文切换。
随着资产量的增大和平台功能的增强,32位寻址技术的局限性也日益凸显,这种情况下也就需要更大的地址空间。由于高端嵌入式设备开始使用超过 4GB的内存,在 GPU上配备具有相当功能的MMU 就可以防止再次出现“特殊GPU可寻址内存窗口”的问题。
您是否还记得原始 IBM PC 的内存映射?大多数外设都只能访问所谓的“较小的”内存区域,因此就涌现了各种专用的内存区,从而将可用内存划分得支离破碎,不必要地增加了编程的繁琐程度。
有了64 位MMU,GPU的内存就可以分配到CPU 64 位地址空间中的任意地方,这样我们就巧妙地解决了上述难题。
Mali-T604 MMU实现了64 位虚拟地址(实际可映射的有 48 位)以及多达48位物理地址空间。这符合典型的大型内存CPU 实现方案,例如Cortex-A15,是页表大小和实际 RAM 大小二者之间的完美折衷。在嵌入式设备上,1TB 的大小对主内存而言已经足够,至少目前是这样。支持存储和解释所有的64 位地址使我们能够在无需更改架构的情况下,在未來继续扩展GPU技术。
功能强大的内存管理单元越来越成为现代嵌入式图形处理器不可或缺的一部分。它解决了一些常见的问题,也为优化图形驱动程序创造了一些令人振奋的新机遇。
总之,功能齐全的 64 位内存管理单元,就像 Mali-T604所拥有的那样,对下一代 GPU而言就是一种“必备”附件。
Sean Ellis 是 ARM 多媒体处理部門的一名技术人员。他从 1988年就开始从事 3D 图形工作,包括处理ARM最初的机器 Archimedes。他在制定 Java移动 3D 图形标准(M3G 和M3G2)这一规范的过程中发挥了关键作用,目前正致力于定义下一代 ARMGPU 的架构。他也参与专利工作,帮助保护 ARM在多媒体领域的创新成果。