GPU架构的缓存一致性
https://www.cs.sfu.ca/~ashriram/papers/2013_HPCA_GPUCoherence.pdf
摘要
尽管可扩展的一致性已在通用芯片多处理器(CMP)背景下得到广泛研究,但GPU架构带来了一系列新的挑战。引入传统的目录协议会给现有的GPU应用增加不必要的的一致性流量开销。此外,这些协议增加了GPU存储系统的验证复杂性。近期研究——库缓存一致性(LCC)[34, 54]——探索了在CMP一致性协议中使用基于时间的方法。本文描述了一种用于GPU的基于时间的一致性框架,称为时序一致性(TC),它利用单芯片系统中的全局同步计数器来开发一种简化的GPU一致性协议。同步计数器使得所有一致性转换(例如缓存块的失效)能够同步发生,从而消除了所有一致性流量和协议竞争。我们提出了一种TC的实现,称为TC-Weak,它消除了LCC在暂停存储操作与增加L1缺失率之间的权衡,从而提升性能并减少互连流量。
通过提供一致的L1缓存,TC-Weak相比基线GPU中禁用非一致L1缓存(NO-L1)的情况,将具有工作组间通信的GPU应用性能提升了85%。我们还发现,在GPU上,写通过协议的性能优于写回协议,因为后者会因对一次性写入数据进行不必要的数据填充而导致流量增加。
1 引言
图形处理器单元(GPU)已在高通量通用计算中无处不在。基于C的编程接口(如OpenCL [29]和NVIDIA CUDA [46])通过抽象掉SIMD硬件并提供独立标量线程并行执行的假象,简化了GPU编程。传统上局限于规则并行性,但近期研究[21, 41]表明,即使是高度不规则的算法也能在GPU上获得显著的加速。此外,近期GPU中包含的多级缓存层次[6, 44]将程序员从软件管理缓存的负担中解放出来,并进一步增加了GPU作为加速具有不规则内存访问模式应用[22, 40]的平台吸引力。

GPU缺乏缓存一致性,如果应用要求内存操作在所有核心间可见,则需要禁用私有缓存[6, 44, 45]。通用芯片多处理器(CMP)通常采用硬件缓存一致性[17, 30, 32, 50]来强制执行严格的内存一致性模型。这些一致性模型构成了高级语言内存模型[10, 35]的基础,并提供了多线程CPU应用所使用的同步原语。一致性极大地简化了在GPU上支持高级语言的精确定义的一致性和内存模型。它还有助于在具有单芯片CPU-GPU集成的异构架构[11, 26]中实现统一地址空间。本文侧重于GPU核心领域的一致性;我们将CPU-GPU缓存一致性留作未来工作。
禁用L1缓存虽然简单地提供了一致性,但代价是应用性能下降。图1(a)展示了一组具有工作组间通信并需要一致L1缓存以保证正确性的GPU应用(在第7节描述)的性能潜在提升。与禁用L1缓存(NO-L1)相比,一个理想的一致性GPU(IDEAL-COH)(其中一致性流量不产生任何延迟或流量成本)将这些应用的性能平均提高了88%。
GPU对一致性提出了三个主要挑战。图1(b)通过比较基线非一致GPU系统(NO-COH)与三个具有缓存一致性协议的GPU系统(写回MESI、包含式写通过GPU-VI和非包含式写通过GPU-VIni(在第4节描述))的互连流量,描绘了第一个挑战。这些协议为包含不需要一致性的数据的GPU应用引入了不必要的一致性流量开销。
其次,在GPU上,类似CPU的最坏情况规模估计[18]将需要不切实际的存储空间来跟踪数千个进行中的一致性请求。第三,现有的一致性协议以瞬态状态和额外消息类别的形式引入了复杂性。它们需要在GPU互连上额外的虚拟网络[58]来确保前进进度,因此增加了功耗。跟踪大量共享者[28, 64]的挑战对当前GPU来说不是问题,因为它们仅包含数十个核心。
在本文中,我们提出使用基于时间的一致性框架来最小化GPU一致性的开销,而不引入显著的设计复杂性。传统的一致性协议依赖于显式消息在需要使某个地址失效时通知其他方。我们描述了一个基于时间的一致性框架,称为时序一致性(TC),它使用同步计数器使缓存块自我失效,并在没有显式消息的情况下维护一致性不变式。现有硬件实现了跨组件同步的计数器[23, 第17.12.1节]以提供高效的定时器服务。利用这些计数器使得TC能够消除一致性流量、降低面积开销并减少GPU一致性的协议复杂性。TC需要预测缓存块的生存期以进行自我失效。
Shim等人[34, 54]最近提出了一种基于时间的硬件一致性协议——库缓存一致性(LCC),它通过暂停对缓存块的写入直到它们被所有共享者自我失效来实现CMP上的顺序一致性。我们描述了TC框架的一种实现,称为TC-Strong,它与LCC类似。第8.3节显示TC-Strong在GPU上表现不佳。我们提出的TC框架的第二种实现,称为TC-Weak,使用一种新颖的基于时间戳的内存栅栏机制来消除写入暂停。TC-Weak使用时间戳来驱动所有一致性操作。它实现了释放一致性[19],从而在GPU上完全支持C++和Java内存模型[58]。

