4

以下は、私が最適化する必要があり、スイッチ構造に移行するのが良いと計画したコードです。しかし、私は場合に応じて比較することができます。そこで、デフォルトのケースとして比較(len> 3)を行うことを計画しました。

比較部分(len> 3)をデフォルトのケースとして作成し、デフォルトをスイッチの最初のケースとして追加すると、より高速になりますか?

または、以下のコードをswitchステートメントとして作成するにはどうすればよいですか?

if ( len > 3 ) {
    // Which will happen more often;
}
else if ( len == 3 ) {
    // Next case which may occur often;

} else if ( len == 2 ) {
    // The next priority case;

} else {
    // And this case occurs rarely;

}
4

6 に答える 6

6

おそらくそうではありません。if...elseとはどちらswitch...caseも高レベルの構造です。あなたを遅くするのは分岐予測です。予測が優れているほど、コードの実行速度が速くなります。あなたが書いたように、最も発生するケースを first if、 secondなどに配置else ifする必要があります。結果はswitch、独自の順序に関係なくケースを並べ替えることができる内部コンパイラの実装に依存します。にフォールバックする前に残りのdefault条件をチェックする必要があるため、 はあまり発生しない状況用に予約する必要がありますdefault

if...elseこれらすべてを結論付けると、条件を正しい順序で設定する限り、 のパフォーマンスに関する使用は最適です。それはコンパイラswitch...case固有のものであり、適用された最適化に依存します。

また、値の単純な比較のみをサポートするため、switch...caseより制限されていることに注意してください。if...else

于 2012-12-28T07:32:53.073 に答える
3

あなたはおそらく最良の答えを受け入れましたが、私は別の答えを提供したかったのです.

標準的な警告が適用されることに注意してください。コードをプロファイリングしない限り、最適化は最適化ではありません。

ただし、ブランチに関連してパフォーマンスが低下している場合は、ブランチを減らすか削除することができます。コードに 1 つ以上の不等式の比較があることは障害ではありません。ケースを一連の直接的な等式に減らし、必要に応じてそれを使用して、まったく分岐するのではなく、テーブルにインデックスを付けることができます。

void doSomething(int len)
{
    static const char* str[] =
    {   "%2d > 3\n",
        "%2d < 2\n",
        "%2d = 2\n",
        "%2d = 3\n"
    };

    int m1 = (len-2)>>31;
    int m2 = (len-4)>>31;

    int r = (len & m2 & ~m1) + !!m1;

    printf(str[r],len); 
}

このコードは、実際には当てはまらない可能性があるいくつかの仮定を行っていることに注意してください。

また、入力パラメーターの実際の範囲とタイプ、および実際に実行する必要がある実際のアクションについての知識があれば、より適切な最適化が可能になる場合があることに注意してください。

于 2012-12-28T08:57:23.140 に答える
2

比較をswitchステートメントに移動することはできません...選択に単一のチェックを使用します..つまり、

switch (len) {

    case 1:
        // Do case 1 stuff here
    break;

    case 2:
        // Do case 2 stuff here
    break;

    case 3:
        // Do case 3 stuff here
    break;
}

ブレークを使用して、case ステートメントが互いに衝突しないようにします。詳細はこちらをご覧ください

あなたのコードは、現在の状態になるのと同じくらい「最適化」されています...

于 2012-12-28T07:28:30.267 に答える
1

知る唯一の方法は、コンパイラでベンチマークすることです。パフォーマンスが問題になる場合は、このオプションを使用してコンパイラにプロファイラ出力を提供し、コンパイラに決定させる必要があります。通常、最適なソリューションが見つかります。(Intel のような特定のアーキテクチャであっても、マシン命令に関する最適なソリューションは、プロセッサごとに異なる場合があることに注意してください。)

あなたの場合、スイッチはおそらく次のようになります。

switch ( len ) {
case 2:
    //  ...
    break;

case 3:
    //  ...
    break;

default:
    if ( len > 3 ) {
        // ...
    } else {
        // ...
    }
}

有効なケースが 2 つしかないため、コンパイラが処理することはあまりありません。典型的な実装 (極端な最適化なし) では、境界チェックを行い、次に 2 つの明示的なケースのテーブル ルックアップを行います。まともなコンパイラは、あなたのdefault場合の比較が、すでに行った境界チェックの1つに対応していることを検出し、それを複製しません。しかし、2 つのケースだけでは、特に最も頻繁なケースでは範囲外になるため、ジャンプ テーブルはおそらく 2 つの比較と比較して大きな違いはありません。

これがコードのボトルネックであるという実際のプロファイラー情報が得られるまで、私はそれについて心配しません。その情報を取得したら、さまざまなバリアントをプロファイリングしてどちらが高速かを確認できますが、最大の最適化を使用してプロファイリング情報をコンパイラにフィードバックしても、違いはないと思います。

于 2012-12-28T10:50:26.350 に答える
1

速度を心配している場合、真実は、何百ものif...elseorswitch...caseステートメントがない限り、 or ステートメントがアプリケーションの速度に実際の影響を与えないということです。速度が低下する場所は、反復またはループです。質問に具体的に答えるためif...elseに、switch...caseステートメントをdefault最初に現れるステートメントに変換することはできません。ただし、そうは言っても、に変換した場合switch...case、それらは同じ速度で実行されることになります (違いは小さすぎて、従来のベンチマーク ツールでは検出できません)。

于 2012-12-28T07:42:26.313 に答える
0

次の場合に範囲を使用できます。

switch (len) {
  case 3 ... INT_MAX:
    // ...
    break;
  case 2:
    // ...
    break;
  default:
    // ...
    break;
 }

しかし、それはGCCが提供する拡張機能です...

于 2012-12-28T07:39:37.613 に答える