Dart VM

Dart VM <-- たぶんこれが正式名称のはず。
DVM <-- JVM風にいうと。。

公式資料

公式ページのslide一覧へのリンクです。

DartVMのリビジョンログ

現在のステータスなども書いてます。

Dart VM Overview

今後はこちらのsphinxのドキュメントに更新していこうと思います。
もしくは、作成したスライドを参照。

概要

dart用のlanguage based virtual machine
ターゲットアーキテクチャは、x86(ia32), x64
現在は、ARM向けとMIPS向けの実装が進んでおり、Linux上からクロスビルドできる。
おそらく2013年中に全機能実装完了するのでは?
ARM向けとMIPS向けの検証に、simarmとsimmipsというビルド方法もある。

対応OSはLinux, Windows, MacOS, Android
Androidで動作するDart VMは、Linux(ia32)環境からクロスビルドしてadbで転送すると動く。

Linuxは、perfが使えるので便利。
JITコンパイラが生成した関数も、perf用にシンボルを生成して測定できる。

VMの構造としては、V8のアイディアを大まかに継承している。
V8からのコピーではなく、あくまで改良。

インタプリタ実行は行わず、
最初にICベースのcodegenが汎用的なコードを生成して実行する。
hotcodeはOptimizer(FlowGraphOptimizer)で高速なコードにリコンパイルする。

メモリ管理周り(heapやchunkの取り方と管理方法)と、GC向けの構造は、後期のV8とほぼ同じはず。
SmallInteger(smi)はDart VMにも存在する。
GC対象のRawObjectの下位2bitが、HeapObjectTagとSmiTagになっている点も同様。

双方のメモリレイアウトの違いは下記が詳しい。

他言語との比較

Dartは、以下を目指しており、まだまだ言語仕様を拡張中。ベータ版がリリースされたので、多少は落ち着くかも。
Class based, Purely Object Oriented, Optionally Typed,
Mixin-based Inheritance, Message-passing Concurrency, Mirror-based Reflection

他のVMと比較して、Dart VMの大きな特徴は、
(1) Optional Type + Operator overload
Dartのメソッド呼び出しは、(a + b)は、a.add(b)の糖衣構文である。
すべてのメソッドコールは、汎用的な型で動作するInstanceCallとして呼び出され、その際に型情報を収集する。
上記のa.add(b)も、RuntimeのICではbがint型でもdouble型でもdynamic型として動作する。
しかしIC(InlineCache)によって予め用意された型以外(たとえば bがStringの場合、言語仕様上 ICは存在しない)
や、classを読み込んで対応するメソッドがなかった場合、
コンパイル時にエラーになるか、Runtimeでエラーになる。

収集した型情報は、再コンパイル時に活用される。
型が1つだけなら特殊化された命令にFlowGraphOptimizerが置換する。
型が複数とりえるなら、PolymorphicInstanceCall命令に置換する。

注意すべきことは、コードに型情報を書いた場合、コンパイルエラーになるだけで、
コンパイル時に型情報は収集されない。そのためOptional TypeはRuntimeに影響を与えない。

(2) IsolateとSnapshot
Dartにはマルチスレッド用のAPIは用意されておらず、スレッドという概念もない。
Dart VMは起動した直後にIsolate領域を作成し、実行時のtopレベルのコンテキストになる。
Isolateは初期化時にCoreライブラリをSnapshotされたバイナリから復元し、Heapを作成し、GCを作成し、JITコンパイラを作成する。
IsolateをSpawnした場合、起動時の1つのプロセスの中に、SpawnされたIsolate領域を作成する。

並行な処理を行いたい場合、複数のIsolateを起動し、非同期なメッセージパッシングで記述する。
Isolate間は、非同期のメッセージパッシングでのみ連携できる。
メッセージパッシングの対象は、snapshot機能によってシリアライズ/でシリアライズされたMessageオブジェクトである。JSONみたいなもん。

スレッドが存在しないため、DartのAPI中に、マルチスレッド用の同期処理やatomicな操作がほとんど見られない。
※DartのAPIのC++レイヤーの実装や、DartVM内部ではマルチスレッドになっていて、Lockは存在する。
※しかしIsolateで分割されているため、Isolate間のAtomic操作は、ほぼない。

Optinal TypeとGenericsに関して


var型もint型も同じ中間表現に落ちる。
intと書いた方がコンパイルエラーを早期に発見できる。
Optional Typeは、Runtimeへ影響を与えないことが設計方針らしい。

Genericsの場合も、intで実体化しても、doubleで実体化しても、同じ中間表現。
型の違いは、RuntimeのICで全て吸収する。※今のところは。

JITコンパイラの中間表現


中間表現は、主に3種類に分類できる。
(1) InstanceCall
codegenが生成する汎用的なIC。
(2) PolymorphicIntanceCall
複数の型を取りうる場合に、FlowGraphOptimizerが生成する。ほぼBoxedオブジェクト。
(3) 特殊化された命令(BinarySmiInstrや、BinaryMintInstr, BinaryDoubleInstr)
単一の型のみ取りうる場合に、FlowGraphOptimizerが生成する。可能な限りUnboxedされており、レジスタに乗った状態で計算する。

Binary命令と比較すると、PoilymorphicInstanceCallは3~5倍遅い、InstanceCallは10倍~20倍遅い。
Binary命令で計算する限り、JVMのpremitive型の演算とほぼ同等のスピードになっているはず。

GC

GCは、世代別GC。new_gen_heap_sizeが32M, old_gen_heap_sizeが512Mが初期値。
GCの起点は、Allocationに失敗した時。
GCを管理するのは、Heapオブジェクト。Heapは、Isolateごとに分割されており、Isolateが管理する。

new領域は、Scavengerクラス。mmapした領域を2分割するCopyGC。適時old領域にpromotion
old領域は、PageSpaceクラス。MarkSweepGC。page単位でallocationして、freelist管理で分割融合、再利用を行う。
1pageに収まらないオブジェクトはLargePageとして別管理、適時allocation。

特徴
peer領域をもつ。詳細は別途。
GC対象外のHandlerを多数持つ。詳細は別途。


過去の記事

FlowGraphOptimizer (2012/05追加)
V8のCrankshaftに相当する、最適化JITコンパイラ
メソッドを3000回くらい呼び出すと起動する。

Dart VMは内部でASTから中間表現に変換したのち、codegen もしくは flow_graph_compilerでコンパイルする。
flow_graph_compilerは、SSA formである。※昔はno ssa formが存在した。

codegenの場合は、汎用的な中間表現にしか変換されないが、
FlowGraphOptimizerを使用すると、ICでプロファイルした型を参照しながら、特殊化された中間表現に置き換えられていく。
特殊化のelseケースに飛んだ場合、そのコードはdeoptimizeされる。

dumpを見る限りは四つ組ぽいかんじ。

中間表現はassign対象の左辺がindexを持っていて、temp_indexとssa_temp_indexがある。
indexは基本的に支配関係で付与しているんだと思うけど、、indexの大小関係を使って最適化していたあれは、V8だったか

  • 最終更新:2013-06-26 23:06:50

このWIKIを編集するにはパスワード入力が必要です

認証パスワード