如何找到英特尔x86 CPU解码指令的微操作?

Ale*_*x D 16 x86 intel compiler-optimization

英特尔优化参考,根据第3.5.1节,建议:

"赞成单微操作指令."

"避免使用复杂指令(例如,输入,离开或循环),这些指令超过4个微操作并需要多个周期才能解码.请使用简单指令序列."

虽然英特尔自己告诉编译器编写者使用解码为少数微操作的指令,但我在他们的任何手册中都找不到任何东西,这解释了每个ASM指令解码的微操作数量!这些信息是否随处可用?(当然,我希望不同代CPU的答案会有所不同.)

Cra*_*son 12

关于x86指令的Agner Fog的PDF 文档(链接主页Hans引用)是我在指令时序和微操作上发现的唯一参考.我从未见过关于微操作故障的英特尔文档.


And*_*bel 7

除了在其他答案(Agner Fog的表IACA)中已经提到的资源之外,您还可以在我们的网站uops.info上找到有关最新Intel CPU(从Nehalem到Cannon Lake)上大多数x86指令队列的详细信息。该网站还包含有关每个指令的延迟和吞吐量的信息。通过在实际硬件(使用硬件性能计数器)和不同版本的IACA上运行自动生成的微基准来获取数据。

相比于昂纳雾的指示表上的数据uops.info是在一些情况下更准确和精确。例如,考虑有关Nehalem的PBLENDVB指令。根据Agner Fog的表,该指令有一个仅可使用端口0的触发器和一个仅可使用端口5的触发器。这可能是基于以下观察结果:在单独地重复执行该指令时,存在平均而言,端口0上有一个触发器,端口5上有一个触发器。uops.info上的微基准测试表明,实际上这两个触发器都可以使用端口0和端口5。使用端口0或端口5。

uops.info上的数据还揭示了英特尔IACA中的一些错误。例如,在Skylake的CVTPI2PS XMM的两个触发器上,MM指令只能使用IACA中的端口0(http://uops.info/html-ports/SKL/CVTPI2PS_XMM_MM-IACA3.0.html)。在实际的硬件上,有一个只能使用端口0的触发器,并且有一个可以同时使用端口0和端口1的触发器。但是,他声称此设备只能使用端口1,这是不正确的。


Cod*_*ray 5

已经指出Agner Fog 的优化手册是一个极好的资源,特别是他的指令表,它几乎全面地涵盖了所有感兴趣的 x86 微体系结构。

\n\n

但您还有另一个选择:英特尔架构代码分析器 (IACA)Stack Overflow 上有一篇关于如何使用它的文章,但上手非常简单(尽管对于一次性分析来说有点乏味)。您只需下载可执行文件,围绕要分析的指令块发出一些序言和尾声代码(它包括用于此目的的 C 头文件 ( iacaMarks.h),可与各种编译器配合使用,或者您可以指示汇编器发出适当的代码字节),然后运行你的二进制文件iaca.exe。当前版本 (v2.2) 仅支持 64 位二进制文​​件,但这并不是主要限制,因为 32 位和 64 位模式的指令级分析不会有太大不同。当前版本还支持专业软件开发人员可能感兴趣的所有现代英特尔微架构,从 Nehalem 到 Broadwell。

\n\n

从该工具获得的输出将告诉您特定指令可以在哪些端口上执行,以及该指令将在指定的微架构上分解为多少个 \xc2\xb5ops。

\n\n

这与您要直接回答您的问题一样接近,因为正如Hans Passant 在评论中指出的那样,每个指令分解的确切 \xc2\xb5ops 被英特尔有意保密。它们不仅是专有的商业秘密,而且英特尔还希望能够自由地将其工作方式从一种微架构更改为另一种微架构。但事实上,优化代码时您只想知道一条指令分解为多少个\xc2\xb5ops。指令分解为哪个\xc2\xb5ops并不重要。

\n\n

但我想重申彼得·科德斯回答的一部分:“不过,在某些情况下很容易猜到”。如果您必须为您正在考虑的每条指令查找此类详细信息,您将浪费大量时间。你还会把自己逼疯,因为正如你所知,不同的微架构的情况是不同的。这里真正的技巧是直观地感受 x86 ISA 中哪些指令是“简单”的,哪些指令是“复杂”的。通过阅读文档,这一点应该非常明显,这种直观的感觉确实是英特尔优化建议所推动的。避免使用“复杂”(旧 CISC 风格)指令,例如LOOPENTERLEAVE等。例如,优先选择DEC+JNZ而不是LOOP。相对而言,只有一小部分“经典”x86 指令可以解码为多于一两个 \xc2\xb5ops。*研究一个好的优化编译器的输出也将引导您走向正确的方向,因为您永远不会看到编译器使用这些“复杂”指令。

\n\n

不过,与 Peter 的回答有些相反,我很确定 Intel 优化手册中引用的部分并不是SIMD 指令。他们谈论的是以微代码实现的老式 CISC 指令,如果他们不必支持它们向后兼容,那么这些指令就已经被放弃了。如果您需要 SSE3 的行为HADDPS,那么您最好使用HADDPS而不是尝试将其分解为“更简单”的组件。(当然,除非你可以更好地安排这些操作。但这在实践中很难做到。)

\n\n
\n\n

*为了完全准确,有些看似简单的指令实际上是使用微代码实现的,并分解为多个 \xc2\xb5ops。64 位除法 ( DIV) 就是一个示例。如果我没记错的话,这是使用 30\xe2\x80\x9340 \xc2\xb5ops (变量)之类的微编码的。然而,这并不是您应该避免的指令,这表明英特尔的手册在此处提供建议时非常笼统。如果需要进行除法,请使用DIV. 显然,在优化速度时喜欢进行除法,但也不要尝试编写自己的除法算法来避免微编码DIV

\n\n

这里的另一个大例外是字符串指令。不过,这些的性能计算比“避免因为它们解码为多个 \xc2\xb5ops”要复杂一些。

\n\n

幸运的是,一件事很简单:永远不要使用没有前缀的字符串指令REP。这根本没有意义,通过自己将指令“分解”为更简单的“组件”指令,您将获得显着更好的性能\xe2\x80\x94例如,MOVSBMOV AL, [ESI]+ MOV ES:[EDI], AL+ INC/DEC ESI+INC/DEC EDI

\n\n

当你开始利用前缀时,决定起来有点棘手REP。尽管这确实会导致指令解码为许多\xc2\xb5ops,但有时使用重复的字符串指令仍然比您自己手动编码循环更有效。但不总是。Stack Overflow 和其他地方已经对这个问题进行了很多讨论;例如,请参阅这个问题

\n\n

详细的分析确实超出了这个答案的范围,但我的快速经验法则是你可以完全忘记REP LOADSREP SCASREP CMPS。另一方面,当您需要重复相当多的次数时,REP MOVSREP STOS很有用。始终使用尽可能大的字大小:32 位上为 DWORD,64 位上为 QWORD(但请注意,在现代处理器上,最好使用/ ,因为它们可以在内部移动更大的数量MOVSBSTOSB即使所有这些条件都满足如果您的目标有可用的矢量指令,您可能想验证使用矢量移动实现移动/存储是否会更快。

\n\n

另请参阅第 150 页 Agner Fog 的一般建议

\n


归档时间:

查看次数:

3570 次

最近记录:

6 年,7 月 前