首先,处理器不需要知道要获取多少字节,它可以获取方便的字节数,足以为典型或平均指令长度提供目标吞吐量。任何额外的字节都可以放在缓冲区中,以用于下一组要解码的字节。相对于支持的指令解码宽度,甚至相对于流水线后面部分的宽度,在获取的宽度和对齐方面存在折衷。获取比平均更多的字节可以减少指令长度可变性的影响以及与所采用的控制流指令相关的有效获取带宽。
(如果 [predicted] 目标在下一次取指之后的一个周期之前不可用,则采取的控制流指令可能会引入取指气泡,并减少有效取指带宽与目标对齐程度低于指令取指。例如,如果取指是 16 字节对齐的——这在高性能 x86 中很常见——一个以块中第 16 个 [最后一个] 字节为目标的分支将导致实际上只获取一个字节的代码,而其他 15 个字节被丢弃。)
即使对于固定长度的指令,每个周期获取多条指令也会带来类似的问题。一些实现(例如,MIPS R10000)将获取尽可能多的指令,即使它们没有对齐,只要指令组不跨越高速缓存行边界。 (我似乎记得有一个 RISC 实现了两个 Icache 标签库,以允许获取跨越缓存块边界而不是页面边界。)其他实现(例如,POWER4)即使针对目标为最后一个的分支也会获取对齐的代码块在这样一个块中的指令。 (对于 POWER4,使用了包含 8 条指令的 32 字节块,但每个周期最多可以通过 5 条指令解码。可以利用这种多余的提取宽度通过不执行提取的周期来节省能量,并为缓存块填充提供空闲的 Icache 周期在只有一个 Icache 读/写端口的情况下丢失后。)
对于每个周期解码多条指令,实际上有两种策略:推测性并行解码或等待长度确定,然后使用该信息将指令流解析为单独的指令。对于像 IBM 的 zArchitecture(S/360 后代)这样的 ISA,16 位包中的长度通常由第一个包中的两位确定,因此等待确定长度更有意义。 (RISC V's 稍微复杂一点的长度指示机制仍然对非推测解码友好。)对于像 microMIPS 或 Thumb2 这样的编码,它们只有两个可由主要操作码确定的长度,并且编码不同长度的指令有很大不同,使用非推测解码可能是首选,尤其是考虑到可能较窄的解码和强调能效,尽管只有两个长度,一些推测在较小的解码宽度下可能是合理的。
对于 x86,AMD 用于避免过度使用解码能量的一种策略是在指令缓存中使用标记位来指示哪个字节结束一条指令。有了这样的标记位,就很容易找到每条指令的开始。这种技术的缺点是它增加了指令缓存未命中的延迟(指令必须被预解码)并且它仍然需要解码器检查长度是否正确(例如,如果跳转到之前的指令的中间)。
英特尔似乎更喜欢推测性并行解码方法。由于要解码的块中前一条指令的长度仅在适度延迟后可用,因此第二个和后面的解码器可能不需要对所有起始点的指令进行完全解码。
由于 x86 指令可能相对复杂,因此通常还存在解码模板约束,并且至少一个早期的设计限制了可以使用的前缀数量,同时保持完整的解码带宽。例如,Haswell 将解码的第二条到第四条指令限制为仅产生一个微指令,而第一条指令最多可以解码为四个微指令(使用微码引擎具有更长的微指令序列)。基本上,这是对常见情况(相对简单的指令)的优化,以牺牲不太常见的情况为代价。
在最近的以性能为导向的 x86 设计中,英特尔使用了一个 µop 缓存,它以解码格式存储指令,避免了模板和提取宽度限制,并减少了与解码相关的能源消耗。