图2 展示了TC-Strong和TC-Weak的高级操作。两个核心C2和C3分别在其私有L1中缓存了地址A和B。在TC-Strong中,C1对A的写入会暂停完成,直到C2自我失效其本地缓存的A副本。类似地,C1对B的写入会暂停完成,直到C3自我失效其B副本。在TC-Weak中,C1对A和B的写入不会暂停等待其他副本被自我失效。相反,栅栏操作确保所有先前写入的地址已在其他本地缓存中被自我失效。这确保了来自该核心的所有先前写入在栅栏完成后将全局可见。
本文的贡献如下:
-
讨论了将现有一致性协议引入GPU所面临的挑战。我们针对VI协议[30]引入了两种优化,使其更适合GPU。
-
对GPU上的包含式和非包含式目录协议进行了详细的复杂性和性能评估。
-
描述了时序一致性(TC),一种利用单芯片系统中同步计数器来消除一致性流量和协议竞争的GPU一致性框架。
-
提出了TC-Weak一致性协议,该协议采用基于时间戳的内存栅栏在GPU上实现释放一致性[19]。
-
为TC-Weak提出了一种简单的生存期预测器,该预测器在一系列GPU应用中表现良好。
我们的实验表明,带有简单生存期预测器的TC-Weak相比基线非一致GPU,将一组具有工作组间通信的GPU应用的性能提升了85%。平均而言,其性能与VI协议相当,并且比MESI在所有基准测试中快23%。此外,对于一组具有工作组内通信的GPU应用,它将MESI、GPU-VI和GPU-VIni的流量开销分别降低了56%、23%和22%,同时将互连能量使用降低了40%、12%和12%。与TC-Strong相比,TC-Weak在所有应用中的性能快28%,互连流量低26%。
本文其余部分组织如下。第2节讨论相关工作,第3节回顾GPU架构和缓存一致性,第4节描述目录协议,第5节描述GPU一致性的挑战。第6节详细介绍了TC-Strong和TC-Weak的实现,第7和第8节介绍了我们的方法和结果,第9节得出结论。
2 相关工作
时间戳的使用已在软件一致性[42, 63]中有所探索。Nandy等人[43]首次考虑了将时间戳用于硬件一致性。库缓存一致性(LCC)[34, 54]是一种基于时间的硬件一致性方案,它将时间戳存储在目录结构中,并延迟对未过期块的存储操作,以在CMP上强制执行顺序一致性。TC框架的TC-Strong实现与LCC类似,因为两者都通过在共享的最后一级缓存处暂停写入来强制执行写入原子性。与LCC不同,TC-Strong支持来自单个核心的多个未完成写入,并实现了宽松的一致性模型。TC-Strong包含了一些优化,以消除由于私有写入和L2驱逐引起的暂停。尽管有这些改变,我们发现TC-Strong中的写入暂停在GPU上导致性能不佳。我们提出了TC-Weak和一种新颖的基于时间戳的内存栅栏机制,以消除所有写入暂停,与TC-Strong相比,提高了性能并减少了互连流量。我们还表明,与CPU应用[34, 54]不同,LCC提出的固定时间戳预测不适合GPU应用。我们提出了一种简单而有效的生存期预测器,可以适应各种GPU应用。最后,我们完整描述了我们提出的协议,包括详细描述实现细节的状态转换表。
私有缓存中块的自我失效先前也在缓存一致性的背景下被探索过。动态自我失效(DSI)[33]通过在接收到对某个块的下一个独占请求之前,推测性地自我失效私有缓存中的块,来减少因失效引起的关键路径延迟。在顺序一致性实现中,DSI需要在自我失效时向目录发送显式消息,并且不会缓解GPU上的流量问题。在其宽松一致性实现中,DSI可以通过使用"撕下"块来减少流量,这些块在同步点被自我失效。最近,Ros等人[48]提出将撕下块扩展到所有缓存块,以完全消除一致性目录,从而降低CPU一致性的实现复杂性和流量。他们的协议要求在同步点自我失效所有共享数据。然而,在GPU上同步事件要频繁得多。数千个标量线程共享一个L1缓存,会导致频繁的自我失效。他们的协议还要求在最后一级缓存处阻塞和缓冲原子操作。GPU支持数千个并发原子操作;缓冲这些操作将非常昂贵。

