【问题标题】:How to perform operator overloading to avoid if condition in c++?如何执行运算符重载以避免c ++中的if条件?
【发布时间】:2016-08-12 09:06:46
【问题描述】:

我举以下例子来说明我的问题:

 bool bSign;
 // bSign will be set depending on some criteria, which is omitted here.
 // b[][][] is a float array, which is initialized in the program  
 for(int i=0; i<1000; i++)
     for(int j=0; j<10000; j++)
         for(int k=0; k<10000; k++)
             if(bSign)
                 a[i][j][k] = (b[i][j][k]>500);
             else
                 a[i][j][k] = (b[i][j][k]<500);

在上面的代码中,我必须依靠bSign 来设计我应该使用的那种运算符(&gt;&lt;)来设置输出变量a。但是,我发现它的成本很高,因为它是在很长的 for 循环中完成的。关于如何摆脱这种情况的任何想法?

【问题讨论】:

  • 我不明白运算符重载应该如何摆脱分支。
  • 1) 您不能更改语言。为 int 重载运算符是不可能的。 2) bSign 的值仅在运行时已知。运算符重载应该如何工作,因为这是在编译期间发生的。
  • @CinCout: a 大概是bool[1000][10000][10000]
  • 一个体面的优化编译器会注意到 bSign 是一个循环不变量,并将其从最内层循环中删除。
  • 我想要一台实际运行这段代码的机器,至少有 500 GB 的内存。在我的家用电脑上,如果我添加一个更大的硬盘驱动器,这将需要永远运行,因为从/向磁盘加载/写入虚拟内存。

标签: c++


【解决方案1】:

如果你的编译器知道bSign 在循环期间不会改变,它应该 能够对此进行优化。让它const 会有所帮助。

如果由于某种原因没有发生这种情况,您可以将if (bSign) 移出以包围整个嵌套循环集。 (基本上,手动执行您希望编译器为您执行的操作。)

除此之外,您还依赖于分支预测(其成功将取决于您的数据的性质)。

不过,运算符重载与此完全无关。

不过,确实,这种迭代是不会有效率的。找不到更好的算法吗?

【讨论】:

  • 只是想同样的事情,如果这个巨大的矩阵矩阵中的值可以在运行时发生变化,我相信在变化期间存储布尔值会更好,而不是“一次全部”
【解决方案2】:

正如许多人所说,最好将if 语句移出循环(如果可能的话)。为了防止代码重复,你可以使用函数指针,但如果有真正的调用可能会更糟。通话费用不仅仅是简单的if。如果编译器可以内联它们就可以了。

您也可以通过这种方式使用模板元编程(本例中为简单的模板算法):

 template<typename ComparatorT>
 void doTheWork(float ***a, const float ***b, ComparatorT comparator)
 {
     for(int i=0; i<1000; i++)
         for(int j=0; j<10000; j++)
             for(int k=0; k<10000; k++)
                 a[i][j][k] = comparator(b[i][j][k], 500);
 }

 ...
 // Usage.
 bool bSign;
 ...
 if (bSign) {
     doTheWork(a, b, std::greater<float>());
 } else {
     doTheWork(a, b, std::larger<float>());
 }

编译器可以内联comparator 的调用(这比使用函数指针要容易得多),并且它几乎就像您编写两个不同的循环一样工作。 comparator 对象可以真正创建,但通常并不重要。一个缺点是它经常产生难以阅读的代码。

【讨论】:

    【解决方案3】:

    你能把检查移出循环吗?

    if (bSign)
        for(int i=0; i<1000; i++)
            for(int j=0; j<10000; j++)
                for(int k=0; k<10000; k++)
                    a[i][j][k] = (b[i][j][k]>500);
    else
        for(int i=0; i<1000; i++)
            for(int j=0; j<10000; j++)
                for(int k=0; k<10000; k++)
                    a[i][j][k] = (b[i][j][k]<500);
    

    【讨论】:

      【解决方案4】:

      我认为你不能在你的程序中摆脱这样的 if。但是您可以帮助处理器更快地运行您的程序。考虑对b 数组进行排序(如果无法更改b,则进行复制和排序)。这应该有助于按处理器预测分支。当然要进行一些基准测试,以确保不会比您当前的代码差。

      【讨论】:

      • 如果你可以对它进行排序,那么你就不需要首先对每个元素进行比较。
      • 好吧,反正不是最里面的数组。但是排序也只能帮助你处理最里面的数组。
      【解决方案5】:

      我不确定这是否更快(您必须进行性能检查),但您可以使用函数指针避免所有 if 语句。

      int comparisonValue = 500;
      auto greater = [comparisonValue](float val) {
          return val > comparisonValue;
      };
      auto lesser = [comparisonValue](float val) {
          return val < comparisonValue;
      };
      
      bool bSign;
      // bSign will be set depending on some criteria, which is omitted here.
      // b[][][] is a float array, which is initialized in the program  
      decltype(greater)* funcPtr;
      
      if(bSign) {
          funcPtr = &greater;
      } else {
          funcPtr = &lesser;
      }
      
      
      for(int i=0; i<1000; i++)
          for(int j=0; j<10000; j++)
              for(int k=0; k<10000; k++)
                  a[i][j][k] = (*funcPtr)(b[i][j][k]);
      

      【讨论】:

        【解决方案6】:

        你为什么不这样用呢?;

          a[i][j][k] = (bSign == (b[i][j][k] > 500)) && (b[i][j][k] != 500);
        

        【讨论】:

        • 它的条件比即时条件更复杂(“更慢”)。
        • 你错了。 “如果”较慢。看这里:link
        猜你喜欢
        • 1970-01-01
        • 2020-10-07
        • 2019-03-27
        • 2014-07-08
        • 1970-01-01
        • 2016-01-13
        • 2013-06-20
        • 2013-07-13
        • 2011-01-26
        相关资源
        最近更新 更多