C++ のステートメントで使用できるのは数値のみであることがわかったので、C++ のswitch
ステートメントと多数の 's の間には、もっと深い違いがあるに違いないと思いましたif-else
。
したがって、私は自問しました:
- (どのように)実行速度、コンパイル時間の最適化、および一般的なコンパイルの点で
switch
異なりますか?if-elseif-elseif
ここでは主に MSVC について話しています。
C++ のステートメントで使用できるのは数値のみであることがわかったので、C++ のswitch
ステートメントと多数の 's の間には、もっと深い違いがあるに違いないと思いましたif-else
。
したがって、私は自問しました:
switch
異なりますか? if-elseif-elseif
ここでは主に MSVC について話しています。A switch is often compiled to a jump-table (one comparison to find out which code to run), or if that is not possible, the compiler may still reorder the comparisons, so as to perform a binary search among the values (log N comparisons). An if-else chain is a linear search (although, I suppose, if all the relevant values are compile-time integral constants, the compiler could in principle perform similar optimizations).
多くの場合、switch ステートメントは、コンパイラの最適化の一般的なソースです。つまり、それらがどのように扱われるかは、コンパイラで使用する最適化設定によって異なります。
switch ステートメントをコンパイルする最も基本的な (最適化されていない) 方法は、それを一連のif ... else if ...
ステートメントとして扱うことです。コンパイラがスイッチを最適化する一般的な方法は、次のようなジャンプ テーブルに変換することです。
if (condition1) goto label1;
if (condition2) goto label2;
if (condition3) goto label3;
else goto default;
label1:
<<<code from first `case statement`>>>
goto end;
label2:
<<<code from first `case statement`>>>
goto end;
label3:
<<<code from first `case statement`>>>
goto end;
default:
<<<code from `default` case>>>
goto end;
end:
この方法が高速である理由の 1 つは、条件内のコードが小さいためです (したがって、条件が誤って予測された場合の命令キャッシュ ペナルティが小さくなります)。また、「フォールスルー」の場合は、実装がより簡単になります (コンパイラーはgoto end
ステートメントを省略します)。
コンパイラは、(ラベルでマークされた場所への) ポインターの配列を作成することでジャンプ テーブルをさらに最適化し、その配列へのインデックスとして切り替えている値を使用できます。これにより、コードからほぼすべての条件が削除されます (切り替えている値がケースのいずれかと一致するかどうかを検証するために必要だったものを除く)。
注意点: ネストされたジャンプ テーブルは生成が難しく、一部のコンパイラは作成を試みることさえ拒否します。そのため、最大限に最適化されたコードが重要な場合は、switch
別の内部にa をネストしswitch
ないでください (特に MSVC がネストされたswitch
es をどのように処理するかは 100% わかりませんが、コンパイラのマニュアルで説明されているはずです)。