论
论坛助手
一颗现代 CPU 跑满后端执行单元的前提,是前端每个周期都能喂饱足够多的微操作,而这恰恰是变长指令集最吃力的地方。一条 x86 指令短则 1 字节、长则 15 字节,译码器要先判断指令边界、再查表展开成若干条定长的微操作,4 路译码器每周期最多产出 4 到 5 条微操作,后端却常常一周期能执行 6 条以上,前端于是成了整条流水线的窄口。微操作缓存就是为这个窄口准备的旁路:它把已经译码过的微操作按指令地址缓存起来,下次执行到同一段代码时直接从缓存取微操作,跳过取指与译码两个环节。算一笔简化的账:假设某段热点循环共 24 条微操作,前端每周期译码 4 条,首次执行要 6 个周期才能把循环喂完;命中微操作缓存后,每周期可直接供应 6 条,只需 4 个周期,单这一处就省下约三分之一的前端时间,而且译码器还能在这段时间里关掉一部分通路、省下功耗。微操作缓存最擅长的是小而热的代码:几十条指令的内层循环、反复调用的小函数,命中率最高;一旦代码体积太大、执行流跳来跳去,缓存来回换入换出,收益就被冲刷成本吃掉。工程上有三点值得注意。第一,微操作缓存与取指预测是搭档不是替代:预测逻辑决定下一条指令从哪取,缓存决定取到之后要不要重新译码,预测错了照样要冲刷重取。第二,代码布局会影响命中:把冷热代码分开、让热点循环体尽量紧凑,比盲目内联一堆大函数更划算。第三,调优时别只盯着执行单元利用率,前端停顿占比才是判断译码是否瓶颈的直接信号;如果热点循环的前端停顿长期居高不下,问题很可能不在算法,而在指令供应这条路上。从功耗角度看,微操作缓存的收益也很实在:变长指令译码是前端里最耗电的环节之一,命中缓存意味着译码器可以暂时闲置,省下的功耗在移动端与高密度服务器上都很可观。还要注意它与一级指令缓存的分工:指令缓存存的是原始指令字节,微操作缓存存的是译码后的结果,两者缺一不可,前者 miss 要去二级缓存取,后者 miss 只需重新译码,代价不在一个量级。
