5

我有一个看起来像这样的代码:

void function(int parameter)
{
  for( ... ) // a big loop
  {
    double a = ...;
    for( ... ) // a big loop
    {
      double b = ...;

      double value;
      if(parameter == 1)
        value = some_math_expression_1(a, b);
      else if(parameter == 2)
        value = some_math_expression_2(a, b);
      ...
    }
  }
}

这个想法是,根据我想将一些数学表达式应用于aand的参数b。这个函数执行了很多次并且必须很快,我想知道每次迭代中的那些条件分支是否会引入我可以节省的开销。

现在,我已经编写了这样的代码:

void function(int parameter)
{
  if(parameter == 1)
    function1();
  else if(parameter == 2)
    function2();
  else
    ...
}

这样我就可以直接应用数学表达式,如果我在每个functionX(). 明显的问题是,当我想更改一段代码时,我必须多次执行(我现在有大约 10 个数学表达式)。

我可以使用什么方法来避免任何开销function

如果我将一个指向函数的指针传递some_math_expression_Xfunction(我会更改函数调用的条件)怎么办?

如果我将整个函数编码为宏 (uf) 并将数学表达式设置为参数会怎样?

如果我使用模板并将数学表达式作为指向内联函数的指针传递怎么办(这甚至可能)?

编辑:谢谢你的回答。我知道我可以使用您提出的方法(指向/函数数组的指针,或依赖分支预测器)。但是,您是否对避免开销方面更好的方法有所了解?数学表达式非常简单(类似于a*b),除了很长的循环之外,function还被多次调用(分支预测是否在调用之间“存活”?)。

4

7 回答 7

4

您可以将函数转换为模板:

void functionT<int PARAMETER>()
{
  for( ... ) // a big loop
  {
    double a = ...;
    for( ... ) // a big loop
    {
      double b = ...;

      double value;
      if(PARAMETER == 1) //Constant condition!!!
        value = some_math_expression_1(a, b);
      else if(PARAMETER == 2)  //Constant condition!!!
        value = some_math_expression_2(a, b);
      ...
    }
  }
}

由于条件总是真或总是假,编译器将优化条件树,只留下真正的数学表达式。没有分支,也没有函数调用!

现在,您只能将其与常量参数一起使用:

functionT<1>();

但不是变量:

int x = 1;
functionT<x>(); //Error

如果需要,可以制作一个包装器:

void function(int parameter)
{
    switch (parameter)
    {
        case 1: functionT<1>(); break;
        case 2: functionT<2>(); break;
    }
}
于 2013-07-12T10:33:03.997 回答
3

不用担心。现代 CPU 具有分支预测器,它们将正确预测所采用的分支。

于 2013-07-12T10:11:33.963 回答
1

您可以设置一个常量函数指针数组,并调用与 parameter.

但如果数学表达式相当小,switch() 语句可能会更快。

switch (parameter) {
    case 1:
        value = math expression 1;
        break;
    case 2:
        ...
}
于 2013-07-12T10:11:13.070 回答
1

首先,我会一如既往地说,您现在应该对这个过程需要多长时间进行基准测试/测量,因为与往常一样,这可能是过早的优化,您可能会发现这不是您的代码中需要很长时间的部分时间。

但是假设您已经测量并发现这是您代码中的瓶颈,那么我会做一些事情。

首先,正如您所说,在这里最会杀死您的事情(假设您的数学函数足够简单)是分支预测。所以为了摆脱分支我会创建一个函数指针数组,而不是做

if(parameter == 1)
    function1();
if...

你可以这样做:

array_of_functions[parameter]();

这将摆脱所有分支预测并大大增加吞吐量,因为您的管道不必被刷新。编译器还应该能够内联函数。

于 2013-07-12T10:12:17.560 回答
0

这取决于很多事情,但通常您可能希望这样做,以便在大多数情况下连续调用第一个或第二个函数。这将使现代 CPU 执行得更快(请参阅为什么处理排序数组比处理未排序数组更快?)。

您可以使用数组和函数指针,但这可能不会加快速度,需要尝试。您可以使用http://www.boost.org/doc/libs/1_54_0/doc/html/function/tutorial.html#idp59212272来提供帮助,但静态函数不需要它。

于 2013-07-12T10:13:12.227 回答
0

我认为最有效的方法之一是创建一个函数指针数组,然后您可以直接传递函数指针而不仅仅是参数。这将节省您在嵌套循环中使用 if/switch 语句所产生的任何类型的开销。

举个例子:

double expression_0(double a, double b) {...};
double expression_1(double a, double b) {...};

void function(double (*expression)(double, double)) {
    for (...) {
        ...
        double a = ...;
        for (...) {
            double b = ...;
            double result = (*expression)(a, b);
        }
    }
}

int main() {
    double (*fpointers[2]) (double, double);
    fpointers[0] = expression_0;
    fpointers[1] = expression_1;

    int parameter = ...;
    function(fpointers[parameter]);
}
于 2013-07-12T10:26:10.537 回答
0

如果您的所有函数都具有相同的签名,那么最简单的做法是:

void function(int parameter)
{
  double ( *fn )( double, double );

  switch( parameter )
  {
    case 1:  fn = &some_math_expression_1;  break;
    case 2:  fn = &some_math_expression_2;  break;
    ...
  }

  for( ... ) // a big loop
  {
    double a = ...;
    for( ... ) // a big loop
    {
      double b = ...;
      double value = fn( a, b );
      ...
    }
  }
}
于 2013-07-12T10:29:14.237 回答