我已阅读有关out-of-order execution和speculative exectution的维基百科页面。
我不能理解的是相似之处和不同之处。在我看来,当例如没有确定条件的值时,推测执行使用无序执行。
当我阅读Meltdown和Spectre的论文并做了更多的研究时,出现了混乱。在Meltdown paper中陈述了Meltdown基于无序执行,而其他一些资源,包括关于sepeculative execution的wiki页面,表明Meltdown基于推测执行。
我想对此有所澄清。
答案 0 :(得分:10)
推测执行和无序执行是正交的。人们可以设计一个处理器这个OoO但不是推测性或推测性的,而是有序的。 OoO执行是一种执行模型,其中指令可以按可能与程序顺序不同的顺序执行。但是,指令仍按程序顺序退出,以便程序观察到的行为与程序员直观预期的行为相同。 (尽管可以设计一个OoO处理器,它以某种不自然的顺序退出指令,但有一定的约束条件。请参阅基于模拟的研究这个想法:Maximizing Limited Resources: a Limit-Based Study and Taxonomy of Out-of-Order Commit)。
从广义上讲,推测执行是一种执行模型,可以在其中获取指令并进入管道甚至开始执行,甚至不必确定它们确实需要执行(根据程序的控制流程) 。该术语通常也用于具体指管道执行阶段的推测性执行。 Meltdown论文确实在第3页定义了这些术语:
在本文中,我们更多地参考推测执行 限制含义,指的是指令序列 跟随分支,并使用术语无序执行来引用 以任何方式在处理器执行之前执行操作 承诺所有先前指示的结果。
请注意,指令可以推测性地执行,但仍然是有序的。当流水线的解码阶段识别条件分支指令时,它可以在分支及其目标上进行推测并从预测的目标位置获取指令。但是,指令也可以按顺序执行。但是,请注意,一旦推测的条件分支指令和从预测路径(或两个路径)获取的指令到达发布阶段,在所有先前的指令退出之前,它们都不会被发布。当发生这种情况时,处理器将知道预测是否正确并否则冲洗管道。
设计用于执行简单任务并在嵌入式系统或物联网设备中使用的处理器通常既不是推测性的也不是OoO。桌面和服务器处理器都是推测性的和OoO。在计算频谱(移动电话和微控制器)的中间,您可以找到OoO但不是推测的处理器(例如ARM Cortex-A9)。英特尔Bonnell微体系结构是推测性的,但是有序。与OoO一起使用时,推测执行特别有用。
当我读到Meltdown和Spectre的文章时,出现了混乱 做了额外的研究。在Meltdown论文中说明了这一点 Meltdown基于无序执行,而另一些则是基于 资源包括关于选择执行状态的wiki页面 Meltdown基于投机执行。
本文所述的Meltdown漏洞需要推测和无序执行。然而,这有点模糊,因为有许多不同的推测和无序执行实现。 Meltdown不适用于任何类型的OoO或推测性执行。例如,ARM11(在Raspberry Pis中使用)支持一些有限的OoO和推测执行,但它不容易受到攻击。
有关Meltdown及其他answer的详细信息,请参阅彼得的答案。
相关:What is the difference between Superscalar and OoO execution?。
答案 1 :(得分:4)
我仍然很难搞清楚,Meltdown如何使用投机执行。本文中的示例(我之前提到的同一个)仅使用IMO OoO - @Name in a comment
消融是基于英特尔CPU乐观推测该负载赢得'吨故障,并且,如果一个断层负载达到负载端口,这是一个较早错误预测转移的结果。所以负载UOP被标记,以便如果达到退休年龄就错,而是继续执行推测的使用数据页表项说你AREN':T允许从用户空间读的
不是在负载执行时触发昂贵的异常恢复,而是等到它肯定达到退役,因为这是机器处理分支未命中的廉价方式 - &gt;负载不好的情况。在硬件中,管道更容易保持管道,除非您需要它以停止/停止正确性。例如没有页表条目,因此TLB未命中的负载必须等待。但是等待TLB 命中(对于具有阻止使用它的权限的条目)将会增加复杂性。通常一个页面故障只会被失败后,页面走上调(其中没有按&#39;找不到虚拟地址的条目),或在失败的TLB入口它击中的权限加载和存储的退休< / p>
在现代OoO流水线型CPU中,所有指令被视为推测直到退休。只有在退休时,指令才会变得非投机性。无序机器并不真正知道或关心它是否在推测已预测但尚未执行的分支的一侧,或推测过去的潜在故障负载。 &#34;投机&#34;加载不发生错误或ALU指令不会引发异常happens even in CPUs that aren't really considered speculative,但完全无序执行会将其转变为另一种推测。
我并不太担心&#34;投机执行&#34;的确切定义,以及什么不重要/什么不重要。我对现代无序设计的实际工作方式更感兴趣,而且在管道结束之前甚至不尝试区分投机与非推测设计实际上更为简单。此答案ISN&#39;吨甚至试图解决简单的按顺序输送管线用推测性指令取(基于分支预测),但不执行,或者在和full-blown Tomasulo's algorithm with a ROB + scheduler与OOO EXEC +有序退休之间的任何位置对于精确的例外。
例如,只有在退出之后,商店才能从商店缓冲区提交到L1d缓存,而不是之前。并且为了吸收短暂的爆发和缓存未命中,它也不必作为退休的一部分发生。因此,唯一一个非投机无序的事情是将商店提交给L1d;就架构状态而言,它们肯定已经发生,所以即使发生中断/异常,它们也必须完成。
如果在分支错误预测的阴影下避免昂贵的工作,那么故障 - 如果到达 - 退休机制是一种很好的方法。如果异常触发,它还为CPU提供正确的架构状态(寄存器值等)。无论你是否让OoO机器在超出你发现异常的地方的指令上搅拌,你确实需要这样做。
分支遗漏是特殊的:在分支上有记录 micro -architectural状态(如寄存器分配)的缓冲区,因此分支恢复可以回滚到而不是冲洗管道并从最后一个已知良好的退休状态重新开始。分支机构会在实际代码中错误预测相当数量。其他例外非常罕见。
现代高性能CPU可以在分支未命中之前保持(无序)执行uop,同时在该点之后丢弃uops和执行结果。快速恢复比丢弃和重新启动所有东西要便宜得多,退休状态可能远远落后于发现错误预测的点。
E.g。在一个循环中,处理循环计数器说明可能会远远领先于环体的其余部分,并检测错误预测在年底足够很快前端重定向,也许不会输多少真正的吞吐量,特别是如果瓶颈是依赖链的延迟或uop吞吐量之外的其他东西。
这种优化的恢复机制仅用于分支(因为状态快照缓冲区是有限的),这就是为什么分支未命中与完全管道刷新相比相对便宜的原因。 (例如,在英特尔,内存订购机器清除,性能计数器machine_clears.memory_ordering
:What are the latency and throughput costs of producer-consumer sharing of a memory location between hyper-siblings versus non-hyper siblings?)
但例外情况并非闻所未闻;页面错误确实发生在正常的操作过程中。例如存储到只读页面会触发copy-on-write。加载或存储到未映射的页面会触发页面调入或处理延迟映射。但是,即使在频繁分配新内存的过程中,数千到数百万条指令通常也会在每个页面错误之间运行。 (1GHz CPU上每微秒或毫秒1)。在没有映射新内存的代码中,您可以更长时间没有例外。大多数情况下,只有一个定时器中断偶尔会在没有I / O的情况下进行纯数字运算。
但无论如何,在您确定确实会触发异常之前,您不想触发管道刷新或任何昂贵的事情。并且您确定自己拥有正确的例外。例如也许早期故障负载的负载地址不能很快就绪,所以执行的第一个故障负载并不是程序中的第一个。等到退休是获得精确例外的廉价方式。在处理这种情况的额外晶体管方面很便宜,并让通常的有序退休机制确切地确定哪些异常火灾是快速的。
在退休后标记为故障的指令之后执行指令的无用工作耗费了一点力量,并且不值得阻止,因为异常是如此罕见。
这就解释了为什么是有意义的设计,这是脆弱的摆在首位,以崩溃硬件显然,这&#39; S的不的安全继续这样做,现在已经想到了Meltdown。
我们不需要在出现故障后阻止投机执行;我们只需要确保它实际上不使用敏感数据。并不是推测性地成功解决了问题,Meltdown基于以下使用该数据产生数据依赖性微架构效应的指令。 (例如,根据数据触摸缓存行)。
因此,如果加载端口将加载的数据屏蔽为零或某些内容以及设置故障退出标志,则执行继续但无法获取有关密钥数据的任何信息。这应该需要大约1个关键路径的额外门延迟,这在加载端口中可能是可能的,而不限制时钟速度或增加额外的延迟周期。 (1个时钟周期足够长,逻辑可以通过流水线级内的许多AND / OR门传播,例如一个完整的64位加法器。)
相关:我在Why are AMD processors not/less vulnerable to Meltdown and Spectre?中为Meltdown的硬件修复建议了相同的机制。