コンパイラの最適化により、switchステートメントは通常、同等のif-else-ifステートメントよりも高速です(この記事で説明されているように)。
この最適化は実際にはどのように機能しますか?誰か良い説明はありますか?
コンパイラの最適化により、switchステートメントは通常、同等のif-else-ifステートメントよりも高速です(この記事で説明されているように)。
この最適化は実際にはどのように機能しますか?誰か良い説明はありますか?
回答:
コンパイラは、必要に応じてジャンプテーブルを作成できます。たとえば、リフレクターを使用して生成されたコードを確認すると、文字列の巨大なスイッチの場合、コンパイラーはハッシュテーブルを使用してこれらをディスパッチするコードを実際に生成します。ハッシュテーブルは文字列をキーとして使用し、case
コードへのデリゲートを値として使用します。
これは多くの連鎖if
テストよりも漸近的に優れた実行時間を持ち、実際には比較的少ない文字列でも高速です。
これは通常if..else if ..
、人が簡単にスイッチステートメントに変換できるシーケンスに遭遇する現代のコンパイラと同様に、少し簡略化されています。コンパイラも同様です。ただし、さらに面白くするために、コンパイラは構文に制限されないため、範囲や単一のターゲットなどが混在する「スイッチ」のようなステートメントを内部で生成できます。また、スイッチとifの両方でこれを実行できます(実行します)。 .elseステートメント。
Anyhoo、Konradの答えの拡張は、コンパイラがジャンプテーブルを生成する可能性があることですが、これは必ずしも保証されていません(望ましくありません)。さまざまな理由で、ジャンプテーブルは最新のプロセッサの分岐予測子に悪いことをし、テーブル自体はキャッシュの動作に悪いことをします。
switch(a) { case 0: ...; break; case 1: ...; break; }
コンパイラが実際にこのためのジャンプテーブルを生成した場合、if..else if..
ジャンプテーブルが分岐予測を無効にするため、代替スタイルコードよりも遅くなる可能性があります。
通常、switch / caseステートメントは1レベルの深さで高速になりますが、2つ以上になると、入れ子になった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レベルだけの深さの単純な型にはスイッチ/ケースを使用することですが、より複雑な比較や複数のネストされたレベルには、従来のif / else構成を使用しますか?