L2 TLB未命中后会发生什么?

问题描述 投票:19回答:1

我很难理解当翻译旁视缓冲区的前两个级别导致未命中时会发生什么?

我不确定特殊硬件电路中是否出现“页面行走”,或者页表是否存储在L2 / L3高速缓存中,或者它们是否只存在于主存储器中。

performance cpu cpu-architecture tlb
1个回答
19
投票

现代x86微体系结构有dedicated page-walk hardware。他们甚至可以推测性地执行页面遍历以在TLB未命中实际发生之前加载TLB条目。 Skylake甚至可以同时在飞行中进行两次漫步,请参阅Section 2.1.3 of Intel's optimization manual。这可能与页面拆分负载惩罚从100个周期下降到5个周期有关。

一些微体系结构protect you from speculative page-walks通过将未缓存的PTE推测性地加载但在第一次真正使用该条目之前用存储器修改为页面表来将其视为误推测。即,对于尚未在任何早期指令中被架构引用的仅推测TLB条目,存储到页表条目的存储。 (Win9x依赖于此,而不是破坏重要的现有代码是CPU供应商所关心的。当Win9x编写时,当前的TLB失效规则尚不存在,所以它甚至不是一个错误;请参阅下面引用的Andy Glew的评论)。 AMD Bulldozer系列违反了这一假设,只给出了x86手册在纸上所说的内容。


页面遍历硬件生成的页表加载可以在L1,L2或L3高速缓存中命中。例如,Broadwell perf counters可以计算您选择的L1,L2,L3或内存中的页面遍历命中(即缓存未命中)。 Oprofile称之为page_walker_loads

由于页面目录条目指向页表条目表的page tables use a radix-tree format,更高级别的PDE(页面目录条目)值得在页面遍历硬件内进行缓存。这意味着you need to flush the TLB in cases where you might think you didn't need to。英特尔和AMD实际上是这样做的,according to this paper (section 3)

那篇论文说,AMD CPU上的页面遍历加载会忽略L1,但会通过L2。 (也许是为了避免污染L1,或减少读端口的争用)。无论如何,这使得在页面漫游硬件内部缓存一些高级PDE(每个都覆盖许多不同的翻译条目)甚至更有价值,因为指针追逐链更昂贵且延迟更高。

但请注意,x86不保证TLB条目的负缓存。将页面从无效更改为有效不需要invlpg。 (因此,如果一个真正的实现确实想要进行那种负面缓存,它必须窥探或以某种方式仍然实现x86手册保证的语义。)

