6

Android Dalvik VMを勉強していますが、ファイルvm / mterp / out/InterpC-portable.cppのmterpコードを読んだときに質問があります。実際には、dexファイルのバイトコードを解釈するのはdalvikvmの主要なインタープリターループです。このファイルを作成した場合は、次のようにswitch-case構造を選択します。

while (hasMoreIns()) {
   int ins = getNextIns();
   switch(ins) {
     case MOV:
       //interprete this instruction
       ...
       break;
     case ADD:
       ...
       break;
     ...
     default: break;
   }
 }

ただし、mterpが使用するものは私の考えとは大きく異なり、次のような魔法のコード(私にとって)を使用します。

FINISH(0);

HANDLE_OPCODE(OP_NOP)
   FINISH(1);
OP_END

HANDLE_OPCODE(OP_MOVE)
   ...
OP_END

...

私はそれをグーグルで検索し、それが変更された「スレッド」スタイルの実行であるように見えます。これは、switch-caseスタイルとは異なり、whileループの分岐操作を削除するためパフォーマンスが向上します。しかし、私はまだこのコードと、なぜそれがパフォーマンスに優れているのかを理解できません。インタプリタの次のコードをどのように見つけますか?

4

1 に答える 1

7

簡単なガイダンスとして、outディレクトリは前処理されたファイルでいっぱいであり、コードを理解しようとしている場合、私が読むのに最適なものとは言えません。対応するソース(それ自体)は、およびディレクトリInterpC-portable.cppの内容です。portablec

コードがオペコードディスパッチを実行する方法に関しては、FINISHマクロの定義を次のように確認する必要がありportable/stubdefs.cppます。

# define FINISH(_offset) {                                                  \
        ADJUST_PC(_offset);                                                 \
        inst = FETCH(0);                                                    \
        if (self->interpBreak.ctl.subMode) {                                \
            dvmCheckBefore(pc, fp, self);                                   \
        }                                                                   \
        goto *handlerTable[INST_INST(inst)];                                \
    }

このマクロは、各オペコード定義の最後で使用され、switch (opcode)ステートメントと同等の役割を果たします。簡単に言うと、これはPCが指す命令コードユニットを読み取ります— inst = FETCH(0)—オペコードを取得します— INST_INST(inst)—そしてそのオペコードをすべてのオペコードのアドレスのテーブルへのインデックスとして使用します。gotoアドレスは、ステートメントで直接分岐されます。

これgotoは、非標準のGCC拡張機能である「計算されたgoto」です。これについてはGCCマニュアルで読むことができます。また、2008年にGoogle IOでDalvikの内部について行ったプレゼンテーションで、このトピックについて少し知ることができます( https://sites.google.com/siteで見つけてください)。 / io / dalvik-vm-internals。)

私の講演では、この手法のパフォーマンス特性についても触れています。簡単に言えば、それは分岐の量を節約し、分岐予測で比較的うまく機能します。ただし、インタープリターを作成するためのより良い方法があります(講演で取り上げたように、実際にはCPU固有のDalvikインタープリターが機能します)。

そして、もう少し大きなコンテキストでは、バイトコードをネイティブCPU命令にコンパイルすると、コンパイルされた結果を保持するのに十分なRAMがあると仮定すると、一般に、最もよく調整されたインタープリターよりも実行が速くなります。Froyoで導入されたトレースベースのDalvikJITは、適度な量の追加RAMを使用して、適度に実り多いパフォーマンスの向上を実現するというトレードオフを行うことを目的としていました。

于 2012-11-14T07:41:40.517 に答える