2

次の例を考えてみましょう:

template<int X> class MyClass
{
    public:
        MyClass(int x) {_ncx = x;}
        void test() 
        {
            for (unsigned int i = 0; i < 1000000; ++i) {
                if ((X < 0) ? (_cx > 5) : (_ncx > 5)) {
                    /* SOMETHING */
                } else {
                    /* SOMETHING */
                }
            }
        }
    protected:
        static const int _cx = (X < 0) ? (-X) : (X);
        int _ncx;
};

私の質問は: MyClass<-6>::test() と MyClass<6>::test() の速度は異なりますか?

負のテンプレート パラメーターの場合、コンパイル時にテスト関数を評価できるため、そう願っていますifが、コンパイル時のものとコンパイル以外のものがある場合、コンパイラの動作がどうなるかわかりません。 -三項演算子の時間のこと(ここではそうです)。

注:これは純粋な「理論上の」質問です。「はい」の可能性が null でない場合は、そのようなコンパイル時のテンプレート パラメーターを使用してコードのクラスを実装します。そうでない場合は、ランタイム バージョンのみを提供します。

4

3 に答える 3

2

条件をループの外に移動します。

        ...
        if ((X < 0) ? (_cx > 5) : (_ncx > 5)) {
            for (unsigned int i = 0; i < 1000000; ++i) {
                /* SOMETHING */
            }
        } else {
            for (unsigned int i = 0; i < 1000000; ++i) {
                /* SOMETHING */
            }
        }
        ...

そうすれば、未使用のコードを削除するためにコンパイラの最適化に依存しなくなります。条件の未使用部分がコンパイラによって削除されない場合、ループのたびにではなく、条件付き分岐の料金を 1 回支払うだけです。

于 2012-09-26T00:34:05.800 に答える
2

私のコンパイラ (OS X の clang++ v2.9 ) の場合、これと似ているが同一ではないコードをコンパイルします。

void foo();
void bar();

template<int N>
void do_something( int arg ) {
  if ( N<0 && arg<0 ) { foo(); }
  else { bar(); }
}

// Some functions to instantiate the templates.
void one_fn(int arg) {
  do_something<1>(arg);
}

void neg_one_fn(int arg) {
  do_something<-1>(arg);
}

これにより、次のアセンブリが生成されますclang++ -S -O3

one_fn = do_something<1>

最初の関数アセンブリには、明らかに への呼び出ししかありませんbar

    .globl  __Z6one_fni
    .align  4, 0x90
__Z6one_fni:                            ## @_Z6one_fni
Leh_func_begin0:
    pushl   %ebp
    movl    %esp, %ebp
    popl    %ebp
    jmp __Z3barv                ## TAILCALL
Leh_func_end0:

neg_one_fn = do_something<-1>

bar2 番目の関数は、 または のいずれかを呼び出す単純な if に縮小されましたfoo

    .globl  __Z10neg_one_fni
    .align  4, 0x90
__Z10neg_one_fni:                       ## @_Z10neg_one_fni
Leh_func_begin1:
    pushl   %ebp
    movl    %esp, %ebp
    cmpl    $0, 8(%ebp)
    jns LBB1_2                  ## %if.else.i
    popl    %ebp
    jmp __Z3foov                ## TAILCALL
LBB1_2:                                 ## %if.else.i
    popl    %ebp
    jmp __Z3barv                ## TAILCALL
Leh_func_end1:

概要

したがって、コンパイラがテンプレートをインライン化し、可能であればブランチを最適化したことがわかります。したがって、あなたが望んでいる種類の変換は、現在のコンパイラで発生します。古い g++ 4.0.1 コンパイラからも同様の結果が得られました (ただし、アセンブリはあまり明確ではありません)。

補遺:

この例は最初のケースと十分に似ていないと判断したので(三項演算子が含まれていないため)、次のように変更しました:(同じ種類の結果を得る)

template<int X>
void do_something_else( int _ncx ) {
  static const int _cx = (X<0) ? (-X) : (X);
  if ( (X < 0) ? (_cx > 5) : (_ncx > 5)) {
    foo();
  } else {
    bar();
  }
}

void a(int arg) {
  do_something_else<1>(arg);
}

void b(int arg) {
  do_something_else<-1>(arg);
}

これにより、アセンブリが生成されます

a() = do_something_else<1>

これにはまだブランチが含まれています。

__Z1ai:                                 ## @_Z1ai
Leh_func_begin2:
    pushl   %ebp
    movl    %esp, %ebp
    cmpl    $6, 8(%ebp)
    jl  LBB2_2                  ## %if.then.i
    popl    %ebp
    jmp __Z3foov                ## TAILCALL
LBB2_2:                                 ## %if.else.i
    popl    %ebp
    jmp __Z3barv                ## TAILCALL
Leh_func_end2:

b() = do_something_else<-1>

ブランチは最適化されています。

__Z1bi:                                 ## @_Z1bi
Leh_func_begin3:
    pushl   %ebp
    movl    %esp, %ebp
    popl    %ebp
    jmp __Z3barv                ## TAILCALL
Leh_func_end3:
于 2012-09-26T01:40:38.893 に答える
0

それはおそらくあなたのコンパイラがどれだけ賢いかに依存します。小さなベンチマークプログラムを作成して、環境で自分でテストし、確実に確認することをお勧めします。

于 2012-09-25T23:37:29.103 に答える