170 likes | 270 Views
LMNtalからC言語への変換の設計と実装. 2007年度卒業論文発表. 2008/2/5. 1G04R013-7. 石川 力. LMNtalとは. ルールによる階層グラフ構造の書き換えで計算が進む言語. ルールの実行順序は自由. →可能なところから計算が進む並行計算のモデル. X=[a,a,a], {p(X,[])}. X=[], {p(X,[a,a,a])}. →. X=[a|T], {p(X,Y), $p[Y]} :- {p(T,M), {M=[a|Y],$p[Y]}}. LMNtal実行までの処理の流れ. 中間命令列が各処理の中心にある.
E N D
LMNtalからC言語への変換の設計と実装 2007年度卒業論文発表 2008/2/5 1G04R013-7 石川 力
LMNtalとは ルールによる階層グラフ構造の書き換えで計算が進む言語 ルールの実行順序は自由 →可能なところから計算が進む並行計算のモデル X=[a,a,a], {p(X,[])}. X=[], {p(X,[a,a,a])}. → X=[a|T], {p(X,Y), $p[Y]} :- {p(T,M), {M=[a|Y],$p[Y]}}.
LMNtal実行までの処理の流れ 中間命令列が各処理の中心にある
中間命令列 中間命令列とは、グラフを操作するための粒度の小さい命令セットである 以下の例は概念を示すためのもので実際の中間命令列とは異なっている (1) グラフの始点を選ぶ findatom [1, 0, 'a'_1] リンクをたどる (2) deref [2, 1, 0, 0] [2, 'b'_1] (3) func アトムの種類を確認する (4) newatom [3, 0, 'c'_1] 新しいアトムを生成する リンクを張る (5) newlink [1, 0, 3, 0] アトムを消す (6) freeatom [2]
研究の動機・目的 より速いLMNtal実行環境を得ること SLIMは軽量・高速な実行時処理系を目指し多くの改良が施されている ・ リンクオブジェクトの排除 ・ ポインタメンバへのデータの埋め込み ・ 内部データ構造の絞込み * SLIMはJava版実行時処理系と比較して数十倍高速 ここにJava版で得られた高速化の手法を持ち込むことで更なる高速化 * Java版実行時処理系において、Javaへの変換を行うと インタープリット実行に比べ数倍高速 ↓ SLIM上においてCへの変換を行う
変換の流れ findatomテンプレート i? = lmn_mem_get_atomlist (?, ?); t? = ? for ( ? = atomlist_head(i?); ? != atomlist_end(i?); ? = atom_next(?) ) { 繰り返す内容 } 失敗時命令 findatom [1, 0, 'a'_1] deref [2, 1, 0, 0] [2, 'b'_1] func newatom [3, 0, 'c'_1] newlink [1, 0, 3, 0] freeatom [2] 変換前 1.中間変数を出力 long v1, a1; /* アトムが入る */ long v2, a2; /* アトムが入る */ ・・・ 3.補助変数に 型情報を出力 i1 = lmn_mem_get_atomlist (v0, 'a'_1); t1 = 'a'_1; for ( v1 = atomlist_head(i1); v1 != atomlist_end(i1); v1 = atom_next(v1) ) { if(LMN_ATOM_GET_ATTR(v1,0) != 0) continue; ・・・ } return 0; 2.各中間命令を対応する Cソースコードに変換 変換後
1.中間変数の出力 各中間命令が使用する中間変数を関数の先頭で宣言する Javaへの変換機では中間変数を基底クラスのポインタ配列で管理し、 使用する際にはその要素に静的なキャストを行っていた Object[] v = new Object[100]; SLIMでのインタプリタでも汎用の変数によって同様の方法を用いていた LmnWord *v = malloc(100*sizeof(LmnWord)); Cへの変換機では、最適化が効きやすいよう変数をばらばらにし、最終的には各変数に静的な型を持たせられるようにした。 現在は汎用型ポインタと整数データのみが利用可能である。 Atom *v1, *v2, *v4; Membrane *v3; int v5; この方法を用いることで、doubleのようにサイズの大きな一時データを 扱うことも期待できる
2.中間命令に対応するC言語コードを出力 各中間命令を実行するためのC言語コードを出力する freemem [ 1 ] ⇒ lmn_mem_free(v1); loadruleset [ 0, 3 ] ⇒ lmn_mem_add_ruleset(v0, (void*)&trans_ruleset_3); しかしSLIMで設計変更した影響により、 中間命令と実行コードが対応しない場合がある そのような場合出力処理が煩雑になりがちである if ( リンクの先がデータなら ) { 代入によるデータコピー } else { 専用関数によるデータコピー } この問題を解決するため、「中間命令に対応するC言語コードを出力する関数」 を出力する補助ツールを作成した #ieq v v if(! (TRANS_DATA($$0) == TRANS_DATA($$1))) $$f; この記述は、中間命令ieqに対して出力するコードを表現しており、 変数0と変数1が等しくなければ次の繰り返しを行う
3.補助変数への型出力 SLIMにおいてはデータ構造の設計上 アトムの種類(型)が分からないとそのアトムのリンクを参照できない しかし機械的な変換では各命令にアトムの種類の情報が 含まれていないため動的に型を調べるしかなかった ⇒型を解析して静的に種類が分かっている場合は無駄を回避したい そこで型を表す補助変数を用意した newatom [1, 0, 'a'_1] ⇒ v1 = newatom('a'_1); t1 = 'a'_1; 中間命令列において、ある中間変数の型が予測できる時があれば、 その時補助変数の内容は定数性を持っているはずである ↓ 自前で型解析を行わないでも C言語コンパイラの定数の畳み込み最適化を利用することで 型に関する最適化を行える
まとめと今後の課題 LMNtalからC言語への変換機能を設計、実装した。 C言語コンパイラの最適化を利用することでルール内の最適化を行い ある程度実用的な例ではSLIMでのインタープリット実行に比べ 3倍程度の高速化を得られた。 速度面では、中間命令を操作しないルール内でのこれ以上最適化は難しく、 中間命令列をルールの全体の意味を考慮して最適化するか、 ルールの境界を越えた最適化を行う必要がある。 今回C言語に変換したのはルール内のみで、 実行方式については自然なC言語とはかけ離れている。 LMNtalプログラムにおいてC言語に近い記述をしていれば それに似た実行方式をとる自然なC言語コードを出力できるようにしたい。
参考 [1] 乾敦行,工藤晋太郎,原耕司,水野謙,加藤紀夫,上田和紀 : ``階層グラフ書換えモデルに基づく統合プログラミング言語LMNtal'' コンピュータソフトウェア,Vol.~25, No.~1, pp.124--150, 2008. [2] 石川力, 堀泰祐, 村山敬, 岡部亮, 上田 和紀 : ``軽量なLMNtal実行時処理系SLIMの設計と実装'' 情報処理学会第70回全国大会(発表予定), pp.??--??, 2008.
(1) findatom [1, 0, 'a'_1] (2) deref [2, 1, 0, 0] この中間命令列は次のように 機械的に変換が可能である [2, 'b'_1] (3) func (4) newatom [3, 0, 'c'_1] 以下のソースコードも 説明のための概念的なもの (5) newlink [1, 0, 3, 0] (6) freeatom [2] (1) foreach(v1 in 'a'_1){ if( v1.attr[0] != 0 ){ continue; } else { v2 = v2.link[0]; } (2) if( v2.func != 'b'_1){ continue; } (3) (4) v3 = newatom('c'_1); newlink(v1, 0, v3, 0); (5) freeatom(v2); (6) /* success */ } /* failure */
変換の流れ char *trans_symbols[] = { "lmn", "tal", "slim" }; *使用される文字列、型等の静的な情報を それぞれ配列として出力 int trans_rule_0_0() { int v0d, v0a; ・・・ } int trans_rule_1_0() { ・・・ } *各ルールを関数として出力 +ルール内で使用される中間変数を出力 ・ 変数の使用目的に合わせた型で宣言 +各中間命令に対応するC言語コードを出力 ・ その際に変数の型を補助変数に出力
SLIMにおいてはデータ構造の設計上 アトムの種類(型)が分からないとそのアトムのリンクを参照できない しかし機械的な変換では各命令にアトムの種類の情報が 含まれていないため動的に型を調べるしかなかった ⇒型を解析して静的に種類が分かっている場合は無駄を回避したい 型を表す補助変数を用意する newatom [1, 0, 'a'_1] ⇒ v1 = newatom('a'_1); t1 = 'a'_1; 種類が予測できる時は型を表す変数の中身が一意に決まるはずなので、 型検査をC言語コンパイラが最適化してくれる ↓ 自前で型解析を行わないでもC言語コンパイラの最適化を利用できる
C言語への変換の効果 ルール実行の失敗が多い場合や、各ルールの長さが短い場合 ボトルネックが最適化の範囲の外のためあまり効果がえられない 例) 単純なアトムの確保/開放を繰り返す場合 インタープリット実行 インタープリット実行 3.775 (s) 3.775 C言語変換 C言語変換 2.414 2.414 それに対し、ルールが複雑になると最適化の効く範囲が実行時間に占める割合が増えるため効果が高くなる 例) lambda計算(Church数の例題) インタープリット実行 インタープリット実行 13.098 C言語変換 C言語変換 4.827