Denovo[16]简化了一致性目录,但使用了需要用户注释代码的限制性编程模型。TC-Weak不改变GPU编程模型。近期的一致性方案[28, 51, 64]简化了针对数千个核心的共享者状态跟踪。GPU有数十个核心;精确的共享者表示不是问题。
3 背景
本节描述了本文评估的基线非一致GPU架构(类似于NVIDIA的Fermi[44])的存储系统和缓存层次结构。并简要讨论了缓存一致性。
3.1 基线GPU架构
图3 展示了我们基线非一致GPU架构的组织结构。一个OpenCL[29]或CUDA[46]应用在CPU上开始执行,并将计算内核启动到GPU上。每个内核将线程层次结构(工作项/标量线程的波前组成的工作组的NDRange)启动到GPU上。每个工作组被分配给一个高度多线程的GPU核心。标量线程作为一个SIMD执行组进行管理,该组由32个线程组成,称为一个warp(NVIDIA术语)或wavefront(AMD术语)。
GPU存储系统。 GPU内核通常访问局部内存、线程私有内存和全局内存空间。软件管理的局部内存用于工作组内通信。线程私有内存是每个线程私有的,而全局内存在GPU上的所有线程之间共享。线程私有内存和全局内存都存储在海外的GDDR DRAM中,并缓存在多级缓存层次结构中,但只有全局内存需要一致性。海外DRAM内存分布在多个内存分区中,这些分区通过互连网络连接到GPU核心。来自同一波前内不同线程对同一缓存块的存储器访问由合并单元合并为单个宽访问。一条内存指令会为波前访问的每个唯一缓存线生成一次内存访问。所有请求都由GPU核心的顺序内存阶段按FIFO顺序处理。在单个波前中,多个标量线程对同一字的写入没有定义的行为[46];只有一个写入会成功。在本文中,从内存一致性模型的角度来看,一个GPU波前类似于一个CPU线程。
GPU缓存层次结构。 GPU缓存层次结构包括每个核心私有的L1数据缓存和一个共享的L2缓存。每个内存分区容纳L2缓存的一个存储体。L1缓存不一致。它们遵循写驱逐[46](写清除[24])、写不分配缓存策略。L2缓存是写回式并采用写分配。由每个GPU核心中的合并单元生成的内存访问被传递(每个周期一个)到每核心MSHR表。MSHR表合并来自不同波前对同一缓存线的读取访问,以确保每个GPU核心对每个缓存线只有一个未完成的读取访问。写入不会被合并,并且由于它们是写通过的,来自一个GPU核心的对同一缓存线的任意数量的写入请求都可能是未完成的。互连网络、L2缓存控制器和海外DRAM通道中的点对点排序确保了来自同一波前对同一地址的多个未完成写入按程序顺序完成。所有缓存控制器按顺序每个周期服务一个内存请求。L2处的未命中通过分配一个MSHR条目并将请求从请求队列中移除来处理,以防止停滞。
原子操作。 读写修改原子操作由每个内存分区中的原子操作单元执行。在我们的模型中,原子操作单元可以在单个周期内对驻留在L2缓存中的行执行读-修改-写操作。
3.2 一致性与连贯性
缓存一致性协议执行以下三个职责[3]:1) 它将新写入的值传播到所有私有缓存的副本。2) 它在一个写入已完成并对所有线程和处理器可见时通知写入线程或处理器。最后,3) 一致性协议可以确保写入原子性[3],即,一次写入的值在逻辑上同时被所有线程看到。在写失效一致性协议中,通常通过要求在写入完成之前使缓存块的所有其他副本都失效来强制执行写入原子性。内存一致性模型可能[4, 19, 57, 59]要求也可能不[2, 19, 53]要求写入原子性。
4 目录协议
本节介绍了本文用于对比的MESI和GPU-VI目录协议。MESI、GPU-VI和GPU-VIni都需要一个一致性目录来跟踪L1共享者。MESI和GPU-VI通过在L2驱逐时使缓存行的所有L1副本失效(召回)来强制包含性。包含性允许共享者列表与L2标签一起存储。GPU-VIni是非包含式的,需要单独的片上存储来存放目录。
4.1 MESI
MESI是一种四状态的一致性协议,采用写回式L1和L2缓存。它包含了一些优化,以消除非一致GPU互连和缓存控制器所需的点对点排序要求。相反,MESI依赖五个物理或虚拟网络来支持五种不同的消息类别,以防止协议死锁。MESI实现了复杂的缓存控制器,能够从待处理请求池中选择可服务的请求。L1的写分配策略要求写入数据必须被缓冲,直到获得适当的一致性许可。这需要在每个GPU核心中增加面积和复杂性来缓冲存储操作。
4.2 GPU-VI
GPU-VI是一种两状态的一致性协议,其灵感来源于Niagara [30]中的写通过协议。GPU-VI实现了写通过、无写分配的L1缓存。它要求任何在L2完成的写入都必须使所有L1副本失效。对共享缓存行的写入在L2控制器发送失效请求并收到所有共享者的确认之前无法完成。
GPU-VI在传统VI协议[60]的基础上增加了两项优化。首先,它在收到确认之前,在写入命中时将数据直接写入L1缓存,从而消除了缓冲存储操作带来的面积和复杂性开销。其次,它将针对有待处理写入的L1块的加载视为缺失。这在保持写入原子性的同时减少了缓存控制器的停顿。GPU-VI需要4个物理或虚拟网络来保证无死锁执行。
4.3 GPU-VIni
非包含式的GPU-VIni将GPU-VI中的目录存储与L2缓存解耦,以允许目录大小独立扩展。它增加了额外的复杂性来管理由独立目录结构引入的状态。GPU-VIni中相同的缓存控制器管理目录和L2缓存。从L2缓存中驱逐不会产生召回请求,但从目录中驱逐则需要召回。GPU-VIni实现了一个8路组相联目录,其条目数是私有缓存块总数量的两倍(R=2,如Martin等人[39]提出的框架中所述)。第8.5节提供了具有更大目录大小的GPU-VIni的数据。
5 GPU一致性的挑战
本节阐述了将传统一致性协议引入GPU所面临的主要挑战。

