Nam*_*ame 8 cpu-architecture speculative-execution
我已阅读维基百科页面关于无序执行和推测性的exectution.
我不能理解的是相似之处和不同之处.在我看来,当推测执行没有确定条件的值时,它会使用无序执行.
当我阅读Meltdown和Spectre的论文并做了进一步的研究时,出现了混乱.它在陈述消融纸即熔毁是基于乱序执行,而其他一些资源,包括对维基页面sepeculative执行状态消融是基于推测执行.
我想对此有所澄清.
Had*_*ais 10
推测执行和无序执行是正交的.人们可以设计一个处理器这个OoO但不是推测性或推测性的,而是有序的.OoO执行是一种执行模型,其中指令可以按可能与程序顺序不同的顺序执行.但是,指令仍按程序顺序退出,因此程序的观察行为与程序员直观预期的行为相同.(虽然可以设计一个OOO处理器,以某些不自然的顺序退出指令,但有一定的约束条件.请参阅基于仿真的研究,了解这个想法:最大化有限资源:基于限制的研究和无序提交的分类法.
从广义上讲,推测执行是一种执行模型,其中可以获取指令并进入管道甚至开始执行,甚至不必确定它们确实需要执行(根据程序的控制流程).该术语通常也用于具体指管道执行阶段的推测性执行.Meltdown论文确实在第3页定义了这些术语:
在本文中,我们以更受限制的含义引用推测性执行,其中它指的是分支之后的指令序列,并使用术语无序执行来指代在处理器具有之前执行操作的任何方式.承诺所有先前指示的结果.
请注意,指令可以推测性地执行,但仍然是有序的.当流水线的解码阶段识别条件分支指令时,它可以在分支及其目标上进行推测并从预测的目标位置获取指令.但是,指令也可以按顺序执行.但是,请注意,一旦推测的条件分支指令和从预测路径(或两个路径)获取的指令到达发布阶段,在所有先前的指令退出之前,它们都不会被发布.当发生这种情况时,处理器将知道预测是否正确并否则冲洗管道.
设计用于执行简单任务并在嵌入式系统或物联网设备中使用的处理器通常既不是推测性的也不是OoO.桌面和服务器处理器都是推测性的和OoO.在计算频谱(移动电话和微控制器)的中间,您可以找到OoO但不是推测的处理器(例如ARM Cortex-A9).英特尔Bonnell微体系结构是推测性的,但仍然是有序的.与OoO一起使用时,推测执行特别有用.
当我阅读Meltdown和Spectre的论文并做了进一步的研究时,出现了混乱.Meltdown论文中指出,Meltdown基于无序执行,而其他一些资源包括关于选择执行状态的wiki页面,Meltdown基于推测执行.
本文中描述的Meltdown漏洞需要推测和乱序执行.然而,这有点模糊,因为有许多不同的推测和无序执行实现.Meltdown不适用于任何类型的OoO或推测执行.例如,ARM11(在Raspberry Pis中使用)支持一些有限的OoO和推测执行,但它不容易受到攻击.
有关Meltdown及其他答案的详细信息,请参阅Peter的答案.
我仍然很难弄清楚 Meltdown 如何使用推测执行。论文中的示例(与我之前提到的相同)仅使用 IMO OoO - @Name 在评论中
Meltdown 基于 Intel CPU 乐观地推测负载不会发生故障,并且如果故障负载到达负载端口,则它是早期错误预测分支的结果。所以加载 uop 被标记,所以如果它到达退休它会出错,但是使用页表条目说你不允许从 user-space 读取的数据推测性地继续执行。
它不会在负载执行时触发代价高昂的异常恢复,而是等到它确实达到退休状态,因为这是机器处理分支未命中-> 坏负载情况的一种廉价方式。在硬件中,管道更容易保持管道,除非您需要它停止/停止以确保正确性。例如,根本没有页表条目的加载,因此 TLB 未命中,必须等待。但是,即使等待 TLB命中(对于具有阻止使用它的权限的条目)也会增加复杂性。通常,只有在页面遍历失败(找不到虚拟地址的条目)之后,或者在加载或存储失败的 TLB 条目的权限失败时才会引发页面错误。
在现代 OoO 流水线 CPU 中,所有指令都被视为推测性直到退休。只有在退休时,指令才变得非投机性。乱序机器并不真正知道或关心它是在预测已预测但尚未执行的分支的一侧进行推测,还是推测过去可能出现故障的负载。“推测”负载不会出错或 ALU 指令不会引发异常,即使在不被真正认为是推测的 CPU 中也会发生,但完全无序执行将其变成了另一种推测。
我不太担心“投机执行”的确切定义,以及什么重要/什么不重要。我对现代乱序设计的实际工作方式更感兴趣,并且在管道结束之前甚至不尝试区分推测性和非推测性实际上更简单。这个答案甚至没有试图通过推测性指令获取(基于分支预测)而不是执行来解决更简单的有序管道,或者在这与具有 OoO exec + in的 ROB + 调度程序的成熟 Tomasulo 算法之间的任何地方- 为精确的例外订购退休。
例如,只有在退休后,存储才能从存储缓冲区提交到 L1d 缓存,而不是之前。并且为了吸收短暂的突发和缓存未命中,它也不必作为退休的一部分发生。因此,唯一的非投机性乱序之一就是将商店提交给 L1d;就架构状态而言,它们肯定已经发生,因此即使发生中断/异常,它们也必须完成。
fault-if-reaching-retirement 机制是避免在分支预测错误的阴影下进行昂贵工作的好方法。如果异常确实触发,它还为 CPU 提供正确的架构状态(寄存器值等)。无论您是否让 OoO 机器在检测到异常的点之外继续搅动指令,您都需要这样做。
分支未命中是特殊的:有缓冲区记录分支上的微架构状态(如寄存器分配),因此分支恢复可以回滚到那个状态,而不是刷新管道并从最后一个已知良好的退休状态重新启动。分支确实会在实际代码中错误预测相当多的数量。其他例外是非常罕见的。
现代高性能 CPU 可以在分支未命中之前保持(无序)执行 uops,同时丢弃该点之后的 uops 和执行结果。快速恢复比从退休状态丢弃和重新启动所有内容要便宜得多,退休状态可能远远落后于发现错误预测的时间点。
例如,在一个循环中,处理循环计数器的指令可能远远领先于循环体的其余部分,并很快检测到末尾的错误预测以重定向前端,并且可能不会损失太多实际吞吐量,特别是如果瓶颈是依赖链的延迟或 uop 吞吐量以外的其他东西。
这种优化的恢复机制仅用于分支(因为状态快照缓冲区是有限的),这就是与完整管道刷新相比分支未命中相对便宜的原因。(例如,在英特尔上,内存排序机器清除,性能计数器machine_clears.memory_ordering
:生产者-消费者在超级兄弟与非超级兄弟之间共享内存位置的延迟和吞吐量成本是多少?)
不过,例外并非闻所未闻。页面错误确实发生在正常操作过程中。例如,存储到只读页面会触发写时复制。加载或存储到未映射的页面会触发页面输入或处理延迟映射。但是,即使在频繁分配新内存的进程中,通常也会在每个页面错误之间运行数千到数百万条指令。(在 1GHz CPU 上每微秒或毫秒 1 个)。在不映射新内存的代码中,您可以无例外地运行更长时间。大多数情况下只是偶尔在没有 I/O 的纯数字运算中的定时器中断。
但无论如何,在您确定异常真的会触发之前,您不希望触发管道刷新或任何昂贵的操作。而且你确定你有正确的例外。例如,可能较早的错误加载的加载地址没有尽快准备好,因此要执行的第一个错误加载不是程序顺序中的第一个。等到退休是获得精确例外的一种廉价方式。在处理这种情况的额外晶体管方面很便宜,并且让通常的有序退休机制快速准确地确定哪个异常触发。
在标记为退休时出错的指令之后执行指令的无用工作会消耗一点点能量,并且不值得阻塞,因为异常非常罕见。
这解释了为什么首先设计易受 Meltdown 影响的硬件是有意义的。 显然,这是不是安全的,继续这样做,现在消融已经想到了。
我们不需要在错误加载后阻止推测执行;我们只需要确保它实际上不使用敏感数据。问题不是推测性地成功加载,而是 Meltdown 基于以下指令使用该数据来产生依赖于数据的微体系结构效果。(例如,根据数据触摸缓存线)。
因此,如果加载端口将加载的数据屏蔽为零或其他内容,并设置了故障退休标志,则执行继续但无法获得有关机密数据的任何信息。这应该需要大约 1 个关键路径的额外门延迟,这在加载端口中可能是可能的,而不会限制时钟速度或增加额外的延迟周期。(1 个时钟周期足以让逻辑通过流水线级中的许多 AND/OR 门传播,例如完整的 64 位加法器)。
相关:我在为什么 AMD 处理器不会/不太容易受到 Meltdown 和 Spectre 的影响中为 Meltdown的硬件修复建议了相同的机制?.
归档时间: |
|
查看次数: |
2628 次 |
最近记录: |