【问题标题】:If-else tree optimizationif-else 树优化
【发布时间】:2019-12-08 14:11:42
【问题描述】:

我正在尝试为以下问题找到一个很好的解决方案:

我的输入是一组布尔变量 b0bn 和 一组if 语句调用某个函数以防表达式(我的布尔变量的结合)为真。

例如:

void test(bool b0, bool b1, bool b2, bool b3, bool b4) {
   if(b0&&b1) { action1(); }
   if(b0&&b1&&b3) { action2(); }
   if(b0&&b1&&b4) { action2(); }
   if(b0&&b2) { action3(); }
}

显然可以优化此代码以尽量减少比较次数。

例如:

void test(bool b0, bool b1, bool b2, bool b3, bool b4) {
   if(b0) {
      if(b1) {
         action1();
         if(b3) { action2(); }
         if(b4) { action2(); }
      }
      if(b2) { action3(); }
   }
}

是否有解决/优化此问题的算法(甚至可能以最佳方式)?
很明显,它可以表示为图形、表格等,但不知何故我仍然找不到聪明的解决方案。

更新:我认为我没有很好地解释这个问题。将问题视为具有多个输出的真值表。输入是布尔变量,输出是动作。所以这不仅仅是可以用 KV map 或 Quine-McCluskey 算法解决的问题。猜猜它更复杂。

上面示例的表格将是(我省略了“无输出”的情况):

b0| b1| b2| b3| b4 || action1 | action2 | action3  
1 | 1 | 0 | 0 | 0  || 1       | 0       | 0
1 | 1 | 0 | 1 | 0  || 1       | 1       | 0
1 | 1 | 0 | 0 | 1  || 1       | 1       | 0
1 | 0 | 1 | 0 | 0  || 0       | 0       | 1

【问题讨论】:

  • 您是否检查了生成的程序集以推断“可以优化此代码以最小化比较次数”?我没有注意到godbolt.org/z/iEh9FE上的两个版本之间存在巨大差异
  • 我相信他们的意思是返回动作而不是仅仅运行它们并继续。
  • @StoryTeller:感谢您的评论。我猜 gcc、clang 等已经在优化此类代码方面做得很好。这更像是一个关于如何最小化比较次数的算法问题。
  • @yukashimahuksay 这与是否退货无关。我有兴趣尽量减少比较次数。
  • 我的意思是您的代码是否只执行其中一项操作,或者它可能执行其中一些操作。在后一种情况下,我认为您的代码甚至不会减少比较次数。

标签: c++ algorithm optimization code-generation compiler-optimization


【解决方案1】:

一般来说,如果您需要处理一堆布尔标志,请将它们合并到一个 uint32 中,这样您就可以相当容易地测试它们......

#include <cstdint>

enum Flags
{
  kB0 = 1 << 0,
  kB1 = 1 << 1,
  kB2 = 1 << 2,
  kB3 = 1 << 3,
  kB4 = 1 << 4,
  kB5 = 1 << 5,
};

inline bool testFlagsSet(uint32_t testFlags, uint32_t flags)
{
  return (testFlags & flags) == testFlags;
}

void test(uint32_t flags) {
  if(testFlagsSet(kB0 | kB1, flags)) { action1(); }
  if(testFlagsSet(kB0 | kB1 | kB3, flags)) { action2(); }
  if(testFlagsSet(kB0 | kB1 | kB4, flags)) { action3(); }
  if(testFlagsSet(kB0 | kB2, flags)) { action4(); }
}

您可以在此处查看方法之间的区别:https://godbolt.org/z/3P06fL

另外值得注意的是,在您发布的原始两种方法中,第二种更好:https://godbolt.org/z/YTbYxh

【讨论】:

    【解决方案2】:

    我明白你在问什么。有一些优化工具可用于帮助转换此类代码。但基本上没有必要。

    而且你的新代码也不是更好。它甚至有 5 个 if 语句。性能方面,编译器的优化器会很小心。它将检测公共子表达式并对其进行优化。

    这也应该基本上由软件开发人员/设计师来完成。同行评审将检测该代码并修复语义问题。

    您当然知道布尔表达式可以通过 Quine&McCluskey 或其他方法最小化。但我知道这不是你的问题。

    Coverity / QAC / SonarCube 的静态代码分析可以检测到它,但不会修复它。这需要手动完成。

    或者您需要评估源代码优化器,例如 PVS Studio

    请检查。

    【讨论】:

    • 将 PVS Studio 称为代码优化器是不正确的。它是一个带有多个警告的静态分析器,与可能的优化有关。
    • 不要太相信编译器。 godbolt.org/z/YTbYxh 性能方面,OP 提出的后一种方法产生更好的 asm。
    • AT:GoverNator 和在 robthebloke:是的,同意了。谢谢你的澄清。一个好的软件设计和算法将胜过任何优化器。
    【解决方案3】:

    不要过早优化。

    如果您正在考虑进行此类优化,则必须先进行衡量。确保此函数是您在生产代码中的热点。因此,

    • 开启最高优化级别 (-O3)
    • 使用适当的工具来定位热点,例如 perf 或 Intel 的 vTune

    然后,如果您发现这是您的热点。改变它并再次测量。在很多情况下你会感到惊讶。编译器很可能会为您完成这项优化工作。

    尽管如此,我认为您的代码的第二个版本可以更流畅地阅读。

    【讨论】:

      猜你喜欢
      • 2020-11-23
      • 2023-03-28
      • 2010-12-29
      • 1970-01-01
      • 2011-05-18
      • 2021-09-22
      • 2013-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多