5.1 一致性流量
传统的一致性协议会给那些为非一致GPU架构设计的现有GPU应用带来不必要的流量开销。这些开销包括因目录驱逐引起的召回流量、因伪共享引起的失效流量,以及因内核间通信引起的失效流量。对于GPU上的包含式协议,召回流量变得尤其成问题,因为共享的GPU L2缓存大小与私有L1缓存的总大小相当 [6, 44]。包含式缓存层次结构是低复杂度一致性实现的一个有吸引力的 [7] 选择。此外,在非包含式协议中,为减少召回流量 [39] 所需的大型目录会占用GPU L2缓存宝贵的空间。
减少一致性流量的一个有效方法是,对不需要一致性的数据区域选择性地禁用一致性。Kelm等人 [27] 提出了一种混合一致性协议,用于禁用数据区域的硬件一致性。它需要额外的硬件支持和代码修改,以允许数据在一致性域之间迁移。第6.3节将解释TC-Weak如何利用时间戳在缓存行粒度上强制执行一致性,而无需任何代码修改来识别一致性和非一致性数据。
5.2 存储需求
在CPU上,由于每个核心仅有数十个线程,其一致性实现可以分配足够的片上存储资源来缓冲最坏情况下的并发一致性请求数量 [18]。然而,GPU并行执行数万个标量线程。在类似CPU的一致性实现 [18] 中,若要有足够的存储来处理最坏情况下的内存访问数量(每个线程一个内存请求),目录协议将需要一个不切实际的片上缓冲区,其大小可能高达GPU L2缓存总量的28%,用于跟踪一致性请求。减少最坏情况下的存储开销需要在端点队列填满时,通过反压流控机制来限制网络 [37]。TC-Weak则消除了一致性消息及其缓冲所需的存储成本。
5.3 协议复杂性
表1列出了我们评估的协议中的状态数量。我们将稳定状态定义为通常与一致性协议相关联的状态,例如MESI协议中的修改(M)、独占(E)、共享(S)和无效(I)状态。瞬态是稳定状态之间出现的中间状态。具体来说,瞬态缓存状态是与常规缓存操作相关的状态,例如在处理读缺失时维护缓存块的状态。瞬态缓存状态在一致性协议以及非一致架构中都存在。瞬态一致性状态是一致性协议所需的额外状态。例如,一个表明给定块正在等待失效确认的状态。一致性协议验证是一个重大挑战,其难度随着状态数量的增加而增长 [16],这个问题被称为状态空间爆炸 [47]。如表1所示,MESI、GPU-VIni和GPU-VI在基线非一致缓存的基础上分别增加了13、9和4个瞬态一致性状态,从而增加了验证的复杂性。TC-Weak在L1和L2中仅需要一个瞬态状态。基于消息的一致性协议需要额外的虚拟网络 [58] 或死锁检测机制 [31] 来确保前进进度。如表4所示,MESI需要比基线GPU多3个虚拟网络,而GPU-VI和GPU-VIni需要多2个虚拟网络。这些额外的虚拟网络用于在出现由一致性消息引起的循环资源依赖时防止死锁。由于TC-Weak消除了一致性消息,因此不需要额外的虚拟网络。
6 时序一致性
本节介绍时序一致性(TC),一种基于时间戳的缓存一致性框架,旨在满足高通量GPU架构的需求。与LCC类似,TC使用基于时间的自我失效来消除一致性流量。但与为CMP实现顺序一致性的LCC不同,TC为GPU应用提供了宽松的内存模型[58]。TC对GPU硬件的修改更少,并能实现更高的内存级并行性。第6.1节描述基于时间的一致性。第6.2节描述TC-Strong并将其与LCC进行比较。第6.3节描述TC-Weak,这是一种新颖的TC协议,它使用时间来驱动一致性和连贯性操作。
6.1 时间与一致性
本质上,基于失效的一致性协议的任务是在一组节点之间通信内存位置纪元[58]的开始和结束。基于时间的一致性利用了这样的见解:单芯片系统可以实现同步计数器[23, 第17.12.1节],从而实现低成本的一致性信息传递。具体来说,如果能够预测一个内存地址当前纪元的生存期,并在读取该位置时在所有读取者之间共享,那么这些计数器使得读取者能够同步地自我失效,从而消除了对纪元结束失效消息的需求。

图4比较了GPU-VI目录协议和TC处理失效的方式。图中描述了处理器C1和C2的读取操作,随后是C1的存储操作,所有操作都针对同一内存位置。图4(a)展示了写通过GPU-VI目录协议发生的事件序列。C1向目录发出加载请求(1),并接收数据。C2发出加载请求(2),也接收数据。接着C1发出存储请求(3)。存储着精确共享者列表的目录发现,在写入完成之前需要使C2失效,于是向C2发送失效请求(4)。C2接收到失效请求,使其私有缓存中的块失效,并发送回确认(5)。目录收到来自C2的失效确认(6),完成C1的存储请求,并向C1发送确认(7)。
图4(b)展示了TC如何处理此示例中的失效。当C1向L2发出加载请求时,它预测该地址的只读纪元将在时间T=15结束(1')。L2收到C1的加载请求和纪元生存期预测,将其记录下来,并回复数据和时间戳T=15(2')。该时间戳向C1表明,它必须在T=15之前自我失效其私有缓存中的该地址。当C2发出加载请求时,它预测纪元将在时间T=20结束(3')。L2收到C2的请求,检查为此地址存储的时间戳,并将其延长至T=20以适应C2的请求,然后回复数据和时间戳T=20(4')。在时间T=15(5'),C1的私有缓存自我失效该地址的本地副本。在时间T=20(6'),C2自我失效其本地副本。当C1向L2发出存储请求时(7'),L2发现全局时间戳(T=20)小于当前时间(T=25),这表明没有L1缓存包含该地址的有效副本。L2立即完成写入并向C1发送确认(8')。

