在循环之前选择更少或更大运算符的最佳方式

时间:2014-02-05 23:16:02

标签: c++ arrays optimization

我有两个数组,包括x,y值,y = f(x)。我想提供一个函数来查找x的值,该值对应于y的最小或最大采样值。

在循环遍历数组中的值之前,选择适当的比较运算符的有效方法是什么?

例如,我想做类似以下的事情:

double FindExtremum(const double* x, const double* y,
                    const unsigned int n, const bool isMin) {
   static std::less<double>    lt;
   static std::greater<double> gt;
   std::binary_function<double,double,bool>& IsBeyond = isMin ? lt : gt;
   double xm(*x), ym(*y);
   for (unsigned int i=0; i<n; ++i, ++x, ++y) {
      if (IsBeyond()(*y,ym)) {
         ym = *y;
         xm = *x;
     }
   }
}

不幸的是,基类std::binary_function没有定义虚拟运算符()。

像g ++ 4.8这样的编译器是否能够优化最直接的实现?

double FindExtremum(const double* x, const double* y,
                    const unsigned int n, const bool isMin) {
   double xm(*x), ym(*y);
   for (unsigned int i=0; i<n; ++i, ++x, ++y) {
      if ( ( isMin && (*y<ym)) ||
           (!isMin && (*y>ym)) ) {
         ym = *y;
         xm = *x;
     }
   }
}

还有另一种方法可以安排事情让编译器更容易优化吗?有没有一个众所周知的算法呢?

如果可能,我宁愿避免使用模板化函数。

4 个答案:

答案 0 :(得分:6)

您需要将比较仿函数作为模板化函数参数传递,例如

template <typename Compare>
double FindExtremum(const double* x, const double* y,
                    const unsigned int n, Compare compare) {
   double xm(*x), ym(*y);
   for (unsigned int i=0; i<n; ++i, ++x, ++y) {
      if (compare(*y,ym)) {
         ym = *y;
         xm = *x;
     }
   }
}

然后,如果您需要运行时选择,请编写如下内容:

if (isMin) {
    FindExtremum(x, y, n, std::less<double>());
} else {
    FindExtremum(x, y, n, std::greater<double>());
}

在这种情况下,实际上不可能避免模板化功能。性能最佳的代码将是直接在循环中嵌入比较操作的代码,避免函数调用 - 您可以编写模板或写入此函数的两个副本。模板化函数显然是更好的解决方案。

答案 1 :(得分:1)

为了获得最高效率,请将比较运算符或比较运算符选项作为模板参数,并且不要忘记度量

在努力实现最高效率时,进行虚拟呼叫并不是朝向目标的方向。

也就是说,这很可能是过早优化的情况,Donald Knuth如此描述:

  

“过早优化是所有邪恶的根源”

(我省略了他的保留,这听起来更有力。:-))

我没有参与微观优化狂热,而是浪费你的时间,而是浪费你的时间,我建议更高效地尝试使代码尽可能清晰且可证明正确。例如,使用std::vector而不是原始数组和单独传递的大小。并且,例如,不要像另一个答案中所建议的那样调用布尔比较运算符compare,因为这是三值比较的常规名称(例如在std::string::compare中)。

答案 2 :(得分:1)

这里出现了一些问题。首先,我认为你的情况过于复杂。例如,有两个函数会更容易,一个计算最小值,另一个计算最大值,然后根据isMin的值调用其中任何一个。

更多信息,请注意您在每次迭代中 如何进行测试以查看isMin是否为真,(至少在您最后显示的“优化”代码中)而这种比较本来可以一次

现在,如果在编译时可以以任何方式推导isMin,则可以使用模板类来选择针对案例优化的正确实现,并且没有任何运行时开销(未经过测试,从中编写)存储器):

template<bool isMin>
class ExtremeFinder
{
static float FindExtreme(const double* x, const double* y,
                    const unsigned int n)
{
    // Version that calculates when isMin is false
}
};

template<>
class ExtremeFinder<true>
static float FindExtreme(const double* x, const double* y,
                    const unsigned int n)
{
    // Version that calculates when isMin is true
}
};

并将其称为ExtremeFinder<test_to_know_isMin>::FindExtreme(...);,或者,如果您无法在编译时决定它,则可以随时执行:

if (isMin_should_be_true)
   ExtremeFinder<true>::FindExtreme(...);
else
   ExtremeFinder<false>::FindExtreme(...);

答案 3 :(得分:1)

如果您有2个分离标准,例如<>=,您可以使用bool less函数参数并在循环中使用XOR:

if (less ^ (a>=b))

不了解性能,但很容易写。

或不覆盖所有可能性 - 分离<>

if ( (a!=b) && (less ^ (a>b) )