当前位置: 首页 > 知识库问答 >
问题:

用CMP reg,0 vs或reg,reg测试寄存器是否为零?

魏威
2023-03-14
cmp al, 0
je done
or al, al
jz done

我知道JE和JZ指令是相同的,而且使用OR给出了一个字节的大小改进。然而,我也关心代码速度。似乎逻辑运算符会比SUB或CMP更快,但我只是想确定一下。这可能是大小和速度之间的权衡,或者是双赢(当然代码会更加不透明)。

共有1个答案

巫培
2023-03-14

是的,在性能上是有区别的。

将寄存器与零进行比较的最佳选择是test reg,reg。它设置标志的方式与cmp reg、0相同,并且1至少与任何其他方式一样快,代码大小更小。

(更好的情况是,ZF已经由设置reg的指令适当设置,这样您就可以直接分支、setcc或cmovcc了。例如,普通循环的底部通常看起来像dec ecx/jnz.loop_top。大多数x86整数指令“根据结果设置标志”,如果输出是0,则包括ZF=1。)。

    null

signed-compare条件还允许您执行jlejg,查看ZF和SF!=of。

test的编码时间比cmp短,直接为0,除了cmp al,imm8特例仍为两个字节。

即便如此,test还是更好的,因为宏融合的原因(对于jle,在Core2上类似),并且因为根本没有immediate可以通过留下一个插槽来提高uop-cache密度,如果另一条指令需要更多的空间(SnB-family)。

P6系列CPU(PPro/PII到Nehalem)具有有限数量的寄存器读取端口,用于发布/重命名阶段从永久寄存器文件读取“冷”值(不是从运行中指令转发的),但最近写入的值可以直接从ROB中获得。不必要地重写寄存器可能会使其再次存在于转发网络中,以帮助避免寄存器读取停滞。(参见Agner Fog的microarch pdf)。

在P6中,用相同的值重写寄存器以使其保持“热”,实际上对于周围代码的某些情况是一种优化。早期的P6系列CPU根本不能进行宏融合,所以使用和reg(reg代替test)就不会错过这一点。但是Core2(在32位模式下)和Nehalem(在任何模式下)都可以宏熔断test/jcc,所以您错过了这一点。

(在P6系列中等效于,但如果您的代码曾经在SandyBridge系列CPU上运行,情况就不那么糟糕了:它可以宏融合/JCC,但不能宏融合/JCC。寄存器DEP链中额外的延迟周期仍然是P6的一个缺点,尤其是涉及它的关键路径是主要瓶颈的情况下。)

在早期的P6-family、和reg中,reg可能可以作为默认的code-gen选择,如果该值不是有问题的循环携带的dep链的一部分,而是稍后读取的。或者如果是,但是也有一个特定的寄存器读取延迟,您可以用和reg,reg来修复。

如果您只想测试完整寄存器的低8位,test al,al避免写入部分寄存器,在P6-系列中,部分寄存器与完整EAX/RAX单独重命名。或al,al的情况要糟糕得多,如果您后来阅读EAX或ax:p6系列中的部分寄存器stall(为什么GCC不使用部分寄存器?)

正如注释中指出的那样,或reg,reg习语可能来自8080或a

在i7-6700k Skylake上(使用perf eventsuops_subsced.anyuops_executed.thread)进行测试:

  • MOV reg,[mem](或MOVZX)+test reg,reg/jnz2个uops在融合域和未使用域中,无论寻址模式如何,或者MOVZX代替MOV。无微保险丝;宏熔断吗。
  • CMP字节[RIP+STATIC_VAR],0+JNE。3个融合,3个未融合。(前端和后端)。相对撕裂+即刻结合防止微融合。它也不宏观熔断。代码较小,但效率较低。
  • CMP字节[rsi+rdi],0(索引地址模式)/JNE3融合,3不融合。解码器中的微熔断器,但在问题/重命名时没有层叠。不进行宏熔断。
  • CMP字节[rdi+16],0+JNE2个融合,3个未融合的UOP。由于寻址方式简单,cmp负载+ALU的微融合确实发生了,但直接阻止了宏融合。和load+test+jnz差不多:更小的代码大小,但多了一个后端uop.

如果寄存器中有0(如果要比较bool则有1),则可以cmp[mem]、reg/jne用于更少的UOP,低至1个fused-domain,2个unfused。但是RIP相关寻址模式仍然不能进行宏融合。

    null
 类似资料:
  • reg

    reg Docker registry v2 command line client and repo listing generator with security checks. Table of Contents Installation Binaries Via Go Usage Auth List Repositories and Tags Get a Manifest Get the

  • regular expression是一种特殊的字符序列,可帮助您使用模式中保存的专用语法来匹配或查找其他字符串或字符串集。 正则表达式在UNIX世界中被广泛使用。 该模块在Python中为类似Perl的正则表达式提供了完全支持。 如果在编译或使用正则表达式时发生错误,则re模块会引发异常re.error。 我们将介绍两个重要的函数,它们将用于处理正则表达式。 但首先要做的是:有各种各样的字符,当

  • IP Reg 是一个 IPAM 工具用于管理和跟踪不同子网,包括不同地区的虚拟专网中的设备、节点(IP地址、MAC地址和DNS别名)。使用 PHP + MySQL 编写。

  • 因此,每个测试都有3+1=4个周期的延迟。 其中一些可以通过在、等之间交替并行运行。 但它仍然相当慢。 有没有更快的方法来实现这一点? 我需要在一行中测试8个XMM/YMM寄存器。一字节位图中每个寄存器1位。

  • 我正在对代码中性能关键的部分进行微优化,并遇到了指令序列(在 我想我终于有了一个xchg的用例,它可以让我删除指令并编写: 然而,令我惊讶的是,我从Agner Fog的指令表中发现,是一个3微操作指令,在Sandy Bridge、Ivy Bridge、Broadwell、Haswell甚至Skylake上具有2个周期延迟。3个完整的微操作和2个周期的延迟!3个微操作丢弃了我的4-1-1-1节奏,2

  • 在x86-64中,如果某些通用寄存器比其他寄存器更受欢迎,某些指令会执行得更快吗? 例如,会比执行得更快吗?我可以想象后者需要一个REX前缀,这会使指令获取速度变慢? 使用代替怎么样?或呢?其他操作?更小的寄存器,如vs?vs? AMD vs Intel?更新的处理器?较旧的处理器?指令的组合? 澄清:某些通用登记册是否应该优先于其他登记册,它们是哪些?