Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
プログラミング言語処理系論 (9) Design and Implementation of Programming Language Processors
佐藤周行
(情報基盤センター/電気系専攻融合情報学コース)
今日の予定
Parrot設計の実際
最適化について
JVMで十分か?
(問題11)次の論文のうち、どれかを読み、要約せよ
1. JVM-hosted languages: they talk the talk, but do they walk the walk?
W. Li, D. White, J. Singer, PPPJ’13, 2013.
2. Characteristics of dynamic JVM languages
A. Sarimbekov et.al., VMIL’13, 2013.
parrot.org/
Register Machine
Register Machine
各種演算のオペランドにレジスタ(変数)を想定する
ハードウェア命令との親和性が高いと主張する
ハードウェアとの親和性を考えるとき、命令体系のレジスタセットとハードウェアのレジスタセットが異なる場合、「Register Allocation」が必要になる
レジスタの選択の自由度が上がる分、コードは一般に複雑
ISA of Parrot
Parrot
I (Integer)
N (Real)
S (String)
P (Polymorphic)
それぞれの型に応じてレジスタが用意されている
Ixx, Nxx, Sxx, Pxx
ターゲット言語の重要な要素
スクリプト言語
動的型付け
文字列の操作
Arrayの操作
Parrot
Perl6用のVM
多くの動的型付け言語対応を唱う Rakudo Perl6 Lua Winxed
では、Parrotのうたい文句を見てみましょう
Parrot is a language-neutral virtual machine for dynamic
languages such as Ruby, Python, PHP, and Perl. It hosts a powerful suite of compiler tools tailored to dynamic languages and a next generation regular expression engine.
少しambitiousなことが書いてある
Its architecture is fundamentally different than existing virtual machines such as the JVM or CLR, with optimizations for dynamic languages included, a register-based system rather than stack-based, and the use of continuations as the core means of flow control.
構成
Parrot
VM
言語として
PIR 中間言語系
PASM アセンブリ言語
Parrotの構造
Parrot
Perl他ターゲット言語
PIR
PASM
PBC
PIR: Parrot Intermediate Language PASM: Parrot Assembly Language PBC: Parrot ByteCode
では、Parrotの解析
データ型は
Integer
Number
String
PMC (PolyMorphic Container)
Register Machine
Registerとして
Ixx
Nxx
Sxx
Pxx
変数はスタックに積むことなく、「レジスタ名」でアクセスされる
簡単な例(ただし分厚いライブラリ付)
.loadlib 'io_ops'
.pcc_sub :main main: getstdin P0 getstdout P1 REDO: readline S0, P0 print S0 if S0, REDO end
Parrotに求められるものは何か?
「オブジェクト」に対する動的な型付けのサポート。そのためにPMCを用意した
PMC
Scalar/array/subroutine/namespace を収容できる
目的に応じて柔軟に型変換が可能になっている
PMCのタイプ
Env
Iterator
Array
Hash
String
Integer
Float
Exception
Timer
たとえば、これはどうか(動的関係ない
.sub 'example' :main $I1 = 96 $I2 = 64 print "Algorithm E (Euclid's algorithm)¥n" e1: $I4 = mod $I1, $I2 e2: unless $I4 goto done e3: $I1 = $I2 $I2 = $I4 branch e1 done: print "The greatest common denominator of 96 and 64 is " print $I2 print ".¥n" .end
ではこちらはどうか
$i = 4.3; print fact(); sub fact { $r = 1; while ($i > 0) { $r = $r * $i; $i--; } return $r; }
解決方法
特定の演算を指定して、polymorphicにする
この場合はdecrement, multiply
PMCを引数に取る時の挙動を定める
→ VTABLEで処理内容を指定する
型指定のタイミングを変数の代入の時点で制御できるようにする
代入のsemanticsを定める
実際 src/ops/math.opsでは
inline op dec(inout INT) { $1--; } inline op dec(inout NUM) { $1--; } inline op dec(invar PMC) { VTABLE_decrement(interp, $1); }
ISA
まずは命令セットを眺めてみましょうか
src/include/oplib/ops.h
1129個の命令
高レベルのものを含む(特に制御関係)
ISA 特に代入(set)を見てみましょう
inline op set(out INT, in INT) { $1 = $2; } inline op set(out INT, in NUM) { $1 = (INTVAL)($2); } inline op set(out INT, in STR) { $1 = Parrot_str_to_int(interp, $2); }
inline op set(out NUM, in NUM) { $1 = $2; } …
Non-trivialなsetは…
inline op set(invar PMC, in INT) { VTABLE_set_integer_native(interp, $1, $2); } inline op set(invar PMC, in NUM) { VTABLE_set_number_native(interp, $1, $2); } inline op set(invar PMC, invar STR) { VTABLE_set_string_native(interp, $1, $2); } inline op set(invar PMC, inconst STR) { VTABLE_set_string_native(interp, $1, $2); } inline op set(out INT, invar PMC) { $1 = VTABLE_get_integer(interp, $2); }
Set
全体として以下のパターンに対してそれぞれ対応する関数が定められている
set (単純なパターン)
set P {I/N/S}
set Px [key] By/ set Ax Py[key]
白鳥は優雅に泳いでいるが…というパターン
関数呼び出しについて(PIR)
まずはフレーム # factorial.pir .sub 'main' :main .local int count .local int product count = 5 product = 1 $I0 = 'fact'(count,
product) say $I0 .end
.sub 'fact' .param int c .param int p
loop: if c <= 1 goto fin p = c * p dec c branch loop fin: .return (p) .end
PASMでの表現
.pcc_sub :main main:
new P10, 'ResizableIntegerArray'
set I1,0
## P9 is used as a stack for temporaries.
new P9, 'ResizableIntegerArray'
loop:
print "fact of "
print I1
print " is: "
new P0, 'Integer'
set P0,I1
local_branch P10, fact
print P0
print "¥n"
inc I1
eq I1,31,done
branch loop
done:
end
### P0 is the number to compute,and also the return value.
fact:
lt P0,2,is_one
## save I2, because we're gonna trash it.
push P9,I2
set I2,P0
dec P0
local_branch P10, fact
mul P0,P0,I2
pop I2,P9
local_return P10
is_one:
set P0,1
local_return P10
まともなやつ
.pcc_sub :main main: # Get @ARGV as a
ResizableStringArray get_params "0", P0 # Discard the program
name shift S0, P0 # Look for additional args if P0, FOUND_EXTRA_ARG print "Hello World¥n" end
FOUND_EXTRA_ARG:
shift S1, P0
print "Hello "
print S1
print "¥n"
end
関数コール
Signature で型付け
IN -> OUT
関数コールごとにcontextを作成(特別のスタックがあるわけではない)
Continuationを受け渡すことができる
Coroutine等が実現可能…
OpCodeを見てみる
クラス関係
Callmethod, tailcallmethod, addmethod, can, does, isa (VTABLE), newclass, subclass, get_class, class, addparent, removeparent, addattribute, removeattribute, getattribute, setattribute, inspect
文字列命令
ord, chr, chopn, concat, repeat, length, bytelength, pin, new, substr, replace, index, rindex, sprintf, upcase, downcase, titlecase, stringinfo, join, split, encoding, find_encoding, trans_encoding, finc_cclass, escape, compose, find_compose
PMC
new, root_new, typeof, find_method, defined, exists, delete, elements, push, pop, shift, splice, setprop, getprop, freeze, thaw, box, iter,
プロセス管理
Schedule, add_handler, wait, pass, invokecc, returncc, disable/enable-preemption, terminate,
関数コール
get/set_params, get/set_result
名前解決
後は、グローバルな変数(名前でしかアクセスできないデータ)の扱いですが
以下の命令が用意されている
{store/find}_lex 名前付き変数の解決
get_namespace をやる前提で
get_global
set_global
find_name
(問題9’’) ParrotについてJVMと同じことを解析せよ
さてと、
レジスタ変数(またはスタック上の変数)の間の演算列という形でプログラムが書かれるようになれば「最適化」が可能になります
今までのtree traversalではできる最適化にも限度がありましたが…
「最適化」は性能をあげるためには必須です
動的言語では
動的な型変換が性能にどれだけのマイナス要因になっているかを理解する
静的にでも動的にでも型変換のルーチンをスキップできれば、性能が上がる
JVM上でスクリプト言語を実行する場合
引数の動的型チェックを行う
引数の型チェックをできるものは静的にすませてしまう
最適化の分類
1. ローカル最適化 1. 基本ブロック内外で、一定のパターンに当てはまるものを変換(ピープホール最適化)
2. 基本ブロック内で、変数の挙動を解析して変換
2. データフロー解析をもとにした大域最適化
3. データ依存解析をもとにしたループ最適化
4. 手続き間解析をもとにした手続き間最適化 1. LTO
2. WPRO
動的型変換においては
ルーチン内で、型が伝播する可能性がある
ローカルな解析をもとにして
データフロー解析をもとにして
ルーチンをまたいで、型が分かる場合がある
手続き関解析をもとにして
LTO (Link Time Optimization)
関数をまたいだ最適化を効率よく行うためにリンク時に最終的に最適化を行うことが普通になりつつある
コンパイラシステムがサポートすることも普通になってきている
代表的な手続き間解析
部分評価
手続きに渡される引数の性質を利用する
int g(x) { if (x==0) return 1; else return h(x-1); } main() { print g(2)+g(0); }
では、これはどのくらい最適化の余地が…
.pcc_sub :main main:
new P10, 'ResizableIntegerArray'
set I1,0
## P9 is used as a stack for temporaries.
new P9, 'ResizableIntegerArray'
loop:
print "fact of "
print I1
print " is: "
new P0, 'Integer'
set P0,I1
local_branch P10, fact
print P0
print "¥n"
inc I1
eq I1,31,done
branch loop
done:
end
### P0 is the number to compute,and also the return value.
fact:
lt P0,2,is_one
## save I2, because we're gonna trash it.
push P9,I2
set I2,P0
dec P0
local_branch P10, fact
mul P0,P0,I2
pop I2,P9
local_return P10
is_one:
set P0,1
local_return P10