通常、switch ステートメントは、コンパイラの最適化により、同等の if-else-if ステートメント (この記事で説明されているものなど) よりも高速です。
この最適化は実際にどのように機能しますか? 誰か良い説明がありますか?
通常、switch ステートメントは、コンパイラの最適化により、同等の if-else-if ステートメント (この記事で説明されているものなど) よりも高速です。
この最適化は実際にどのように機能しますか? 誰か良い説明がありますか?
コンパイラは、必要に応じてジャンプ テーブルを作成できます。たとえば、リフレクターを使用して生成されたコードを確認すると、文字列の巨大なスイッチについて、コンパイラーがハッシュ テーブルを使用してこれらをディスパッチするコードを実際に生成することがわかります。ハッシュ テーブルは、文字列をキーとして使用し、case
コードへのデリゲートを値として使用します。
これは、多くの連鎖テストよりも実行時間が漸近的でif
あり、実際には比較的少数の文字列でも高速です。
これは、通常、現代if..else if ..
のコンパイラが人によって簡単に switch ステートメントに変換できるシーケンスに遭遇するため、わずかに単純化したものです。コンパイラもそうします。しかし、さらに楽しいことを追加するために、コンパイラは構文によって制限されないため、範囲、単一のターゲットなどが混在する「switch」のようなステートメントを内部的に生成できます。 .else ステートメント。
とにかく、コンラッドの答えの拡張は、コンパイラがジャンプテーブルを生成する可能性があるということですが、それは必ずしも保証されているわけではありません(望ましくもありません)。さまざまな理由で、ジャンプ テーブルは最新のプロセッサの分岐予測子に悪いことをし、テーブル自体は動作をキャッシュするのに悪いことをします。
switch(a) { case 0: ...; break; case 1: ...; break; }
if..else if..
コンパイラが実際にこのためにジャンプ テーブルを生成した場合、ジャンプ テーブルが分岐予測を無効にするため、別のスタイルのコードよりも遅くなる可能性があります。
ノーマッチ統計は良くないかもしれません。
ソースを実際にダウンロードすると、if と switch の両方のケースで、一致しない値は 21 であることがわかっています。コンパイラは、どのステートメントを常に実行する必要があるかを認識して抽象化できる必要があり、CPU は適切に分岐予測できる必要があります。
私の意見では、より興味深いケースは、すべてのケースが壊れるわけではありませんが、それは実験の範囲ではなかった可能性があります.
通常、switch/case ステートメントは 1 レベルの深さの方が高速ですが、2 つ以上になると、switch/case ステートメントはネストされた if/else ステートメントの 2 ~ 3 倍の時間がかかり始めます。
この記事には、そのようなステートメントがネストされている場合の速度の違いを強調するいくつかの速度比較があります。
たとえば、彼らのテストによると、サンプル コードは次のようになります。
if (x % 3 == 0)
if (y % 3 == 0)
total += 3;
else if (y % 3 == 1)
total += 2;
else if (y % 3 == 2)
total += 1;
else
total += 0;
else if (x % 3 == 1)
if (y % 3 == 0)
total += 3;
else if (y % 3 == 1)
total += 2;
else if (y % 3 == 2)
total += 1;
else
total += 0;
else if (x % 3 == 2)
if (y % 3 == 0)
total += 3;
else if (y % 3 == 1)
total += 2;
else if (y % 3 == 2)
total += 1;
else
total += 0;
else
if (y % 3 == 0)
total += 3;
else if (y % 3 == 1)
total += 2;
else if (y % 3 == 2)
total += 1;
else
total += 0;
同等の switch/case ステートメントの実行にかかった時間の半分で完了しました。
switch (x % 3)
{
case 0:
switch (y % 3)
{
case 0: total += 3;
break;
case 1: total += 2;
break;
case 2: total += 1;
break;
default: total += 0;
break;
}
break;
case 1:
switch (y % 3)
{
case 0: total += 3;
break;
case 1: total += 2;
break;
case 2: total += 1;
break;
default: total += 0;
break;
}
break;
case 2:
switch (y % 3)
{
case 0: total += 3;
break;
case 1: total += 2;
break;
case 2: total += 1;
break;
default: total += 0;
break;
}
break;
default:
switch (y % 3)
{
case 0: total += 3;
break;
case 1: total += 2;
break;
case 2: total += 1;
break;
default: total += 0;
break;
}
break;
}
ええ、それは初歩的な例ですが、要点を示しています。
したがって、1 レベルの深さしかない単純な型には switch/case を使用するという結論になるかもしれませんが、より複雑な比較や複数の入れ子になったレベルには従来の if/else コンストラクトを使用しますか?
if over ケースの唯一の利点は、最初のケースの発生頻度が著しく増加する場合です。
しきい値がどこにあるのか正確にはわかりませんが、最初の「ほぼ常に」最初のテストに合格しない限り、ケース構文を使用します。