(历史记录:Andy Glew's answer to a duplicate of this question over on electronics.SE说,在P5及更早版本中,硬件页面遍历加载绕过内部L1缓存(但它通常是直写,因此页面行走与商店一致).IIRC,我的Pentium MMX主板上有L2缓存mobo,也许是作为内存端缓存.Andy还确认P6和后来从正常的L1d缓存加载。另一个答案最后也有一些有趣的链接,包括我在最后一段末尾链接的文章。它似乎也认为操作系统可能会更新TLB本身,而不是页面表,页面错误(HW pagewalk没有找到条目),并想知道是否可以在x86上禁用HW页面行走。(但实际上操作系统只修改内存中的页表,从#PF返回重新运行错误指令,这样HW pagewalk这次就会成功。)

我认为实际上不可能在P5(或任何其他x86)上禁用HW pagewalk。这将要求软件使用专用指令(没有一个)或wrmsr或MMIO存储来更新TLB条目。令人困惑的是,Andy说(在我下面引用的一个帖子中),P5上的软件TLB处理速度更快。如果有可能的话,我认为他的意思会更快。当时他正在Imation(在MIPS上)工作,其中SW页面漫步是一个选项(有时是唯一选项),与x86 AFAIK不同。


正如Paul Clayton points out(关于TLB未命中的另一个问题),硬件页面遍历的一大优势是TLB未命中并不一定会使CPU失速。 (乱序执行正常进行,直到重新命令缓冲区填满,因为加载/存储不能退出。退休按顺序发生,因为CPU无法正式提交任何不应该发生的事情,如果以前的指示有问题。)

顺便说一下,可能有可能构建一个x86 CPU,通过捕获到微码来处理TLB未命中,而不是专用硬件状态机。这会(很多?)性能较差,也许不值得推测性地触发(因为从微代码发出uops意味着你不能从正在运行的代码发出指令。)

如果你在SMT风格的单独硬件线程(interesting idea)中运行那些微操作,那么微编码TLB处理在理论上可能是非常糟糕的。你需要它比正常的超线程更少的启动/停止开销,用于从单线程切换到两个逻辑核心活动(必须等待事情消耗,直到它可以分区ROB,存储队列等),因为与通常的逻辑核心相比,它会经常启动/停止。但是,如果它不是一个完全独立的线程而只是一些单独的退出状态,那么这可能是可能的,因此其中的缓存未命中不会阻止主代码的退出,并且它使用一些隐藏的内部寄存器用于临时。它必须运行的代码由CPU设计者选择,因此额外的HW线程不必接近x86核心的完整架构状态。它很少需要做任何存储(可能只是为了PTE中访问的标志?),所以让这些存储使用与主线程相同的存储队列也不错。您只需将前端分区以混合TLB管理uops,并让它们与主线程无序执行。如果你可以保持每页分页的uop数量很小,那可能不会太糟糕。

在我所知道的单独的HW线程中,没有CPU实际上使用微代码进行“HW”页面遍历,但这是理论上的可能性。


In some RISC architectures (like MIPS), the OS kernel is responsible for handling TLB misses。 TLB未命中导致执行内核的TLB未命中断处理程序。这意味着操作系统可以在这种体系结构上自由定义自己的页表格式。我想在写入后将页面标记为脏也需要陷阱到OS提供的例程,因为CPU不知道页表格式。

This chapter from an operating systems textbook解释了虚拟内存,页表和TLB。它们描述了软件管理的TLB(MIPS,SPARCv9)和硬件管理的TLB(x86)之间的区别。

如前所述,如果禁用HW页面漫游功能,则SW TLB管理是x86上的一个选项,并且在P5上获胜。

其他链接:


Comments about TLB coherency from Andy Glew, one of the architects on Intel P6 (Pentium Pro / II / III), then later worked at AMD.

英特尔开始运行页表的主要原因是通过缓存,而不是绕过缓存,是性能。在P6之前,页面表行走缓慢,没有从缓存中受益,并且是非推测性的。足够慢的软件TLB错过处理是一个性能win1。 P6加速TLB通过使用缓存,以及通过缓存页面目录条目等中间节点来推测它们而错过了。

顺便说一句,AMD不愿意投机地进行TLB错过处理。我想因为他们受到DEC VAX Alpha架构师的影响。其中一位DEC Alpha建筑师相当强烈地告诉我TLB未命中的投机处理,例如P6正在做的,是不正确的,并且永远不会奏效。当我在2002年左右到达AMD时,他们仍然有一个叫做“TLB Fence”的东西 - 不是围栏指令,而是在TLB错过的rop或微码序列中的一个点可能会或不会被允许发生 - 我害怕我不记得它究竟是如何运作的。

所以我认为推土机放弃了TLB和页面表走的一致性并不是那么多,因为推土机可能是第一台进行适度侵略性TLB未命中处理的AMD机器。

回想一下,当P6启动时,P5没有发货:现有的x86都是按顺序缓存旁路页面表,非推测性地,没有异步预取,而是通过缓存写入。即它们是缓存一致的,并且OS可以依赖于TLB条目的确定性替换。 IIRC我为TLB条目以及数据和指令缓存编写了关于推测性和非确定性可缓存性的架构规则。您不能因为不遵循当时不存在的页表和TLB管理规则而责怪Windows,UNIX和Netware等操作系统。

IIRC我为TLB条目以及数据和指令缓存编写了关于推测性和非确定性可缓存性的架构规则。您不能因为不遵循当时不存在的页表和TLB管理规则而责怪Windows,UNIX和Netware等操作系统。

脚注1:据我所知,没有x86 CPU支持软件TLB管理。我认为安迪打算在P5上说“本来会更快”,因为无论如何它都不会是推测性的或无序的,并且运行带有物理地址的x86指令(禁用分页以避免捕获22)会允许页面表加载的缓存。 Andy可能正在考虑MIPS,这是他当时的日常工作。


More from Andy Glew from the same thread, because these comments deserve to be in a full answer somewhere.

(2)我最大的遗憾之一是P6是我们没有提供内部指令TLB一致性支持。某些指令多次访问同一页面。同一指令中的不同uops可以为同一地址获得不同的翻译。如果我们给微码提供了保存物理地址转换的能力,然后使用它,那么事情会更好恕我直言。

(2a)当我加入P6时,我是RISC的支持者,我的态度是“让SW(微码)做到这一点”。

(2a')最令人尴尬的错误之一与内存中的附加携带有关。在早期的微码。负载将进行,进位标志将被更新,并且存储可能出错 - 但进位标志已经更新,因此指令无法重新启动。 //这是一个简单的微码修复,在写入进位标志之前进行存储 - 但是一个额外的uop足以使该指令不适合“中速”ucode系统。

(3)无论如何 - 主要的“支持”P6及其后代处理TLB一致性问题是在报告错误之前在退休时重新铺设页面表。这避免了在页表表示不应该有故障时通过报告故障来混淆操作系统。

(4)元评论:我认为任何架构都没有正确定义缓存无效TLB条目的规则。 // AFAIK大多数处理器不会缓存无效的TLB条目 - 除了可能的Itanium及其NAT(非A Thing)页面。但是有一个真正的需要:推测性内存访问可能是通配地址,错过TLB,执行昂贵的页面表行走,减慢其他指令和线程 - 然后一遍又一遍地执行它因为“这是一个坏事实地址,不需要走页表“不记得。 //我怀疑DOS攻击可以使用它。

(4')更糟糕的是,操作系统可能会做出隐含的假设,即永远不会缓存无效的转换,因此在从无效转换为有效时不会导致TLB失效或MP TLB击落。 //更糟糕^ 2:想象一下你正在缓存页表缓存的内部节点。想象一下,PD包含所有无效的PDE;更糟糕的是,PD包含指向全部无效的PT的有效d PDE。您是否仍然可以缓存这些PDE?操作系统何时需要使条目无效?

(4'')因为使用处理器间中断的MP TLB射击是昂贵的,OS性能的人(就像我以前一样)总是在做“我们不需要在将PTE从无效变为有效之后使TLB无效”之类的参数。或“从有效只读到有效可写与不同地址”。或者“在将PDE更改为指向PTE与原始PT完全相同的不同PT后,我们不需要使TLB无效......”。 //很多巧妙的论点。不幸的是并不总是正确

我的一些计算机架构师朋友现在支持连贯的TLB:侦听写入的TLB就像数据缓存一样。主要是允许我们构建更具侵略性的TLB和页表缓存,如果叶子和内部节点的有效和无效条目都是如此。而不必担心操作系统人员的假设。 //我还没到那里:低端硬件太贵了但可能值得在高端做。

我:神圣的废话,所以这是额外的ALU uop来自内存目标ADC,即使在Core2和SnB系列?永远不会猜到,但一直困惑于此。

Andy:通常当你“做RISC事情”时,需要额外的指令或微指令,仔细的顺序。然而,如果您具有“CISCy”支持,例如特殊硬件支持,以便单个指令是一个事务,要么全部完成,要么全部未完成,可以使用更短的代码序列。

类似的东西也适用于自我修改代码:我们想让自修改代码快速运行,因为试图使自己修改代码的遗留机制 - 排除管道以便序列化CPUID等指令 - 并不是那么简单窥探Icache和管道。但是,这又适用于高端机器:在低端机器上,传统机制足够快且便宜。

同上内存排序。高端窥探速度更快;低端排水更便宜。

很难保持这种二分法。

特定实现必须实现与架构语句兼容但更强的规则,这是很常见的。但并非所有实现都必须以相同的方式执行。

这个评论主题是关于Andy对自修改代码和看到陈旧指令的问题的回答;另一种情况是真正的CPU超出了纸面上的要求,因为如果你没有跟踪分支之间发生的事情,那么总是更容易窥探EIP / RIP附近的商店而不是只重新分支分支指令。

© www.soinside.com 2019 - 2024. All rights reserved.