与指令执行并行的代码获取是如此重要,甚至 8086 也做到了(在有限的范围内,具有非常小的预取缓冲区和低带宽)。即便如此,取码带宽实际上是 8086 的主要瓶颈。
(我刚刚意识到你没有标记这个 x86,尽管你确实使用了 x86 指令作为示例。我的所有示例都是 x86,但对于任何其他体系结构来说,基础知识几乎都是相同的。除了非 x86 CPU 不会不使用解码的 uop 缓存,x86 是唯一仍然普遍使用的 ISA,它很难解码,因此值得缓存解码结果。)
在现代 CPU 中,取码很少成为瓶颈,因为caches预取隐藏了延迟,并且与数据所需的带宽相比,带宽要求通常较低。 (但是,代码占用量非常大的臃肿代码可能会因指令缓存未命中而导致速度减慢,从而导致前端停顿。)
L1I 缓存与 L1D 缓存分开,CPU 每个周期获取/解码至少 16 字节的 x86 代码块。具有解码 uop 缓存的 CPU(Intel Sandybridge 系列和 AMD Ryzen)甚至可以缓存已解码的指令以消除解码瓶颈。
See http://www.realworldtech.com/sandy-bridge/3/有关 Intel Sandybridge 中前端的相当详细的文章(获取/预解码/解码/重命名+问题),以及像这样的框图,显示了 Intel Sandybridge 与 Intel Nehalem 和 AMD Bulldozer 的指令获取逻辑。 (解码在下一页)。 “预解码”阶段找到指令边界(即在解码每条指令实际是什么之前解码指令长度)。
L1I 缓存未命中会导致向统一 L2 发出请求。现代 x86 CPU 还具有共享 L3 缓存(在多个内核之间共享)。
硬件预取将即将需要的代码引入 L2 和 L1I,就像将数据预取到 L2 和 L1D 中一样。大多数情况下,这隐藏了 DRAM > 200 个周期的延迟,通常仅在跳转到“冷”功能时失败。当运行一长串没有采取分支的代码时,它几乎总是可以领先于解码/执行,除非其他东西(如数据加载/存储)耗尽了所有内存带宽。
您可以构建一些以每个周期 16 字节进行解码的代码,这可能高于主内存带宽。或者在 AMD CPU 上甚至更高。但通常解码瓶颈会比纯粹的代码获取带宽限制更多。
也可以看看Agner Fog 的微架构指南了解有关各种微架构中的前端以及针对它们优化 asm 的更多信息。
另请参阅以下中的其他 CPU 性能链接x86标签维基。