C++ 逻辑运算符和分支预测失败

C++ 逻辑运算符和分支预测失败,c++,performance,compiler-optimization,short-circuiting,branch-prediction,C++,Performance,Compiler Optimization,Short Circuiting,Branch Prediction,考虑以下循环: while((expressionA) & (expressionB)){ // do something } 其中expressionA和expressionB是bool类型的表达式,expressionB没有副作用。在这些条件下,这两种情况似乎是等价的(对吧?) 一个天真地从源代码中获取线索的(假设的)编译器将在&&版本中放置一个分支,我们最终将得到它 对于现代编译器(如当前的GCC),在任何情况下,&版本都会比&版本有显著的性能提升吗 我的猜测是否定的,

考虑以下循环:

while((expressionA) & (expressionB)){
    // do something
}
其中
expressionA
expressionB
bool
类型的表达式,
expressionB
没有副作用。在这些条件下,这两种情况似乎是等价的(对吧?)

一个天真地从源代码中获取线索的(假设的)编译器将在
&&
版本中放置一个分支,我们最终将得到它

对于现代编译器(如当前的GCC),在任何情况下,
&
版本都会比
&
版本有显著的性能提升吗


我的猜测是否定的,因为:

  • 如果
    expressionB
    足够便宜,编译器将识别出这一点并避免创建短路分支
  • 如果
    expressionB
    足够昂贵,编译器将创建短路,因为:
    • 如果
      expressionA
      的概率不接近1.0,我们可以从短路中获得可观的平均性能增益
    • 如果
      expressionA
      的概率接近1.0,我们不会支付太多费用,因为分支预测往往会成功

如果expressionA有副作用,则无法避免。因为无法告诉优化器哪些情况最有可能发生,这使得像
If(a&&b&&c&&d&&e)
这样没有副作用但计算代价高昂的情况很难发生。理论上,C标准允许以任何顺序进行测试,因此,即使您将最昂贵的测试放在第一位,优化器也可能不会遵守。我发现,在GCC 5.2中,在一些简单的测试中,
运算符&
实际上会导致寄存器分配上的细微差异,但在分支方面与
运算符&&
相同(至少对于计算结果为
bool
的操作数)。正如M.M.所提到的,有一件事是,编译器无法真正找出这里的“便宜”是什么,因为投币表达式的概率/可预测性通常是基于仅在运行时提供的输入。它超越了基本的评估。我所看到的是短路应用于这两个操作符。@m.m.你能解释一下为什么你提到了
expressionA
的副作用吗?@Ike谢谢。我所说的“廉价表达式”只是指可以快速计算的表达式,因此从这个意义上讲,编译器可以找出什么是廉价的。概率当然重要,但作为一个单独的因素。以
expressionB
为例,它只是一个
bool
变量,其值在编译时是未知的,但已经被计算过(在运行时的这个时候),因为它的值在代码的其他地方已经需要了。该值“便宜”可用,在这种情况下短路(对于两个运营商中的任何一个)似乎都是浪费,特别是因为分支预测可能失败。
while((expressionA) && (expressionB)){
    // do something
}