扩展具有相同条件的多个 "if" 会导致性能提升

expanding multiple "if"s with same condition result in performance gain

假设我有

void f(const bool condition) {
  if (condition) {
    f2();
  else {
    f3();
  }

  f4();

  if (condition) {
    f5();
  } else {
    f6();
  }
}

因为condition永远不会改变,上面可以简化为下面的

void f(const bool condition) {
  if (condition) {
    f2();
    f4();
    f5();
  } else {
    f3();
    f4();
    f5();
  }
}

请注意,f4() 在第二个代码中重复,但第二个代码部分的 if 个分支较少。我试图分析这 2 个代码片段,但在我看来,性能几乎相同。想象一下,在现实生活中,上述代码片段在相同条件下可以有更多 if。所以我想知道现代 x86/64 处理器:

  1. 使用 2 个巨大的 if 语句而不是基于相同条件的许多小语句是否有任何性能提升?
  2. const 关键字会帮助 compiler/processor 生成更好的分支预测吗?

首先,要发现任何差异,您需要多次 运行 您的代码片段,例如:

for (int i=0; i<100000000; ++i)
    f(true);

您需要 select 次迭代,使整体 运行ning 时间为 10-30 秒。在这种情况下,您将看到函数本身的性能,而不是加载应用程序等各种开销。

其次,你的函数的复杂度是多少f2 ... f6?如果这些函数比 f 本身复杂得多,您也不会注意到任何差异。

第三,您的第二个版本会稍微快一些,尽管差异很小。添加 const 对编译器没有任何帮助。

最后,我建议查看可带来显着性能提升的更改。

理论上消除任何条件操作可以提高性能。但在现实世界中,根本没有区别。 在您的特定情况下,编译器可以轻松地为您执行建议的优化,因此应该没有区别,因为您已经测试过了。优化编译器的一项有价值的工作——分支消除。他们寻找避免不必要分支的可能性。

所以,你的问题 1 的答案是: 在大多数情况下,现代编译器不会有任何区别。

关于 const 关键字: const 本身对分支预测没有帮助。编译器可以查看是否有任何变量未被修改,并尽其所能生成快速代码。当处理器执行二进制代码时,没有任何迹象表明该值是常量。至少在 x86 和 x86-64 处理器上。

无论如何"premature optimization is the root of all evil" (c) Donald Knuth。您需要避免任何低级优化,除非您有显示瓶颈的分析数据。为此,您需要一个基准来分析性能。

首先,您的示例非常简单,任何体面的编译器都可以为这两种情况生成 identical code

为了足够混淆它,您应该做一些更复杂的事情,而不是简单地调用 f4like so:

void f_seprate_ifs(const bool condition) {
  if (condition) {
    f2();
  } else {
    f3();
  }

  for ( int i = 0; i < 100; i++ ){
    f4();
  }

  if (condition) {
    f5();
  } else {
    f6();
  }
}

void f_duplicate_f4(const bool condition) {
  if (condition) {
    f2();
    for ( int i = 0; i < 100; i++ ){
      f4();
    }
    f5();
  } else {
    f3();
    for ( int i = 0; i < 100; i++ ){
      f4();
    } 
    f6();
  }
}

但这不是样式问题,而是速度和 space 之间的明确权衡 - 您正在复制代码以消除分支( 和 IMO,这对我的例子来说根本不是一个好的权衡 )。编译器已经通过函数内联一直这样做,并且对何时内联有非常复杂的启发式。对于您的示例,它甚至为您完成了。

总而言之,除非您绝对确定有必要,否则不要尝试进行此类微优化。特别是当它们损害可读性时。尤其是当它们引起复制粘贴错误时。

至于 const 修饰符,再一次,任何体面的编译器都会注意到 condition 永远不会改变,并且是 effectively const,用 Java 术语来说。在 C++ 中,const 很少提供额外的优化机会。它是为程序员准备的,而不是为编译器准备的。

例如,对于:

void f(const bool& condition){

conditionNOT 常量 - 编译器 必须 假设它可以被 f4 改变, 因此片段在语义上不再等同。