1

関数の最適化を試みていfun_a1()ます。変数jは のスコープでは変更されませんfun_a1()。したがって、「i」反復ごとに j==1 または 2 または 3 をチェックすることは、明らかに CPU サイクルの無駄です。しかし、条件評価をループの外に出そうとすると、条件ごとに冗長なループを書かなければなりません。C では、関数ポインターを使用してこれを簡単に解決できます。ただし、C++ は非静的関数へのポインターを許可しません。不思議な「メンバーへのポインター」を説明するリンクがいくつか見つかりました。(例 1例 2 ) しかし、オブジェクト自体の内部から、たとえば fun_a() の内部からどのように使用すればよいかはまだ明確ではありません。または、他の方法で最適化できますか?

class A{
    void fun_b(int i);

    void fun_c(int i);

    void fun_d(int i);

    void fun_f(int i);

    void fun_a1(int j){

        for(int i=0; i<1000; i++){

                 if(j==1) fun_b(i);
            else if(j==2) fun_c(i);
            else if(j==3) fun_d(i);

            fun_f(i);           
        }

    }


    void fun_a2(int j){

        if(j==1){           
            for(int i=0; i<1000; i++) { 
                fun_b(i); 
                fun_f(i); 
            }
        }
        else if(j==2){          
            for(int i=0; i<1000; i++) { 
                fun_c(i);
                fun_f(i);
            }            
        }
        else if(j==3){
            for(int i=0; i<1000; i++) { 
                fun_d(i);           
                fun_f(i);
            }           
        }       
    }   
};
4

3 に答える 3

6

メンバー関数へのポインターを使用する方法は次のとおりです。

void (A::*fun)(int);
if(j == 1) fun = &A::fun_b;
else if(j == 2) fun = &A::fun_c;
else if(j == 3) fun = &A::fun_d;

for(int i=0; i<1000; i++) {
    (this->*fun)(i);
    fun_f(i);
}
于 2013-02-10T04:32:08.653 に答える
1

関数ポインターを使用すると、コンパイラーがそれらを削除しない場合、パフォーマンスが大幅に低下します。

変更されていないローカル変数に対する生の if は、おそらくループ外で最適化されます。これは、あまり手の込んだ最適化ではありません。

ただし、これを明示的にしたい場合、答えは関数またはメソッドのポインターではありません。それはラムダとファンクターです。

template<typename Functor>
void fun_a2_internal(Functor f) {
  for(int i = 0; i < 1000; ++i) {
    f(i);
  }
}

void fun_a2(int j) {
  if (j==1)
    fun_a2_internal([&](int i){ fun_b(i); fun_f(i); });
  else if (j==2)
    fun_a2_internal([&](int i){ fun_c(i); fun_f(i); });
  else if (j==3)
    fun_a2_internal([&](int i){ fun_d(i); fun_f(i); });
}

ここでfun_a2_internalは、ループを実行するジョブを記述し、ループ内で何らかのタスクを実行します。

fun_a2そのタスクをラムダを介してファンクターとして渡します。

これには、ループをコンパイルするときにコンパイラがループの本体の詳細を知るようになるという効果があります。ファンクターoperator()は非仮想ファンクターであり、したがってすべてのインスタンスで同じであるためです。

一般に、最適化の問題に対する答えが「関数ポインターを使用する」(またはメンバー ポインター) である場合、その答えは間違っています。

この手法が、C++ がstd::sortC よりも高速である理由qsortです。

于 2013-02-10T06:22:29.490 に答える
-2

「最適化」は曖昧であり、

  • 1、カバーされた効率の概念に基づいて教授が尋ねている特定の問題があります

  • 2、実行する特定の最適化があります。

あなたが求めているのは、この「switch」ステートメントを最適化することです

はい、switchステートメントはこの状況ではよりクリーンですが、これを行うためのより効率的な方法は、これらのループを含むvoid関数への関数ポインターの配列を作成してから実行することです。

doFunction[j]();

例えば

typedef void (*myfunc)(void);

myfunc options[10];

main()
{
    options[0] = (void*)loop1;
    options[1] = (void*)loop2;
    options[2] = (void*)loop3;
    options[3] = (void*)loop4;  
}

PS:コンパイラによって最適化されない限り、i++は++iよりも低速です。

于 2013-02-10T04:32:25.800 に答える