Joh*_*Doe 5 performance x86 assembly micro-optimization
测试 AL 中的字节是否为零/非零通常哪个更快?
TEST EAX, EAXTEST AL, AL假设前面的"MOVZX EAX, BYTE PTR [ESP+4]"指令加载了一个带有零扩展的字节参数到 EAX 的其余部分,从而防止了我已经知道的组合值惩罚。
因此 AL=EAX 并且读取 EAX 不会受到部分寄存器的影响。
直观上,仅检查 AL 可能会让您认为它更快,但我敢打赌,对于 >32 位寄存器的字节访问,需要考虑更多的惩罚问题。
任何信息/细节表示赞赏,谢谢!
代码大小相同,所有 x86 CPU 的性能也相同。
Intel CPU(具有部分寄存器重命名)在写入 EAX 后读取 AL 绝对不会受到惩罚。其他 CPU 也不会因读取低字节寄存器而受到惩罚。
读取 AH 会对 Intel CPU 造成影响,比如一些额外的延迟。(Haswell/Skylake上的部分寄存器到底表现如何?写AL似乎对RAX有错误的依赖,与AH不一致)
一般来说,32 位操作数大小和 8 位操作数大小(低 8 位而不是高 8 位)速度相同,除了错误依赖性或稍后写入8 位寄存器的部分寄存器读取惩罚之外。由于 TEST 仅读取寄存器,因此这不会成为问题。Even 也add al, bl很好:指令已经对两个寄存器都有输入依赖性,并且在 Sandybridge 系列上,寄存器低字节的 RMW 不会单独重命名它。(无论如何,Haswell 和后来的版本都不会单独重命名低字节寄存器)。
选择您喜欢的操作数大小。8位和32位基本相等。选择只是人类可读性的问题。如果稍后要将该值作为 32 位整数使用,则使用 32 位。如果它在逻辑上仍然是一个 8 位值,并且您仅将其用作ARM或 MIPSmovzx的 x86 等效项,那么使用 8 位是有意义的。ldrblbu
cmp al, imm像这样可以使用 no-modrm 短格式编码的 指令具有代码大小优势。cmp al, 0仍然比一些旧的 CPU(Core 2)更糟糕test al,al,其中cmp/jcc 宏融合的灵活性不如 test/jcc 宏融合。(使用 CMP reg,0 与 OR reg,reg 测试寄存器是否为零?)
这些指令之间有一个区别:根据 AL 的高位(可以非零) test al,al设置SF 。test eax,eax总是会清除SF。如果您只关心 ZF 那么这没有什么区别,但是如果您在 SF 中的高位用于以后的分支或 cmovcc/setcc 那么您可以避免执行第二个test.
如果您使用 setcc 或 cmovcc 而不是 jcc 分支来使用标志结果,那么宏融合在下面的讨论中并不重要。
如果稍后您还需要寄存器中的实际值,movzx//几乎肯定是最好test的jcc。否则,您可以考虑内存目标比较。
cmp [mem], immediate只要寻址模式不是 RIP 相关的,就可以在 Intel 上微熔丝到 load+cmp uop 中。(在 Sandybridge 系列上,即使在 Haswell 及更高版本上,索引寻址模式也不会层压:请参阅微融合和寻址模式)。Agner Fog 没有提及 AMD 对于将 cmp/jcc 与内存操作数融合是否有这种限制。
;;; no downside for setcc or cmovcc, only with JCC on Intel
;;; unknown on AMD
cmp byte [esp+4], 0 ; micro-fuses into load+cmp with this addressing mode
jnz ... ; breaks macro-fusion on SnB-family
Run Code Online (Sandbox Code Playgroud)
我没有 AMD CPU 来测试当 cmp 为 时,Ryzen 或任何其他 AMD 是否仍然融合 cmp/jcc mem, immediate。现代 AMD CPU通常执行 cmp/jcc 和 test/jcc 融合。(但不像 SnB 系列那样添加/子/和/jcc 融合)。
cmp mem,imm/jcc (与movzx/ test+jcc):
与主流英特尔上相同数量的前端/融合域微指令 (2)。cmp如果+负载的微融合不可能,例如使用RIP相对寻址模式+立即,则这将是3个前端uop 。或者在具有索引寻址模式的 Sandybridge 系列上,它会在解码后但在发送到后端之前取消层压至 3 uop。
优点:在 Silvermont/Goldmont/KNL 或没有宏融合的非常旧的 CPU 上,这仍然是 2。movzx/test/jcc 的主要优点是宏融合,因此它在没有这种情况的 CPU 上落后。
3 个后端 uops(未融合域 = 调度程序中的执行端口和空间,又名 RS),因为cmp-immediate 无法与 Intel Sandybridge 系列 CPU 上的 JCC 进行宏融合(在 Skylake 上测试)。uop 是 load、cmp 和单独的分支 uop。(相对于 2 而言movzx/ test+jcc)。后端微指令通常不是直接的瓶颈,但如果负载暂时没有准备好,它会占用 RS 中的更多空间,从而限制了这种乱序执行可以看到的程度。
cmp [mem], reg /jcc 可以将宏+微熔丝合并到单个比较+分支微指令中,所以它非常好。如果您需要在函数后面的任何内容中使用归零寄存器,请先对其进行异或归零,然后将其用于内存上的单微指令比较+分支。
movzx eax, [esp+4] ; 1 uop (load-port only on Intel and Ryzen)
test al,al ; fuses with jcc
jnz ... ; 1 uop
Run Code Online (Sandbox Code Playgroud)
对于前端来说这仍然是 2 uop,但对于后端来说也只有 2 uop。test/jcc 宏融合在一起。不过,它会花费更多的代码大小。
如果您不分支而是使用cmovccor的 FLAGS 结果setcc,则 usingcmp mem, imm没有任何缺点。只要您不使用 RIP 相对寻址模式(当存在立即数时,它总是阻止微融合)或索引寻址模式,它就可以进行微融合。
| 归档时间: |
|
| 查看次数: |
916 次 |
| 最近记录: |