首先,术语:
通常,至少在英特尔,中断是来自外部世界的东西。通常它与处理器上执行的指令不同步,即它是一个异步外部中断。
在英特尔术语中,异常是由处理器上执行的指令引起的。例如。页面错误或未定义的指令陷阱。
---+ 中断刷新所有正在运行的指令
在我熟悉的每台机器上 - 例如。自 P5 以来的所有 Intel 处理器(我在 P6 上工作)、AMD x86s、ARM、MIPS - 当收到中断信号时,管道中的指令几乎总是被刷新、丢弃。
我说“几乎总是”的唯一原因是,在其中一些机器上,您并不总是在允许您接收中断的地方。因此,您继续到允许中断的下一个地方 - 通常是任何指令边界 - 然后丢弃管道中的所有指令。
就此而言,中断可能会被阻止。所以你继续直到中断被解除阻塞,然后你把它们扔掉。
现在,这些机器并不是完全简单的 5 级流水线。尽管如此,这种观察 - 大多数机器丢弃管道中的所有指令,在中断逻辑所在的管道阶段之前的管道阶段中 - 几乎普遍正确。
在简单机器中,中断逻辑通常位于流水线的最后阶段 WB,大致对应于高级机器的提交流水线阶段。有时它会被移到之前的管道阶段,例如MEM 在你的例子中。所以,在这样的机器上,IF ID EX 中的所有指令,通常是 MEM,都被丢弃了。
---++ 为什么我关心:避免浪费的工作
这个话题很贴近我的心,因为我建议不要这样做。例如。在我们计划构建 P6 时的客户访问中,我询问客户他们更喜欢哪一个 - 较低的延迟中断、正在运行的刷新指令,或(稍微)更高的吞吐量,至少允许完成一些正在运行的指令,在延迟稍长的代价。
然而,虽然有些客户更喜欢后者,但我们还是选择了做传统的事情,立即冲水。除了较低的延迟之外,主要原因是复杂性:
例如如果您接受中断,但如果已经在运行的指令之一也发生异常,则在您重新转向 IF(指令获取)之后但在中断中的任何指令提交之前,哪个优先? - 答:这取决于。这种事情处理起来很痛苦。
---+++ 民间传说:大型机操作系统中断批处理
这很像一些 IBM 大型机操作系统据报道的运行方式:
- 在正常操作中所有中断都被阻塞,除了定时器中断;
- 在定时器中断中,你解除阻塞,并处理它们;
- 然后以中断阻塞模式返回正常操作
可以想象,他们可能只在重负载时使用这种“中断批处理”模式;如果负载较轻,它们可能不会阻塞中断。
---+++ 延迟机器检查异常
延迟中断以使已经在管道中的指令有机会执行的想法也类似于我所说的延迟机器检查异常——我在大约 1991 年的原始英特尔 P6 系列机器检查架构中包含了一个概念—— 1996 年,但似乎尚未发布。
问题来了:机器检查错误,如(不可)纠正的 ECC 错误,可能发生在指令退役之后(即,在假设较年轻的指令已提交状态之后,例如写入寄存器之后),或在指令退役之前。
AFTER 错误的典型示例是由存储触发的不可纠正的 ECC,该存储在毕业时被放入写入缓冲区。几乎所有现代机器都这样做,所有具有 TSO 的机器,这几乎意味着总是存在不精确的机器检查错误的可能性,如果您足够小心不要缓冲存储,则可能是精确的。
BEFORE 错误的经典例子是……嗯,每条指令,在任何有管道的机器上。但更有趣的是,错误路径指令的错误,在分支预测错误的阴影下。
当加载指令出现无法纠正的 ECC 错误时,您有两种选择:
(1) 您可以立即拉动链条,不仅会杀死比加载指令年轻的指令,还会杀死任何旧指令
(2) 或者您可以将某种状态代码写入控制推测的逻辑中,并在退休时处理异常。对于页面错误,这几乎是您必须做的事情,它使此类错误变得精确,有助于调试。
(3) 但是,如果导致无法纠正的 ECC 错误的加载指令是错误的路径指令,并且永远不会因为旧的飞行分支预测错误而退出,该怎么办?
好吧,您可以写下状态以使其准确。您应该有精确错误和不精确错误的计数器。否则,您可以忽略此类错误路径指令上的错误 - 毕竟,如果它是一个硬错误,它要么会再次被触及,要么可能不会被触及。/例如该错误可能在架构上是无声的 - 例如一个坏的缓存行可能会被同一地址的一个好的缓存行覆盖。
而且,如果你真的想要,你可以设置一点,这样如果旧的分支预测错误,那么你会在那个时间点进行机器检查异常。
这样的错误不会发生在与导致错误的指令关联的程序计数器上,但可能仍具有其他精确状态。
我调用 (2) 推迟机器检查异常; (3) 就是您处理延期的方式。
IIRC,所有 Intel P6 机器检查异常都不精确。
---++ 在紧握的手上:更快
所以,我们已经讨论过了
0) 立即接受中断,或者,如果中断被阻塞,则执行指令和微指令,直到达到中断未阻塞点。然后在飞行中刷新所有指令。
1) 尝试执行流水线中的指令,避免浪费工作。
但还有第三种可能:
-1) 如果您有微架构状态检查点,请立即中断,永远不要等待中断未阻塞点。只有在最近的“安全中断”点有一个所有相关状态的检查点,你才能做到这一点。
这甚至比 0) 还要快,这就是我将其标记为 -1) 的原因。但它需要检查点,许多但不是所有激进的 CPU 都使用这些检查点 - 例如。 Intel P6 不使用检查点。在存在共享内存的情况下,这样的退休后检查点会变得很时髦——毕竟,您可以在中断被阻止时执行诸如加载和存储之类的内存操作。您甚至可以在 CPU 之间进行通信。即使是硬件事务内存通常也不会这样做。
---+ 异常标记受影响的指令
相反,异常,比如页面错误,会标记受影响的指令。
当该指令即将提交时,此时将刷新异常之后的所有后续指令,并重定向指令获取。
可以想象,可以更早地重新引导指令获取,就像大多数处理器上已经处理分支错误预测的方式一样,此时我们知道异常将会发生。我不知道有谁这样做。在当前的工作负载中,异常并不那么重要。
---+ "软件中断"
“软件中断”是一条命名错误的指令,通常与系统调用相关。
可以想象,可以在不中断流水线的情况下处理这样的指令,就像分支一样预测。
但是,我熟悉的所有机器都以某种方式进行序列化。用我的话来说,他们不会重命名权限级别。
---+“精确中断”,EMON,PEBS
另一张海报提到了精确的中断。
这是一个历史术语。在大多数现代机器上,中断被定义为精确的。具有不精确中断的旧机器在市场上并不是很成功。
但是,我参与介绍了另一个含义:当我让英特尔添加在性能计数器溢出时产生中断的能力时,首先使用外部硬件,然后在 CPU 内部,它是在前几代,完全不精确。
例如您可以设置计数器来计算退出的指令数。引退逻辑 (RL) 将看到指令引退,并向性能事件监控电路 (EMON) 发出信号。将此信号从 RL 发送到 EMON 可能需要两到三个时钟周期。 EMON 会增加计数器,然后看到有溢出。溢出将触发对 APIC(高级可编程中断控制器)的中断请求。 APIC 可能需要几个周期
弄清楚发生了什么,然后发出退休逻辑信号。
即EMON 中断将不精确地发出信号。不是在事件发生时,而是在之后的某个时间。
为什么会出现这种不精确?好吧,在 1992-6 年,性能测量硬件并不是一个高优先级。我们正在利用现有的中断硬件。乞丐不能挑剔。
但此外,某些性能本质上是不精确的。例如。你什么时候会在一条永不退休的推测指令上发出缓存未命中的中断信号? (我有一个称为 Deferred EMON events 的方案,但这仍然被认为过于昂贵。)对于那个问题,存储指令的缓存未命中情况如何,存储被放入存储缓冲区,并且指令已经退出?
即有时性能事件发生在与它们关联的指令已提交(退出)之后。有时以前。而且通常不完全按照它们相关的指令。
但据我所知,到目前为止,在所有实现中,这些性能事件都被视为中断:管道中的现有指令被刷新。
现在,您可以将表演事件视为一个陷阱,从而使其更加精确。例如。如果它是类似指令退休的事件,您可以立即让退休逻辑陷阱,而不是采取我上面描述的那个迂回循环。如果它在流水线中较早发生,您可以在 ROB(重新排序缓冲区)中的指令故障状态中标记它发生的事实。这就是英特尔对 PEBS(基于事件的精确采样)所做的事情。 http://software.intel.com/sites/products/collateral/hpc/vtune/performance_analysis_guide.pdf.
但是,请注意,并非所有事件都可以使用 PEBS 进行采样。例如,上面示例中的 PEBS 可以计算缓存命中或未命中的负载,但不能计算存储(因为存储稍后发生)。
所以这就像例外:仅在指令退出时才传递事件。因为从某种意义上说,事件还没有完全发生——它是一个加载指令,它需要一个缓存未命中,然后退出。并且在标记的 PEBS 指令之后的指令从流水线中被刷新。
我希望 ---+ 关于早期计算机的后期补充