我知道 8086 有一个 BIU 和一个 EU,这有助于对处理器进行流水线化。BIU 有一个 6 字节的预取队列,用于提取指令指针指向的地址之后的字节。现在,当要执行的指令是跳转到另一个位置的指令时,所有预取的 6 字节会发生什么情况?他们会被冲洗然后重新加载吗?(这会破坏处理器的流水线效率,不是吗?)
1 回答
是的,跳转会丢弃 8086 中的指令预取队列以及类似工作的后续微架构。任何控制传输后的指令取指都以空缓冲区开始。
对于 JIT / 自修改代码,这意味着任何跳转都足以避免过时的指令获取。
他们会被冲洗然后重新加载吗?这会破坏处理器的流水线效率,不是吗?
缓冲区中的指令来自错误的路径,除非它是jmp +0
nop。所以它们不会重新加载;它们没用,必须加载正确的路径。
这不是很好,而且是跳跃的额外费用。 这就是为什么后来的像 Pentium 这样的有序 CPU 具有分支预测功能,因此它们可以在跳转甚至解码之前从正确的路径中获取。 (分支预测需要预测分支的存在,例如给定一个提取块地址,预测接下来要提取的块。以及预测条件分支将走向哪条路。)
8086 很难像 5 级 RISC 那样高效。
无论如何,指令取指通常是 8086 的主要瓶颈,因此缓冲区可能在大多数跳转时通常不会满。您最多只会丢失 6 个字节(3 个字的提取)浪费的预取工作,而且可能更少。(这就是为什么在 8086 上优化速度几乎可以优化代码大小,除了避免像乘法这样的一些慢指令。这也是为什么 x86 的紧凑型可变长度指令对于 8086 来说是一个很好的设计。)
我不知道跳转解码/执行需要多长时间,但是跳转是 2 或 3 个字节长(在 x86-16 中),甚至 4+ 用于使用 opcode+modrm+disp16 + 可选前缀的间接跳转。刚刚执行的跳转指令可能使 8086 上的预取缓冲区接近空。
在纸面上,x86 ISA 至少曾经要求像序列化指令一样iret
或cpuid
避免任何过时指令的风险。但是为了避免破坏现有代码,真正的 x86 CPU 只需要一次跳转1
现代 OoO x86 CPU 具有分支预测 + 推测执行(如 P6 系列)不需要任何东西;他们积极地窥探管道以检测与运行中指令重叠的存储。 使用自修改代码观察 x86 上的陈旧指令获取
脚注 1:超越纸面规范以保持与广泛使用的软件的兼容性是 x86 多年来的普遍现象;真正的向后兼容和二进制兼容性基本上是 x86 与更清洁的 RISC ISA 的主要卖点,直到 x86 变得如此占主导地位,以至于其他 ISA 放弃了瞄准高功率/高性能市场。(多亏了不断增长的晶体管预算和巧妙的设计理念,使得支付“x86 税”并仍然运行得很快。
对于无法运行现有版本的 DOS、Windows、Lotus Notes 或其他任何版本的更快的新 CPU,市场会遇到巨大的阻力。