Kink: プロトタイプベース言語で の invokedynamic (構想) @miyakawa_taku 2012-02-22 JJUG Night Seminar
Kink: プロトタイプベース言語での invokedynamic (構想)
@miyakawa_taku
2012-02-22
JJUG Night Seminar
要旨
• Kink という JVM 言語を作っています
• JavaScript や Lua と同様に、クラスのないオブジェクト指向言語です
• invokedynamic がどこに使えるものか考えてみました
1
論点
• Kink の紹介
– クラスのない世界
–なんでもメソッド
–気になる性能は?
• invokedynamic の使い所
–スロットアクセスの最適化
– SwitchPoint で実行モード切り替え
2
論点
• Kink の紹介
– クラスのない世界
–なんでもメソッド
–気になる性能は?
• invokedynamic の使い所
–スロットアクセスの最適化
– SwitchPoint で実行モード切り替え
3
クラスのない世界
• クラスはなく値だけがある
• データもメソッドもいきなり値のスロット (≒フィールド) に突っ込む
&DOG = value DOG.&bark = { 'Bow!' } printline(DOG.bark) # => Bow!
4
クラスのかわりに親
• 自分自身がスロットを持っていなかったら親から取ってくる
• 親のことをプロトタイプとも言う
&PROTODOG = value('bark' : { 'Bow!' }) &DOG = PROTODOG.child printline(DOG.bark) # => Bow!
5
親/子関係の一つ覚え
• つまり子は親の名前空間を継承する
• 様々な仕組みを親/子関係で実現する
– クラス / インスタンス (のようなもの)
–親クラス / 子クラス (のようなもの)
–外側のスコープ / 内側のスコープ
6
なんでもメソッド
• 足し算も掛け算もメソッド
• 条件分岐もメソッド
• 代入もメソッド
• 引数渡しもメソッド
7
足し算も掛け算もメソッド
• 演算子はメソッドの構文糖
(TOP + BOTTOM) * HEIGHT
TOP.plus(BOTTOM).multiply(HEIGHT)
8
条件分岐もメソッド
• if-then-else は bool 値のメソッド
(N % 2 == 0).then { 'even' } { 'odd' }
(N % 2 == 0).then({ 'even' } { 'odd' })
9
代入もメソッド
• スロットへの代入はスロットのメソッド
&NUM = 42
&NUM.assign(42)
10
引数渡しもメソッド
• 引数渡しは仮引数列 (スロットのリスト) のメソッド
&diff = { (&X &Y) X - Y }
&diff = { [&X &Y] = _args X - Y }
11
気になる性能は?
• tarai(13 6 0) (たらい回し関数、竹内関数) で マイクロベンチマーク
&tarai = { (&X &Y &Z) (X <= Y).then { Y } { tarai(tarai(X - 1 Y Z) tarai(Y - 1 Z X) tarai(Z - 1 X Y)) } } printline(tarai(~ argv.map { __.int }))
12
結果: bc よりは速い
0 20 40 60 80 100 120 140 160
JRuby 1.6.6
Groovy 1.7.0
Lua 5.0.3
C Ruby 1.9.2
mawk 1.3.3
Python 2.6.6
scm 5e5
C Ruby 1.8.7
Kink 2012-02-19
GNU bc 1.06.95
秒
13
つまりこんな言語
• クラスがなくて値だけがある
• 代入も制御構造もメソッド呼び出し
• 速くするのはかなり難渋する
• あと、末尾呼び出しでスタックオーバーフローが起きないことを保証しています
14
論点
• Kink の紹介
– クラスのない世界
–なんでもメソッド
–気になる性能は?
• invokedynamic の使い所
–スロットアクセスの最適化
– SwitchPoint で実行モード切り替え
15
処理系の作り
• プログラムはJVM バイトコードにせず、「評価器」を作って実行する (cf. SICP 4章)
&loop.do { print("twift!") loop }
call
slot proc
chunk
抽象構文木
call
slot proc
chunk
評価器 プログラム
16
invokedynamic の使い所
• そもそもJVM バイトコードにしないので、メソッド呼び出しがいきなり invokedynamic になることはない
• ただし、高速化のためにバイトコード生成を使っている所はあって、そこに invokedynamic が使えるかも
17
スロットアクセスの最適化
• スロットアクセスを速くするため、スロット集合のクラスを実行時に生成している
• 本来はアクセス元のコードでキャッシュを効かせて速くする (現在は未実装)
• ここに invokedynamic が使えそう
DOG
bark { "Bow!" }
jump { "Pong!" }
DOG.bark
ここに使う
18
if (slots.getClass() == #cachedClass) { return ((#cachedClass) slots).bar; } else { deoptimize(); return slots.get("bar"); }
スロットアクセスの最適化
• キャッシュヒット → フィールドを直接取得
• キャッシュミス → フォールバック
• MethodHandles#guardWithTest が使えそう
test
target
fallback
19
SwitchPoint で実行モード切り替え
• スロットやリストへの代入はメソッド呼び出し
• 毎回メソッドを呼ぶと大変なコストなので、可能な限りショートカット処理している
• ここで SwitchPoint#guardWithTest が使えそう
20
if 代入メソッドは再定義されていない? ショートカット処理 else 真面目にメソッド呼び出し
SwitchPoint で実行モード切り替え
• SwitchPoint は MethodHandle のファクトリ
– SP.guardWithTest(target, fallback):MethodHandle
–通常は target が実行される
– SwitchPoint が invalidate されると、それ以降は fallback が実行される
• 処理の実行モードを切り替えるのに使える
–代入処理の MH を SwitchPoint で生成
–代入メソッドが再定義されたら invalidate
21
invokedynamic の使い所 (rep)
• クラスのない言語でも充分使い所はある
–スロットアクセスの最適化
–実行モード切り替え
22
参考
• Kink Programming Language
– http://code.google.com/p/kink-lang/
• V8 JavaScript Engine
– http://code.google.com/p/v8/
–スロットアクセスを最適化する仕組みのパクリ元 (Hidden Class)
• An efficient implementation of SELF
– http://dl.acm.org/citation.cfm?id=74884
23