与GPU-VI相比,TC不使用失效消息。全局同步计数器使得L2能够在本地且无需间接寻址地做出一致性决策。此示例展示了TC框架如何实现我们对GPU一致性的期望目标:所有一致性流量都被消除,并且由于没有失效消息,记录未完成失效请求状态的瞬态状态也不再需要。生存期预测在基于时间的一致性中很重要,因为它影响缓存利用率和应用性能。第6.4节描述了我们在TC-Weak中使用的简单预测器,它根据应用行为调整请求的生存期。
6.2 TC-Strong 一致性
TC-Strong 实现了具有写入原子性的释放一致性[19]。它使用写通过的 L1 缓存和写回的 L2 缓存。TC-Strong 需要在 GPU 核心和 L2 控制器(如图 5(a) 所示)处配备同步的时间戳计数器,以便为各组件提供当前系统时间。L1 和 L2 缓存的每个缓存行中都添加了一个小的时间戳字段,如图 5(b) 所示。L1 缓存行中的本地时间戳值指示了该特定缓存行有效的截止时间。如果 L1 缓存行的本地时间戳小于当前系统时间,则该缓存行无效。L2 中的全局时间戳值指示了一个时间点,在此时间点之前,所有 L1 缓存都将已自我失效该缓存行。
6.2.1 TC-Strong 操作
每个加载请求都会检查 L1 缓存行的标签和本地时间戳。它将标签匹配有效但本地时间戳已过期的情况视为缺失;自我失效一个 L1 块不需要显式事件。L1 处的加载缺失会向 L2 生成一个带有生存期预测的请求。L2 控制器将全局时间戳更新为当前全局时间戳和请求的本地时间戳中的最大值,以适应请求的时间量。L2 使用数据和全局时间戳响应 L1。L1 在完成加载之前,使用响应消息中的值更新其数据和本地时间戳。
存储请求会写通过到 L2,其完成会延迟到全局时间戳过期之后。

图 6(b) 阐释了 TC-Strong 如何维护一致性。图 6(a) 所示的代码片段来自 Sorin 等人 [58] 的一个例子,代表了在流水线并行应用中实现非阻塞队列 [20] 的常见编程模式。图 6(b) 左侧显示了核心 C1 产生的内存请求,右侧显示了 C2 的 L1 中两个内存位置 flag 和 data 的状态。最初,C2 缓存了 flag 和 data,其本地时间戳分别为 60 和 30。为简化起见,我们假设 C2 的操作被延迟了。
C1 执行指令 S1 并为 data 生成一个到 L2 的写入请求 (1),随后发出内存栅栏指令 F1 (2)。F1 延迟了该波前的调度,因为该波前有一个未完成的存储请求。当 S1 的存储请求到达 L2 (3) 时,L2 会暂停处理该请求,因为 data 的全局时间戳要到时间 T=30 才会过期。在 T=30 时,C2 自我失效 data (4),然后 L2 处理 S1 的存储 (5)。当 C1 收到 S1 请求的确认 (6) 时,栅栏指令完成。对于 S2 对 flag 的存储,也发生相同的事件序列。L2 暂停 S2 的写入请求 (7),直到 flag 在 C2 中自我失效 (8)。
L2 驱逐优化:写通过 L1 处的驱逐不会产生发送到 L2 的消息。只有过期的全局时间戳才能从 L2 中驱逐,以维护包含性。TC-Strong 使用 L2 MSHR 条目来存储未过期的时间戳。
私有写入优化:TC-Strong 实现了一项优化,以消除对私有数据的写入暂停。它将 L2 中单一的有效状态细分为两个稳定状态:P 和 S。P 状态表示私有数据,而 S 状态表示共享数据。一个仅被读取过一次的 L2 行处于 P 状态。如果对处于 P 状态的 L2 行的写入来自最初执行读取的核心,则这些写入是私有写入。在 TC-Strong 中,存储请求会携带 L1 处的本地时间戳(如果存在)到 L2。此时间戳与 L2 处的全局时间戳进行匹配,以检查最初执行读取的核心是否正在执行私有写入。
6.2.2 TC-Strong 与 LCC 的比较
LCC 和 TC-Strong 都使用基于时间的自我失效,并且都需要在 L1 和 L2 中具备同步的计数器和时间戳。两种协议都会在最后一级缓存处暂停对未过期时间戳的写入。
TC-Strong 对基线非一致 GPU 架构的硬件修改要求极低。它支持每个 GPU 波前有多个未完成的写入请求。相比之下,LCC 假设每个核心只有一个未完成的写入请求。通过放宽内存模型并利用基线 GPU 存储系统的点对点排序保证,TC-Strong 为每个 GPU 核心上数千个并发标量线程提供了更高的内存级并行性。
LCC 会暂停对未过期 L2 块的驱逐。TC-Strong 通过分配一个 L2 MSHR 条目来存储未过期的时间戳,从而消除了这种暂停。这减少了顺序执行的 GPU L2 缓存控制器昂贵的暂停。LCC 还会因为暂停对私有数据的写入直到全局时间戳过期,而对私有读写数据造成性能损失。TC-Strong 中的私有写入优化可以检测并消除这些暂停。
6.3 TC-Weak 一致性
本节介绍 TC-Weak。TC-Weak 放宽了 TC-Strong 的写入原子性要求。正如我们在第 8.3 节所示,这样做使得性能相比 TC-Strong 提升了 28%,互连流量降低了 26%。
TC-Strong 和 LCC 通过暂停写入来对所有数据强制执行一致性。TC-Weak 的洞见在于,GPU 应用可能包含大量不需要一致性的数据,而写入暂停会不必要地惩罚这些数据。通过放宽写入原子性,TC-Weak 消除了写入暂停,并将任何潜在的暂停转移到显式内存栅栏操作上。这带来了两个主要好处:首先,它消除了在共享 L2 缓存控制器处昂贵的暂停(这会影响所有核心和波前),并将其转移到内存栅栏处对单个波前的调度上。一个因内存栅而被取消调度的波前不会影响其他波前的性能。其次,它仅在程序通过内存栅栏要求并指定时才强制执行一致性。它实现了 RCpc [19] 一致性模型;关于此点的详细讨论可参见其他文献 [56]。

