PMD
教えてくれます
ブランチが3つ未満のスイッチは非効率的です。代わりに、ifステートメントを使用してください。
何故ですか?なぜ3?彼らはどのように効率を定義しますか?
ステートメントは、とswitch
である2つの特別なJVM命令でコンパイルされるためです。これらは多くのケースで作業する場合に役立ちますが、ブランチが少ない場合はオーバーヘッドが発生します。lookupswitch
tableswitch
代わりに、if/else
ステートメントは典型的なje
jne
...チェーンにコンパイルされます。これは高速ですが、ブランチの長いチェーンで使用される場合は、さらに多くの比較が必要になります。
バイトコードを見ると違いがわかります。いずれにせよ、私はこれらの問題について心配する必要はありません。何か問題が発生する可能性がある場合は、JITが処理します。
実例:
switch (i)
{
case 1: return "Foo";
case 2: return "Baz";
case 3: return "Bar";
default: return null;
}
にコンパイルされます:
L0
LINENUMBER 21 L0
ILOAD 1
TABLESWITCH
1: L1
2: L2
3: L3
default: L4
L1
LINENUMBER 23 L1
FRAME SAME
LDC "Foo"
ARETURN
L2
LINENUMBER 24 L2
FRAME SAME
LDC "Baz"
ARETURN
L3
LINENUMBER 25 L3
FRAME SAME
LDC "Bar"
ARETURN
L4
LINENUMBER 26 L4
FRAME SAME
ACONST_NULL
ARETURN
その間
if (i == 1)
return "Foo";
else if (i == 2)
return "Baz";
else if (i == 3)
return "Bar";
else
return null;
にコンパイルされます
L0
LINENUMBER 21 L0
ILOAD 1
ICONST_1
IF_ICMPNE L1
L2
LINENUMBER 22 L2
LDC "Foo"
ARETURN
L1
LINENUMBER 23 L1
FRAME SAME
ILOAD 1
ICONST_2
IF_ICMPNE L3
L4
LINENUMBER 24 L4
LDC "Baz"
ARETURN
L3
LINENUMBER 25 L3
FRAME SAME
ILOAD 1
ICONST_3
IF_ICMPNE L5
L6
LINENUMBER 26 L6
LDC "Bar"
ARETURN
L5
LINENUMBER 28 L5
FRAME SAME
ACONST_NULL
ARETURN
ifステートメントを使用する場合と比較して、スイッチを使用する場合の効率はわずかに向上しますが、ほとんどの場合、これらの向上はごくわずかです。そして、その価値のあるソースコードスキャナーは、マイクロ最適化がコードの明確さの二次的なものであることを認識します。
彼らは、スイッチが大幅に短い場合、ifステートメントはswitchステートメントよりも読みやすく、コードの行数も少ないと言っています。
PMDのウェブサイトから:
TooFewBranchesForASwitchStatement:Switchステートメントは、複雑な分岐動作をサポートするために使用することを目的としています。スイッチはif-thenステートメントほど理解しにくいため、ごく少数の場合にのみスイッチを使用することはお勧めできません。このような場合は、if-thenステートメントを使用して、コードの可読性を高めます。
何故ですか?
コードがJITコンパイラによって(最終的に)ネイティブコードにコンパイルされる場合、さまざまな命令シーケンスが使用されます。スイッチは、間接分岐を実行する一連のネイティブ命令によって実装されます。(シーケンスは通常、テーブルからアドレスをロードしてから、そのアドレスに分岐します。)if / elseは、条件を評価する命令(おそらく比較命令)とそれに続く条件分岐命令として実装されます。
なぜ3?
これは経験的な観察であり、生成されたネイティブコード命令の分析やベンチマークに基づいていると思います。(またはそうではないかもしれません。絶対に確実にするために、そのPMDルールの作成者にその番号をどのように導き出したかを尋ねる必要があります。)
彼らはどのように効率を定義しますか?
命令の実行にかかった時間。
私は個人的にこのルールに問題を抱えています...より正確にはメッセージに問題があります。if / else
ステートメントは、2つのケースを持つスイッチよりも単純で読みやすいと言えます。効率の問題は二次的なものであり、おそらく無関係です。
私はそれがスイッチとif/elseがコンパイルされる方法に関係していると信じています。
switchステートメントを処理するのに5回の計算が必要だとします。ifステートメントが2つの計算を行うと言います。スイッチのオプションが3つ未満の場合、ifsでは4つの計算になり、スイッチでは5つの計算になります。ただし、スイッチのオーバーヘッドは一定のままであるため、3つの選択肢がある場合、ifは3 * 2で処理されますが、スイッチでは5が処理されます。
何百万もの計算を見るときの利益は非常に無視できます。それはあなたに影響を与えるかもしれない何かよりもむしろ「これがそれをするためのより良い方法である」という問題です。それは、かなりの反復でその関数を何百万回も循環する何かでのみそうします。