C++ 比较是否意味着一个分支?
我正在阅读关于优化的维基百科页面: 我遇到了问题: 对于流水线处理器,比较比差异慢,因为它们意味着一个分支 为什么比较意味着一个分支? 例如,如果:C++ 比较是否意味着一个分支?,c++,optimization,pipelining,C++,Optimization,Pipelining,我正在阅读关于优化的维基百科页面: 我遇到了问题: 对于流水线处理器,比较比差异慢,因为它们意味着一个分支 为什么比较意味着一个分支? 例如,如果: int i = 2; int x = i<5; inti=2; int x=i这只涉及一个分支: unsigned(i – min_i) <= unsigned(max_i – min_i) unsigned(i–min_i)序言:现代编译器能够以各种方式消除分支。因此,这些示例都不一定会在最终(汇编程序或机器)代码中产生分支 那么
int i = 2;
int x = i<5;
inti=2;
int x=i这只涉及一个分支:
unsigned(i – min_i) <= unsigned(max_i – min_i)
unsigned(i–min_i)序言:现代编译器能够以各种方式消除分支。因此,这些示例都不一定会在最终(汇编程序或机器)代码中产生分支
那么,为什么逻辑基本上意味着分支呢
代码
bool check_interval_branch(int const i, int const min_i, int const max_i)
{
return min_i <= i && i <= max_i;
}
bool check_interval_diff(int const i, int const min_i, int const max_i)
{
return unsigned(i - min_i) <= unsigned(max_i - min_i);
}
代码
bool check_interval_branch(int const i, int const min_i, int const max_i)
{
return min_i <= i && i <= max_i;
}
bool check_interval_diff(int const i, int const min_i, int const max_i)
{
return unsigned(i - min_i) <= unsigned(max_i - min_i);
}
(这里的技巧是,根据进位标志,sbb
执行的减法不同于1,进位标志由cmp
指令设置为1或0。)
事实上,您在这里看到了三个差异(2xsub
,1xsbb
)
这可能取决于您的数据/用例,哪一个更快。
请参阅关于分支预测
您的代码intx=i虽然这里给出的答案很好,但并不是所有的比较都被转换为分支指令(它们引入了数据依赖关系,这也可能会降低性能)
例如,下面的C代码
int main()
{
volatile int i;
int x = i<5;
return x;
}
setl
指令根据前面的比较指令的结果设置AL
的值
当然,这是一个非常简单的示例,cmp/setl
组合可能会引入依赖项,从而阻止处理器并行执行它们,甚至可能会花费您几个周期
尽管如此,在现代处理器上,并不是所有的比较都能转化为分支指令。编写过该页面的人并不能胜任程序员的工作。第一
比较并不一定意味着一个分支;这取决于你要做什么
别理他们。而这是否意味着一个分支取决于
处理器和编译器。if
通常需要分支,但是
即使这样,一个好的优化器有时也可以避免它。当
或
for
通常需要一个分支,除非编译器可以展开
循环,但该分支是高度可预测的,因此即使在
预测是一个问题,可能无关紧要
更一般地说,如果你在写作时担心这个级别的任何事情
你的代码,你在浪费你的时间,使维护变得更加困难
困难的你唯一应该关心的是一旦你有了
性能问题,分析器显示这是
你失去了表演。在这一点上,你可以用
编写代码的几种不同方法,以确定哪种方法
为编译器和硬件的组合生成更快的代码。
(更改编译器或硬件,可能不是同一个。)我认为这是因为比较(和后续分支)会影响分支预测器。分支预测器在预测良好时加快流水线处理器中的执行,在预测失败时减慢执行(必须再次填充流水线)。与论坛网站不同,我们不使用“感谢”或“感谢任何帮助”或签名。请参阅“。关于特定问题是否合适的问题应在上提问。一如既往,在提出新问题之前,先进行一些搜索。我认为是编译器(而不是处理器)是将比较转化为分支但通常你是正确的。在某些情况下,优化编译器即使逻辑包含分支也会生成无分支代码。@像素化学家我认为他的观点是大多数现代处理器都有指令(如setl
)这可以用来避免分支。当然,即使没有分支,像sbb%al,%al
这样的东西也可以用来将进位输入寄存器。(1980年,用于8086的原始Intel PL/M编译器就是这样做的,所以对现代编译器的期望似乎不高。)这两种方法都不一定涉及分支;编译器可以很容易地优化这两种方法以消除所有分支,至少在大多数体系结构上是这样。同意的,这取决于它的使用方式,但没有指定。是的。如果不使用结果,编译器可能会完全消除测试。如果它们被分配给bool
(稍后使用),在这两种情况下避免分支都是一个相对简单的优化。即使它们控制if
,根据受控块中的内容,也可能避免分支(但这可能需要相当复杂的编译器)。请注意,min_i即使没有优化,为int x=i<5;
@JamesKanze生成分支指令的编译器也会非常糟糕。我认为问题更多的是示例表达式中的逻辑,而不是编译器生成的代码是否会在任何情况下忽略分支的问题我倾向于说:比较意味着一个分支,在某些(或许多)情况下,它可以由体面的编译器进行优化。
bool check_interval_diff(int const i, int const min_i, int const max_i)
{
if (unsigned(i – min_i) <= unsigned(max_i – min_i)) { return true; }
return false;
}
push ebp
mov ebp, esp
mov edx, DWORD PTR _i$[ebp]
mov eax, DWORD PTR _max_i$[ebp]
sub eax, DWORD PTR _min_i$[ebp]
sub edx, DWORD PTR _min_i$[ebp]
cmp eax, edx // comparison
sbb eax, eax
inc eax
pop ebp
ret 0
int x = false;
if (i < 5)
{
x = true;
}
int main()
{
volatile int i;
int x = i<5;
return x;
}
movl -4(%rbp), %eax
cmpl $5, %eax
setl %al
movzbl %al, %eax