在 TC-Weak 中,对 L2 中未过期全局时间戳的写入不会暂停。写入响应会返回写入时 L2 缓存行的全局时间戳。返回的全局时间戳是保证该写入将对系统中所有核心可见的时间点。这是因为到此时,所有核心都将已使其私有缓存中的陈旧副本失效。TC-Weak 会为每个波前跟踪由写入返回的全局时间戳,称为全局写入完成时间(GWCT)。内存栅栏操作使用此信息来足够长时间地取消调度该波前,以保证该波前所有先前的写入都已全局可见。
如图 5(a) 所示,TC-Weak 为每个 GPU 核心添加了一个小的 GWCT 表。GWCT 表包含 48 个条目,对应 GPU 核心中的每个波前。每个条目持有一个时间戳值,该值是该波前观察到的所有 GWCT 中的最大值。
6.3.1 TC-Weak 操作
TC-Weak 中的内存栅栏会取消调度一个波前,直到该波前所有未完成的写入请求都已返回确认,并且直到该波前在 GWCT 表中的时间戳已过期。后者确保了所有先前的写入在栅栏完成时已对系统可见。
图 6(c) 通过展示图 6(a) 中 C1 内存指令的执行,阐明了 TC-Weak 如何维护一致性。C1 执行 S1 并向 L2 发送一个针对 data 的存储请求 (1')。随后,C1 发出一个内存栅栏操作 (2'),该操作延迟了波前的调度,因为 S1 有一个未完成的内存请求。L2 接收到存储请求 (3') 并返回 L2 中为 data 存储的当前全局时间戳。本例中,返回的值为 30,对应于 C2 初始缓存的副本。L2 不会暂停该写入,并返回一个带有 GWCT 的确认,该 GWCT 更新了 C1 中此波前的 GWCT 条目。在 C1 收到确认 (4') 后,没有未完成的内存请求了。此时波前的调度被延迟,因为该波前的 GWCT 条目(包含时间戳 30)尚未过期。当 data 在 C2 的缓存中自我失效时 (5'),该波前的 GWCT 过期,栅栏被允许完成 (6')。下一条存储指令 S2 向 L2 发送一个针对 flag 的存储请求 (6')。L2 返回一个 GWCT 时间 60 (7'),对应于 C2 缓存的副本。
比较图 6(c) 和 6(b) 可以看出,TC-Weak 的性能优于 TC-Strong,因为它仅在显式内存栅栏操作处暂停。这确保了对不需要一致性的数据的写入影响最小。
表 2 以 Martin [36] 使用的格式展示了 TC-Weak 完整的 L1 和 L2 状态机。每个表条目列出了对于给定的初始缓存行状态(左侧)和转换事件(顶部),所执行的操作以及最终的缓存行状态。4 个稳定的 L2 状态 I、P、S 和 E 分别对应于无效行、具有一个读取者的行、具有多个读取者的行以及具有过期全局时间戳的行。瞬态缓存状态 I_S 和 I_M 跟踪 L2 处读和写请求的缺失。瞬态一致性状态 M_I 跟踪被驱逐但具有未过期全局时间戳的 L2 块。注意,L2 缺少瞬态状态,并且对有效(P、S 和 E)行的写入不会在 L2 处暂停。在 L1 处,稳定状态 I 表示无效行或本地时间戳已过期的行,稳定状态 V 表示本地时间戳有效的行。瞬态缓存状态 I_V 和 I_I 用于跟踪读和写缺失,而瞬态一致性状态 V_M 跟踪对有效行的写入请求。
私有写入优化:为确保内存栅栏不会因对私有数据的写入而暂停,TC-Weak 使用了与 TC-Strong 类似并在第 6.2.1 节中描述的私有写入优化。对处于 P 状态的 L2 行的写入请求,如果其 L1 本地时间戳与 L2 全局时间戳匹配,则表明是私有写入,并且不返回 GWCT。由于 TC-Weak 不在 L2 处暂停写入,一个处于 P 状态的 L2 行可能对应多个未过期但已陈旧的 L1 行。TC-Weak 中的写入总是通过将全局时间戳加一来修改它。这确保了来自另一个具有陈旧数据的 L1 缓存的写入请求携带的本地时间戳与 L2 处的全局时间戳不匹配,并且写入响应会回复更新后的数据。
6.4 生存期预测
预测的生存期不应太短,以免 L1 块过早自我失效;也不应太长,以免存储被驱逐的时间戳浪费 L2 缓存资源并可能引入资源暂停。在第 8.4 节中,我们展示了为所有访问使用单一生存期值效果良好。而且,这个值是依赖于应用的。基于此洞见,我们提出一个简单的生存期预测器,它在每个 L2 缓存存储体维护一个单一的生存期预测值,并根据应用行为进行调整。加载操作在 L2 存储体处获取其生存期预测值。
该预测器基于 L2 存储体本地的事件更新预测的生存期。首先,如果一个带有未过期时间戳的 L2 块被驱逐,则本地预测值减少 t_evict 个周期。这减少了需要在 L2 驱逐后存储的时间戳数量。其次,如果加载请求因 L1 块过期而在 L1 处缺失,则本地预测值增加 t_hit 个周期。这有助于减少因过早自我失效导致的 L1 缺失。如果 L2 接收到对一个全局时间戳已过期的有效块的加载请求,生存期也会增加 t_hit 个周期。这确保了即使 L1 块被快速驱逐,预测值也会增加。第三,如果存储操作写入 L2 处一个未过期的块,则生存期减少 t_write 个周期。这有助于减少栅栏操作等待 GWCT 过期(即等待写入全局可见)的时间。对于不使用栅栏的应用,这第三种机制被禁用,因为它会不必要地增加 L1 缺失率。表 4 列出了我们评估中使用的常量值;我们发现这些值在所有应用中能产生最佳性能。
6.5 时间戳回绕
处于有效状态但时间戳已过期的 L1 块,在全局时间计数器回绕时可能变得未过期。这可以通过简单地闪断清除 L1 缓存中的有效位 [52] 来处理。更复杂的方法是可能的,但这超出了本工作的范围。我们评估的所有基准测试程序都没有运行得足够长,以至于在使用 32 位时间戳时会触发 L1 刷新。
7 方法论
我们通过使用 GEMS [38] 中的 Ruby 存储系统模型扩展 GPGPU-Sim 版本 3.1.2 [8],来对缓存一致的 GPU 架构进行建模。基线非一致存储系统及所有一致性协议均在 SLICC 中实现。MESI 缓存一致性协议取自 gem5 [9]。我们使用扩展了 Ruby 的 GPGPU-Sim,配置为模拟一个通用的 NVIDIA Fermi GPU [44]。我们使用 Orion 2.0 [25] 来估算互连功耗。
互连网络使用 Garnet [5] 中的详细固定流水线网络模型进行建模。两个交叉开关(每个方向一个)将 GPU 核心连接到内存分区。每个交叉开关每个互连周期可以向每个内存分区或从每个内存分区传输一个 32 字节的微片,每个方向的峰值带宽约为 175 GB/s。GPU 核心通过私有端口连接到互连网络。基线非一致及所有一致性协议均使用 GPGPU-Sim 中的详细 GDDR5 DRAM 模型。模拟的最小 L2 延迟为 340 周期,最小 DRAM 延迟为 460 周期(以核心周期计),以匹配通过 Wong 等人 [62] 发布的微基准测试在 Fermi GPU 硬件上观察到的延迟。表 4 列出了其他主要配置参数。

