まるで聞こえます packed-switch
Javaのものと同等です tableswitch
, 、 と sparse-switch
に lookupswitch
.
a packed-switch
フォームで索引付けされたシンプルなジャンプテーブルを使用します low + n
, 、 どこ low
の間で最も低いテスト値です case
ラベル、および n
の入力です switch
. 。各インデックスの値は、それぞれのバイトコードオフセットを表します case
. 。正しいジャンプアドレスを見つけることは、一定の時間操作です。
a sparse-switch
各キーがからのテスト値であるキー価値ペアのソートされたリストを使用します case
ラベル、値はジャンプオフセットです。 aの正しいジャンプターゲットを見つける lookupswitch
キーのバイナリ検索が必要なため、対数時間操作です。
コンパイラは使用するものを選択します。キーがクラスター化される傾向がある場合 詰め込まれています 緊密に、次にa packed-switch
(または、Javaの用語では、a tableswitch
)効率的に放出できます。しかし、キーがある場合 スパース, 、および値の範囲(high - low + 1
)が大きい場合、ジャンプテーブルを使用するには、対応するものがあるかどうかに関係なく、その範囲内のすべての値がジャンプテーブルに存在する必要があるため、バイトコードの大きなブロックが必要になります。 case
ラベル。これらのシナリオでは、コンパイラはaを発します sparse-switch
(lookupswitch
).
興味深いことに、Dalvikのエンジニアは、これらのオペコードを使用する必要がある重要な分布を説明する方法でこれらのオペコードに名前を付けることを選択しましたが、Javaエンジニアは、バイトコードオペランドが似ている概念データ構造を説明する名前を選択しました。
いくつかの例を見てみましょう。次のJavaコードを検討してください。 tableswitch
(そして、Dalvikに変換された場合、a packed-switch
):
static String packedSwitch(final int n) {
switch (n) {
case 5:
return "Five";
case 3:
return "Three";
case 1:
return "One";
default:
return "Other";
}
}
概念的には、のペイロード packed-switch
opcodeは次のようになります:
ご覧のとおり、それはかなりコンパクトです。 5つのスロットのうち3つが実際のものを指します case
ターゲット、残りの2つがにジャンプします default
目標。しかし、テスト値がさらに広がった場合はどうなりますか?
static String sparseSwitch(final int n) {
switch (n) {
case 500:
return "Five Hundred";
case 300:
return "Three Hundred";
case 100:
return "One Hundred";
default:
return "Other";
}
}
コンパイラがこれをaとして放出しようとした場合 packed-switch
, 、ペイロードは次のようになります:
数百のスロットのうち3つだけが実際に指していることに注意してください case
元のコードからのラベル。残りは、ジャンプテーブルを埋めるためだけにあります。あまりスペース効率が良くありませんか?そのため、コンパイラはaを発します sparse-switch
, 、この特定の例では、はるかにコンパクトなバイトコードフットプリントがあります。
さて、それははるかに合理的です、あなたは思いませんか?ただし、マイナス面は、入力に基づいてジャンプするインデックスを正確に把握する代わりに、一致するテスト値が見つかるまでテーブルでバイナリ検索を実行する必要があることです。スイッチが大きいほど、効果は対数曲線を持っていますが、パフォーマンスへの影響が大きくなります。