2011-04-11 15 views
5

私のプロジェクトには、ドメイン固有の言語からコンパイルされたバイトコードを実行するVMがあります。私は、バイトコードの実行時間を改善できる方法を見ています。最初のステップとして、マシンコードのコンパイルに入る前にバイトコードインタープリタを単純に改良する方法があるかどうかを見たいと思います。最適な仮想マシン/バイトコードインタプリタループ

インタプリタのメインループは次のようになります。

while(true) 
{ 
    uint8_t cmd = *code++; 
    switch(cmd) 
    { 
    case op_1: ...; break; 
    ... 
    } 
} 

QUESTION:は、アセンブラに頼ることなく、このループを実装するためのより高速な方法はありますか?

表示される1つのオプションは、ラベルアドレスで動的なgotoを使用することに特有のGCCです。各ケースの最後にbreakではなく、次の命令に直接ジャンプすることができました。私はオプティマイザが私のためにこれを行うことを期待していましたが、逆アセンブリを見れば明らかにそうではありません。ほとんどのop_codesの終わりに繰り返される定数ジャンプがあります。

該当する場合、VMは浮動小数点レジスタと整数レジスタ(それぞれ8個)を持つ単純なレジスタベースのマシンです。スタックはなく、グローバルヒープのみ(その言語はそれほど複雑ではありません)です。

答えて

3

1つの非常に簡単な最適化ではなく スイッチ/ケース/ケース/ケース/ケース/ケース、

だけ各機能は、指定されたコマンドを処理するであろう関数ポインタの配列(または結合を定義することですあなたは同じ関数に配列して複数のエントリを設定することができ、かつ機能自体は正確なコードをチェックすることもでき、その場合には、コマンドの)、そして代わりの

switch(cmd) 

だけ

を行います3210

これはあなたがあまりにも多くのコマンドを持っていないことが与えられています。また、すべての可能なコマンドを定義しないかどうかチェックしてください(たぶん300のコマ​​ンドしかないかもしれませんが、2バイトを表現する必要があるので、65536個のアイテムを持つ配列を定義するのではなく、もしそうでなければ、参照をしないでください)

これをしないのであれば、最もよく使われているコマンドをswitch文の先頭に並べ替えてください。

それ以外の場合、ハッシュテーブルを調べることになりますが、多くのコマンドがないと仮定します。この場合、ハッシュ関数を実行するオーバーヘッドはおそらくスイッチを必要としない場合よりも多くなります。 (または、VERY単純なハッシュ関数を持つ)

+1

スイッチの代わりに関数呼び出しを行う方が速い(関数呼び出しにかなりのオーバーヘッドがある)ことはほとんどありません。とにかくスイッチはジャンプテーブルに適合するはずです(GCCはこれを少なくとも行います)。 –

+0

クールでは、gccについては分かりませんでしたが、とにかく関数をインラインで定義でき、引数を渡す代わりにグローバル変数にするなど、関数呼び出しのオーバーヘッドを非常に小さくします。もちろん、このレベルになると、アセンブラですべてをコード化する方が簡単かもしれません。 (それを行う方法がわかっている場合) – Cray

+1

ディスパッチテーブル経由でのインラインコールはできません。ここで最も近いオプションはGCCラベルアドレスを使用することです。これは基本的にジャンプテーブルで独自のインライン関数を作ることができます。 –

1

アーキテクチャとは何ですか?ワードアラインのオペコードでスピードアップすることもできますが、コードサイズを吹き飛ばすため、キャッシュミスのコストとのバランスをとる必要があります。あなたがswitch(*code++)、直接ポインタ間接を使用し、その後、どこでもswitch()よりcmdを使用しない場合は

+0

x86_64。それは確かに私のコードサイズをバルーンにしますが、コードは非常に小さいです。 –

+0

2番目の考えでは、コードがインラインデータ(定数)でインターリーブされているため、これは機能しない可能性があります。整列を保つためには、すべてが8バイトに成長する必要があります。それはかなりのコストです(私はとにかくそれを試してみません)。 –

+0

いいえ、速度がゼロになります。 –

-1

私が見るほとんど明らか最適化は、

  1. です。 while(true)ループの場合、これはほとんど役に立たない可能性があります。
  2. switch()にはbreakの代わりにcontinueを使用できます。をif/elseまたはswitchの内部で使用すると、コンパイラは実行が外側のループにジャンプしなければならないことを認識します。 breakswitchに関して)については同じことが当てはまらない。 これが役立つことを願っています。
+0

ポイント1私は前に試しましたが、それは違いはありません(オプティマイザはそれを同じように見ています)。ポイント2私は実際には、それは非常にわずかな違いがあるように見えます(オプティマイザはそれを単独ではしないと思っていますが、時差はそれほど小さいので統計的な異常を除外するのは難しい)。 –