对于现代硬件上的典型现代编译器,
? :
运算符会导致影响指令管道的分支吗?
换句话说,调用这两种情况以避免可能的分支,哪个更快:
bool testVar = someValue(); // Used later.
purge(white);
purge(black);
或者选择实际需要清除的部分并仅由操作员进行
?:
:
bool testVar = someValue();
purge(testVar ? white : black);
我意识到你不知道 purge() 需要多长时间,但我只是在这里问一个一般性问题,关于我是否想要调用 purge() 两次以避免代码中可能出现分支。
我意识到这是一个非常微小的优化,可能没有真正的区别,但仍然想知道。 我希望
?:
不会导致分支,但想确保我的理解是正确的。
取决于平台。 具体来说,取决于CPU的跳转预测表的大小以及CPU是否允许条件操作(如ARM)。
具有条件操作的CPU将强烈支持第二种情况。 具有较大跳转预测表的 CPU 将有利于第一种情况。
真正的答案(与任何其他性能问题一样):测量和比较。 有时,代码的其余部分会抛出曲线球,通常无法预测某些更改的影响。
CMOV(Conditional MOVe)指令自 Pentium Pro 以来一直是 x86 指令集的一部分。由于常用的编译器选项和 C 语言的限制,GCC 很少自动生成它。 SETCC/CMOV 序列可以通过内联汇编插入到您的 C 程序中。仅当条件变量是程序内部循环(数百万次执行)中的随机振荡值时才应执行此操作。在非振荡情况和简单振荡模式的情况下,现代处理器可以以非常高的准确度预测分支。 2007 年,Linus Torvalds 在此建议 在大多数情况下避免使用 CMOV。
英特尔在 英特尔(R) 架构软件开发人员手册,第 2 卷:指令集参考手册中描述了条件移动:
CMOVcc 指令检查一个或多个状态 EFLAGS 寄存器中的标志(CF、OF、PF、SF 和 ZF)并执行 如果标志处于指定状态(或条件),则进行移动操作。一个 条件代码 (cc) 与每条指令相关联以指示 正在测试的条件。如果不满足条件,则 不执行移动并继续执行指令 遵循 CMOVcc 指令。
这些指令可以将 16 位或 32 位值从内存移动到 通用寄存器或从一个通用寄存器到 其他。 8 位寄存器操作数的条件移动不是 支持。
描述中给出了每个 CMOVcc 助记符的条件 上表的列。术语“更少”和“更大”用于 有符号整数和术语“上方”和“下方”的比较是 用于无符号整数。
因为状态标志的特定状态有时可以是 以两种方式解释,为某些操作码定义了两个助记符。 例如,CMOVA(条件移动,如果以上)指令和 CMONVBE(不低于或等于则条件移动)指令是 操作码 0F 47H 的备用助记符。
我无法想象第一种方法会更快。
使用第一种方法,您可以避免分支,但是用函数调用替换它,这通常会涉及分支以及更多内容(除非它是内联的)。 即使是内联的,除非 purge() 函数内部的功能绝对微不足道,否则它几乎肯定会变慢。
调用函数至少与执行逻辑测试+跳转一样昂贵(是的,
? :
三元运算符需要跳转)。
在第一种情况下,清除被调用两次。在第二种情况下,清除被调用一次
很难回答有关分支的问题,因为它非常依赖于编译器和指令集。例如,在 ARM(具有条件指令执行)上,它可能不会分支。在 x86 上几乎肯定会