x86用の最速の仮想マシン設計とは何ですか?
-
10-07-2019 - |
質問
x86で仮想マシンを実装しますが、どのような設計が最良の結果をもたらすか疑問に思います。ジュースを潰すには何に集中すればよいですか?仮想マシン全体をx86アセンブリに実装します。
あまり指示がなく、フォームを選択できます。指示は、ブロック内のsmalltalkの構文に直接投影されます。私が考えていた命令設計を提供します:
^ ... # return
^null # return nothing
object # address to object
... selector: ... # message pass (in this case arity:1 selector: #selector:)
var := ... # set
var # get
考えていたVMの種類:
mov eax, [esi]
add esi, 2
mov ecx, eax
and eax, 0xff
and ecx, 0xff00 # *256
shr ecx, 5 # *8
jmp [ecx*4 + operations]
align 8:
operations:
dd retnull
dd ret
# so on...
retnull: # jumps here at retnul
# ... retnull action
ret:
# ... ret action
#etc.
さらに別の仮想マシンの実装が必要な理由を尋ね始めないでください。解釈ルーチンは、必要なときにいつでも手に入れることができるものではありません。他の場所で提案しているほとんどの仮想マシンは、パフォーマンスを犠牲にして移植性を重視しています。私の目標は移植性ではなく、パフォーマンスです。
このインタープリターが必要な理由は、smalltalkブロックが同じ方法で解釈されないためです:
A := B subclass: [
def a:x [^ x*x]
clmet b [...]
def c [...]
def d [...]
]
[ 2 < x ] whileTrue: [...]
(i isNeat) ifTrue: [...] ifFalse: [...]
List fromBlock: [
"carrots"
"apples"
"oranges" toUpper
]
解釈ルーチンから得られる真の利益、つまりプログラムを読み込むコンテキストの選択が必要です。もちろん、優れたコンパイラーは、ほとんどの場合、「ifTrue:ifFalse」または'whileTrue:'、またはリストの例。インタープリターの必要性がなくなるのは、ブロックが期待どおりに処理されるかどうかわからない場合が常にあるからです。
解決
ここでは移植性について多少の混乱があるので、いくつかの問題を明確にする義務があります。これらは私の謙虚な意見ですので、もちろん、あなたはそれらに反対する自由があります。
http://www.complang.tuwien.acに出会ったと思います.at / forth / threading / VMを真剣に作成することを検討している場合は、説明した手法については詳しく説明しません。
既に述べたように、VMを対象とすることには、コードサイズの削減、コンパイラの複雑さの軽減(コンパイルの高速化につながることが多い)、移植性(VMのポイントは言語の移植性 、したがって、VM自体が移植可能でないかどうかは関係ありません)。
例の動的な性質を考慮すると、VMは他の一般的なものよりも JITコンパイラに似ています。ですから、S.Lottはこのケースでポイントを逃しましたが、Forthについての彼の言及は非常にその場にあります。非常に動的な言語用にVMを設計する場合、解釈を2つの段階に分けます。
-
オンデマンドでASTストリームを調べて、より意味のある形式に変換するプロデューサーステージ(たとえば、ブロックを取得し、すぐに実行するか、後で実行するためにどこかに保存するかを決定する)新しい種類のトークン。基本的に、ここでの解析で失われる可能性のある状況依存情報を回復します。
-
1から生成されたストリームを取得し、他のマシンと同様に盲目的に実行するコンシューマステージ。 Forthのようにすると、命令ポインタをジャンプする代わりに、保存されたストリームをプッシュして、それで完了できます。
おっしゃるように、いまいましいプロセッサが別の方法でどのように機能するかを真似しても、必要なダイナミズム(またはセキュリティのような気になる価値のある他の機能)を達成することはできません。それ以外の場合は、コンパイラを作成します。
もちろん、ステージ1で複雑な最適化を任意に追加できます。
他のヒント
本当に高速なものが必要な場合は、 LLVM を使用してみてください。高レベルのプログラム記述からほとんどのプロセッサ用のネイティブコードを生成できます。最も便利なものに応じて、独自のアセンブリ言語を使用するか、アセンブリフェーズをスキップしてllvm構造を生成できます。
それがあなたの問題に最適かどうかはわかりませんが、プログラムの他の部分ではコンパイルできないパフォーマンスの重要なコードの実行を行う場合、間違いなく使用します。
通訳のポイントは、ほとんどの場合 移植性です。私が考えることができる最速のアプローチは、JITコンパイラーが行うように、メモリ内でx86コードを直接生成することですが、もちろん、もはやインタープリターはありません。コンパイラがあります。
ただし、アセンブラーでインタープリターを作成することで最高のパフォーマンスが得られるかどうかはわかりません(アセンブラーの第一人者で、プロジェクトの範囲が非常に限られている場合を除く)。高レベルの言語を使用すると、シンボル検索やレジスタ割り当て戦略などのより良いアルゴリズムに集中できます。
エンコードされていない命令を次のように設定すると、ディスパッチルーチンを高速化できます。
mov eax, [esi]
add esi, 4
add eax, pOpcodeTable
jmp eax
これにはオーバーヘッドが必要です<!> lt; CPUの各<!> gtのディスパッチごとに4サイクル。 Pentium 4.
さらに、パフォーマンス上の理由から、各プリミティブルーチンでESI(IP)をインクリメントすることをお勧めします。インクリメントが他の命令とペアになる可能性が高いためです。
mov eax, [esi]
add eax, pOpcodeTable
jmp eax
〜1-2サイクルのオーバーヘッド。
パフォーマンスに焦点を当てた仮想マシンを作成する理由を尋ねる必要がありますか? x86コードを直接書くだけではどうですか?これ以上速くなることはありません。
非常に高速に解釈される 言語が必要な場合は、今後をご覧ください。彼らのデザインは非常にきちんとしていて、コピーがとても簡単です。
JITが気に入らず、目標が移植性でない場合。 Google NativeClient プロジェクトに興味を持つかもしれません。静的アナリスト、サンドボックスなどを行います。ホストはRAW x86命令を実行できます。