間接ジャンプは、おそらく命令のデコードに最適です。
1997 年の Intel P6 などの古いマシンでは、間接的なジャンプで分岐の予測ミスが発生する可能性があります。
Intel Core i7 などの最新のマシンでは、分岐の予測ミスをかなりうまく回避する間接ジャンプ予測子があります。
しかし、間接分岐予測子を持たない古いマシンでも、いたずらをすることができます。ちなみに、このトリックは、Intel P6 の時代から Intel Code Optimization Guide に記載されています (だった):
次のようなものを生成する代わりに
loop:
load reg := next_instruction_bits // or byte or word
load reg2 := instruction_table[reg]
jmp [reg]
label_instruction_00h_ADD: ...
jmp loop
label_instruction_01h_SUB: ...
jmp loop
...
としてコードを生成します
loop:
load reg := next_instruction_bits // or byte or word
load reg2 := instruction_table[reg]
jmp [reg]
label_instruction_00h_ADD: ...
load reg := next_instruction_bits // or byte or word
load reg2 := instruction_table[reg]
jmp [reg]
label_instruction_01h_SUB: ...
load reg := next_instruction_bits // or byte or word
load reg2 := instruction_table[reg]
jmp [reg]
...
つまり、命令フェッチ/デコード/実行ループの先頭へのジャンプを、各場所のループの先頭にあるコードに置き換えます。
間接的な予測子がない場合でも、これははるかに優れた分岐予測を行うことがわかります。より正確には、条件付きの単一ターゲットの PC インデックス付き BTB は、後者のスレッド化されたコードで、間接ジャンプのコピーが 1 つしかない元のコードよりもはるかに優れています。
ほとんどの命令セットには特殊なパターンがあります。たとえば、Intel x86 では、ほとんどの場合、比較命令の後に分岐が続きます。
頑張って楽しんでね!
(念のために言っておきますが、業界の命令セット シミュレーターで使用される命令デコーダーは、ほとんどの場合、N ウェイ ジャンプのツリー、またはデータ駆動型デュアルを実行し、ツリー内の各エントリを指して、N ウェイ テーブルのツリーをナビゲートします。他のノード、または評価する関数に。
ああ、おそらく言及する必要があります: これらのテーブル、これらの switch ステートメントまたはデータ構造は、特別な目的のツールによって生成されます。
ジャンプ テーブルのケース数が非常に多くなると問題が発生するため、N ウェイ ジャンプのツリー - 1980 年代に作成したツール mkIrecog (命令認識ツールの作成) では、通常 64K までのジャンプ テーブルを作成しました。つまり、16 ビットでジャンプします。当時のコンパイラは、ジャンプ テーブルのサイズが 16M (24 ビット) を超えたときに壊れました。
データ駆動型、つまり他のノードを指すノードのツリー。(a) 古いマシンでは間接的なジャンプがうまく予測できない可能性があり、(b) 多くの場合、命令間に共通のコードがあることが判明したためです。命令ごとにケースにジャンプし、次に共通コードを実行し、次に切り替えて、2 番目の予測ミスを取得するときの分岐予測ミスでは、わずかに異なるパラメーター (命令ストリームの何ビットを消費するかなど) を使用して共通コードを実行します。ここで、次に分岐するビットのセットは (are) です。
私は mkIrecog に非常に積極的でした。スイッチで最大 32 ビットを使用できるようにすると言っていましたが、実際の制限により、ほとんどの場合 16 ~ 24 ビットで停止しました。最初のデコードは 16 ビットまたは 18 ビットの切り替え (64K ~ 256K エントリ) であることがよくありましたが、他のすべてのデコードははるかに小さく、10 ビット以下でした。
うーん: 1990 年頃に mkIrecog を Usenet に投稿しました
。(親切にしてください: 私は当時若かったです。これが Pascal だったのか C だったのか思い出せません。それ以来、何度も書き直しましたが、C++ ビット ベクトルを使用するようにはまだ書き直していません。)
私が知っているこの種のことを行う他のほとんどの人は、一度に 1 バイトずつ処理を行います。つまり、8 ビット、256 ウェイ、ブランチまたはテーブルのルックアップです。)