我们使用了两组基准测试进行评估:一组包含工作组间通信并需要一致的缓存以保证正确性,另一组仅包含工作组内通信。虽然对于后一组可以禁用一致性,但我们保持一致性启用,并将这组基准测试作为未来可能同时包含需要一致性和不需要一致性的数据的工作负载的代理。以下基准测试属于前一组:
Barnes Hut (BH) 在 CUDA 中实现了 Barnes Hut n-body 算法 [13]。我们报告了用于迭代构建一个包含 30000 个物体的八叉树的树构建内核的数据。
CudaCuts (CC) 在 CUDA 中实现了用于图像分割的最大流/最小割算法 [61]。我们通过利用一致的内存空间将推送、拉取和重新标记操作合并到单个内核中,对 CC 进行了优化,从而将性能提升了 30%。
Cloth Physics (CL) 是一个基于 "RopaDemo" [12] 的布料物理模拟。我们关注距离求解器内核,该内核使用一组约束来调整布料粒子的位置,以模拟弹簧-质量系统。
Dynamic Load Balancing (DLB) 在 CUDA 中实现了任务窃取 [14]。它使用非阻塞任务队列来负载均衡八叉树的划分。我们报告了输入图大小为 100000 个节点的数据。
Stencil (STN) 使用模板计算来实现用于地震成像中有用的 3D 波传播的有限差分求解器。每个工作组处理模板节点的子集。模板中的每个节点与 24 个相邻节点通信。一致的内存空间确保对不同子集中邻居的更新是可见的。STN 使用快速屏障 [55] 在计算时间步之间同步工作组。
Versatile Place and Route (VPR) 是一个用于 FPGA 的布局工具。我们将 VTR 1.0 [49] 中基于模拟退火的布局算法移植到了 CUDA。我们模拟了针对 bgm 电路在退火调度中的一次迭代。在禁用 L1 缓存的 GPU 硬件上,VPR 比串行 CPU 版本快 4 倍。

具有工作组内通信的基准测试集选自 Rodinia 基准测试套件 [15]、Bakhoda 等人 [8] 使用的基准测试以及 CUDA SDK [1]。选择这些基准测试是为了突出各种行为;我们没有排除任何 TC-Weak 性能比其他协议差的基准测试。我们评估的所有基准测试均列于表 3 中。
8 结果
本节比较了各一致性协议在 GPU 上的性能。第 8.3 节比较了 TC-Weak 与 TC-Strong。TCW 表示采用了第 6.4 节所述生存期预测器的 TC-Weak。
8.1 性能与互连流量
图 7(a) 比较了对于具有工作组间通信的应用,各一致性协议相对于禁用 L1 缓存的基线 GPU (NO-L1) 的性能。图 7(b) 比较了对于仅具有工作组内通信的应用,各协议相对于启用 L1 缓存的非一致基线协议 (NO-COH) 的性能。对于具有工作组间通信的应用,TCW 相比基线 GPU 实现了 85% 的调和平均性能提升。虽然所有协议在具有工作组间通信的应用上取得了相近的平均性能,但在没有此类通信的应用上,MESI 的性能显著差于写通过协议。这是 MESI 的 L1 写回写分配策略导致的结果,该策略有利于写入局部性,但为 GPU 应用中常见的一次性写入访问模式引入了不必要的流量。非包含式 GPU-VIni 可能具有更大的有效缓存容量,但并未带来超越包含式 GPU-VI 的性能优势。在 DLB 中,每个工作组从共享队列获取任务并插入任务。因此,任务获取和任务插入的失效延迟位于大量线程的关键路径上。TCW 消除了 DLB 中的这一关键路径失效延迟,性能比基于失效的协议快达 2 倍。

