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_X関数へのポインターを渡すとどうなりますfunctionか (関数呼び出しの条件を変更します)。

関数全体をマクロ (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);
      ...
    }
  }
}

条件は常に true または false であるため、コンパイラは条件ツリーを最適化し、実際の数式のみを残します。分岐も関数呼び出しもありません!

現在、定数パラメーターでのみ使用できます。

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

すべての関数が同じ署名を持っている場合、最も簡単なことは次のようになります。

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 に答える
0

これを行う最も効率的な方法の 1 つは、関数ポインターの配列を作成することだと思います。その後、パラメーターだけでなく、関数ポインターを直接渡すことができます。これにより、ネストされたループで 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

多くのことに依存しますが、一般的には、ほとんどの場合、最初または 2 番目の関数が連続して呼び出されるようにすることができます。これにより、最新の CPU がこれを非常に高速に実行できるようになります (ソートされた配列を処理する方がソートされていない配列よりも速いのはなぜですか? を参照してください)。

配列と関数のポインターを使用できますが、速度が向上しない可能性があるため、試す必要があります。http://www.boost.org/doc/libs/1_54_0/doc/html/function/tutorial.html#idp59212272を使用して支援できますが、静的関数には必要ありません。

于 2013-07-12T10:13:12.227 に答える