Never Stop Questioning

仮想マシン

最終更新:

t-style

- view
メンバー限定 登録/ログイン

仮想マシン(Virtual Machine : VM)

現実の計算機(CPU)上に別の計算機をソフトウェアで実現したもの。とくに本稿ではVMWareのような一般的なOS(Windows, Linux)を動作させるようなVMではなく、JavaVMのような処理系を提供するためのVMを対象とする。

レジスタマシン

特徴

VMではあまり採用されない。しかし、レジスタマシンにおける命令では、例えばA=B+Cといった演算を1命令で実行できる(3オペランド形式の場合)ためスタックマシンに比べ高速化できる可能性がある。そのほか、多くのCPUで採用されている命令と同様の命令を定義することで、VMとネイティブのコードを共通化できる。

具体例

Lua VM

Ver.5以降、スタックマシンからレジスタマシンとなった。3オペランド形式で、クロージャの作成や末尾再帰最適化のための命令が用意されている。C言語で実装されており、型無しの言語の中ではかなり高速。クロージャを作成ために、Return時に参照する可能性がある変数をUpValue(ヒープメモリ)に退避する。

DalvikVM

Androidで動作するJava VM。ARMをターゲットとしており、全く同じレジスタ構成をしている(らしい)。Andoroid向けのJVMがJIT化できていないため、高速なJVMとして重宝されている模様。

スタックマシン

特徴

VMではもっとも多く採用されている。これはメモリをどのように使うかを深く考えなくてよいためVMの設計が簡単となることに起因していると思われる。たとえば、A=B+Cという演算に対して、Push(B), Push(C), Add, Store(A)という命令を発行することになるが、この際、メモリのどの領域を使うかは自明であり、レジスタの割り当てのような計画は必要ない。

具体例

Java VM (JVM)

型ごとに演算命令がある。基本的にC言語のスタックの使い方を踏襲している。オブジェクトの操作に関する機能が内包されている。JITによって相当高速化されている。

Python VM

正式名称は不明。型無しの演算命令となっている。そのほか、配列アクセスのための命令が充実している。

Gauche VM

Scheme用の奇跡的に高速なVM。合成された命令がたくさんある。

その他のマシン

SECDマシン

Stack, Environment, Code, Dumpという4つの(ツリー構造を構成する)リストをレジスタとして持つVM。ある時点での継続をSECDのコピーとして保存することができるという特徴があり、Schemeのような継続を直接操作することが必要な言語を簡単に実装できる。命令数も基本的に少ない。もともと学術的な趣が強く、高速化は困難。

VMの高速化

JIT化

JIT(ジャストインタイム)で中間コードをコンパイルする。したがって、起動時に時間を必要とするものの、起動してしまえば、非常に高速になる。なお、前述のように起動時にコンパイルしたらあとはそのままという方式(静的JIT)と、実行時にコンパイルする方式(動的JIT)がある。ちなみに、JITは、ターゲットマシンやアプリケーションの特性に合わせて後からコンパイルできるという意味で、通常のコンパイラで生成したコードより有利になる可能性がある。

命令の結合(Complex命令)

1つの処理をできる限り短い命令数で達成できるように命令あるいはアーキテクチャをデザインする。たとえば、上述したとおり、スタックマシンの命令にくらべレジスタマシンの命令の方が、演算に関する必要な命令数を減らすことができる。また、比較した結果に基づいてJUMPするといった場合、それを複合させた命令を作っておくことで、一つの命令で複数の処理ができる。

型付け

命令の中で条件分岐があると高速化にはデメリットとなる。そこで、あらかじめ型を決めておき、実行時はチェック無しに型に応じた演算を実行することで高速化できる。これは言語のデザインにも影響する。

静的なメモリ確保

動的にメモリを確保すると時間を要する。そこで、たとえば、クロージャの作成処理は目をつぶって、事前確保したスタックで処理するといった高速化を行う。
記事メニュー
目安箱バナー