图 8(a) 和 8(b) 显示了不同一致性协议之间的互连流量细分。LD、ST 和 ATO 分别是来自加载、存储和原子请求的数据流量。MESI 在 L1 缓存执行原子操作,其流量包含在 ST 中。REQ 指所有协议的控制流量。INV 和 RCL 分别是失效流量和召回流量。
MESI 在 L1 的写分配策略由于对一次性写入数据的不必要数据填充,显著增加了存储流量。平均而言,MESI 在所有应用上相比基线非一致 GPU 增加了 75% 的互连流量。写通过协议 GPU-VI 和 GPU-VIni 引入了不必要的失效和召回流量,对于没有工作组间通信的应用,平均带来 31% 和 30% 的流量开销。对于这组应用,TCW 消除了所有失效和召回流量,因此相比 MESI、GPU-VI 和 GPU-VIni 分别减少了 56%、23% 和 23% 的互连流量。
8.2 功耗
图 9 显示了互连功耗和能量使用的细分。TCW 相比 MESI、GPU-VI 和 GPU-VIni,分别将互连功耗降低了 21%、10% 和 8%,将互连能量使用降低了 36%、13% 和 8%。这得益于动态功耗(由于互连流量降低)和静态功耗(由于 TCW 所需的虚拟通道缓冲区更少)的双重减少。
8.3 TC-Weak 与 TC-Strong
图 10(a) 和 10(b) 分别比较了 TC-Strong 和 TC-Weak 在所有应用上的调和平均性能和平均互连流量。TCS 表示采用 LCC [34, 54] 提出的 FIXED-DELTA 预测方案的 TC-Strong,该方案选择一个在所有应用上表现最佳的单一固定生存期。TCS 使用 800 个核心周期的固定生存期预测,该值被发现在其他生存期值中能产生最佳的调和平均性能。TCW-FIXED 使用 TC-Weak 和 3200 个核心周期的固定生存期,该值被发现在其他值中性能最佳。TCW 如前所述,表示采用我们提出的预测器的 TC-Weak。
TCW-FIXED 与 TCS 使用相同的预测器,但性能高出 15%,同时流量减少 13%。TCW 相比 TCS 实现了 28% 的性能提升,并将互连流量降低了 26%。TC-Strong 在较高生存期带来的额外写入暂停与较低生存期带来的额外 L1 缺失之间存在权衡。TC-Weak 通过不暂停写入来避免这种权衡。这允许使用更长的生存期和更少的 L1 缺失,从而相比 TC-Strong 提高了性能并减少了流量。

8.4 TC-Weak 性能分析
图 11 展示了 TC-Weak 在整个应用运行期间使用不同固定生存期预测值时的性能。图 11 中的向下箭头指示了 TCW 中的平均生存期预测值。性能随生存期增加而提升是由于 L1 命中率提高。性能随生存期过大而下降则是由栅栏暂停以及因存储被驱逐但未过期的时间戳引起的 L2 资源暂停所导致。注意在 DLB 中,生存期为 0 的 TCW-FIXED 比 NO-L1 快 3 倍,因为 TCW-FIXED 中使用 L1 MSHR 通过合并跨波前的冗余请求,将加载请求减少了 50%。该性能分析得出两个主要观察结果:首先,每个应用偏好不同的固定生存期。例如,NDL 的流式访问模式受益于短生存期,或等效于禁用 L1。相反,HSP 偏好长生存期以充分利用 L1 缓存。其次,指示 TCW 平均生存期的箭头靠近每个应用的峰值性能生存期。因此,我们的简单预测器能够有效地为这些应用中的每个基准测试定位最佳固定生存期。
8.5 目录大小扩展
图 12(a) 和 12(b) 比较了 TCW 与目录大小从 8 路组相联且条目数为 L1 块总数 2 倍 (VIni-2x-8w) 到 32 路组相联且条目数为 L1 块总数 16 倍 (VIni-16x-32w) 的 GPU-VIni 的性能和流量。在图 12(a) 中,目录大小和相联度对 GPU 应用的性能没有影响。在图 12(b) 中,虽然高相联度和大目录大小减少了工作组内通信中的一致性流量开销,但无法完全消除它们。图 12(c) 显示了 RG 在这些目录配置下的流量细分。随着目录大小从 2 倍增加到 16 倍,召回流量的减少被因内核间通信导致的失效流量增加所抵消。因此,虽然更大的目录可能减少召回流量,但真实通信带来的一致性流量成本无法消除。TCW 通过使用同步时间来促进通信,能够同时消除这两种一致性流量开销的来源。

9 结论
本文提出并解决了 GPU 缓存一致性带来的一系列挑战。我们发现传统的一致性实现不太适合 GPU。为管理数千个进行中内存访问的瞬态状态增加了硬件和复杂性开销。一致性给现有的 GPU 应用带来了不必要的流量开销。要加速同时包含一致性和非一致性数据的应用,需要后者引入最小的一致性开销。
我们提出了时序一致性,一种基于时间戳的一致性框架,可降低 GPU 一致性的开销。我们提出了该框架的一个实现——TC-Weak,它使用新颖的基于时间戳的内存栅栏来减少这些开销。
我们的评估表明,对于一组不含一致性数据的应用,带有简单生存期预测器的 TC-Weak 将 MESI、GPU-VI 和 GPU-VIni 目录一致性协议的流量分别降低了 56%、23% 和 22%。与基于 LCC 的 TC 协议 TC-Strong 相比,TC-Weak 性能快 28%,互连流量低 26%。对于一组需要一致缓存的应用,它相比禁用非一致 L1 缓存提供了 85% 的加速。支持 TC-Weak 的 GPU 为程序员提供了易于理解的内存一致性模型,并简化了不规则 GPU 应用的开发。
致谢
我们感谢 Mark Hill、Hadi Jooybar、Timothy Rogers 以及匿名评审们提出的宝贵意见。此项工作部分由加拿大自然科学与工程研究委员会和超威半导体公司的资助支持。
1199

被折叠的 条评论
为什么被折叠?



