<ul><ul><li>3imp における </li></ul></ul><ul><ul><li>Direct Function Invocations の </li></ul></ul><ul><ul><li>実装 </li></ul></u...
3imp <ul><li>3imp (Three Implementation Models for Scheme [ pdf ])という、Schemeコンパイラを実装するための論文がある </li></ul><ul><ul><li>Schem...
スタックベースVMの改善点 <ul><li>4.7 できそうな改善 </li></ul><ul><ul><li>1. グローバル変数とプリミティブ関数 </li></ul></ul><ul><ul><li>2.  関数を直接実行する </li>...
関数の直接実行? <ul><li>まず、Schemeでの関数呼び出しとは </li></ul><ul><ul><li>例:(func 1 2) </li></ul></ul><ul><ul><li>括弧の先頭(func)が関数、残り(1 2)が...
関数とは <ul><li>lambda式 </li></ul><ul><ul><li>(lambda (仮引数...) 本体...) </li></ul></ul><ul><ul><li>名なし関数 </li></ul></ul>
関数の直接呼び出しとは? <ul><li>名前のついた関数ではなく、lambda式を関数呼び出し形式の先頭に書いて直接呼び出す </li></ul><ul><ul><li>((lambda (...) ...)  引数...) </li></u...
let式 <ul><li>Schemeでローカル変数を導入するしくみ </li></ul><ul><li>よく使う </li></ul>(let ((x 111)) (* x x))
let式(2) <ul><li>実はlet式はマクロによって変換されてる </li></ul>((lambda (x) (* x x)) 111) <ul><li>関数の直接呼び出しの形が頻繁に発生!  m9(^Д^)ぷぎゃー </li></u...
コンパイル結果 (CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1) <ul><li>クロージャを生成し、そ...
実行時の動作 (CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1) <ul><li>初期状態 </li></ul>
実行 (1) (CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1) <ul><li>CONST, ARGで定...
実行 (2) <ul><li>CLOSEでクロージャ生成 </li></ul><ul><ul><li>(1 . 1)は受け付ける引数の数 </li></ul></ul><ul><ul><li>0はキャプチャする自由変数の数 </li></ul>...
実行 (3) <ul><li>SHIFTで末尾呼び出し用にスタック操作 </li></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2...
実行 (4) <ul><li>APPLY 1で、クロージャに引数1個を適用 </li></ul><ul><ul><li>適用した引数の数をスタックに積む </li></ul></ul>(CONST 111 ARG CLOSE (1 . 1) 0...
実行 (5) <ul><li>クロージャの本体に制御が移動 </li></ul><ul><li>LREF 0で最初の引数(111)を参照 </li></ul><ul><li>ARGでスタックに積む </li></ul>(CONST 111 AR...
実行 (6) <ul><li>同様 </li></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
実行 (7) <ul><li>GREF * で、グローバル関数*を参照 </li></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2...
実行 (8) <ul><li>SHIFTで末尾呼び出し用にスタック操作 </li></ul><ul><ul><li>一つ上のスタックフレームを潰す </li></ul></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (L...
実行 (9) <ul><li>APPLY 2で、*に2個の引数を適用 </li></ul><ul><li>結果として 12321 を得る </li></ul><ul><li>スタックは空に </li></ul>(CONST 111 ARG CL...
問題点 <ul><li>1ヶ所でしか使われない関数なのに、実行時にクロージャが生成されてしまうのは嬉しくない </li></ul><ul><li>関数のインライン展開のように、関数の本体を直接埋め込みたい </li></ul>
VMへの命令の追加 <ul><li>VMにEXPANDとSHRINKという命令を追加 </li></ul><ul><ul><li>EXPAND: スタックフレームを拡張 </li></ul></ul><ul><ul><li>SHRINK: スタ...
EXPAND <ul><li>EXPAND nで、n個の要素をスタックフレームに追加 </li></ul>現在の フレームm個 追加する 要素n個 フレームを拡張
SHRINK <ul><li>SHRINK nで、n個の要素をスタックフレームから取り除く </li></ul>
コンパイル時の動作 <ul><li>関数の直接呼び出しの形を見つけたら CLOSE〜APPLYの命令の代わりに、 EXPAND〜SHRINK命令を生成 </li></ul>
コンパイル結果 (CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) (CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG L...
実行時の動作 <ul><li>初期状態 </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
実行 (1) <ul><li>CONST, ARGで定数をスタックに積む </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
実行 (2) <ul><li>EXPAND 1でスタックフレームを1つの引数分拡張する </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
実行 (3) <ul><li>(LREF 0, ARG) x 2 </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
実行 (4) <ul><li>SHIFT, *関数を呼び出し、結果12321 </li></ul><ul><li>末尾呼び出しによってSHRINKが省略されている </li></ul><ul><li>実行時にmallocなしで済むように なった...
結論 <ul><li>関数の直接呼び出しを実装することで、実行時の無駄なクロージャ生成が省けた </li></ul><ul><li>めでたしめでたし </li></ul>
Upcoming SlideShare
Loading in...5
×

3impにおけるDirect Function Invocationsの実装

989
-1

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
989
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

3impにおけるDirect Function Invocationsの実装

  1. 1. <ul><ul><li>3imp における </li></ul></ul><ul><ul><li>Direct Function Invocations の </li></ul></ul><ul><ul><li>実装 </li></ul></ul><ul><ul><li>mokehehe </li></ul></ul>'09/04/05
  2. 2. 3imp <ul><li>3imp (Three Implementation Models for Scheme [ pdf ])という、Schemeコンパイラを実装するための論文がある </li></ul><ul><ul><li>SchemeのソースをコンパイルしVMで実行する </li></ul></ul><ul><li>スタックベースモデルの実装例 </li></ul>
  3. 3. スタックベースVMの改善点 <ul><li>4.7 できそうな改善 </li></ul><ul><ul><li>1. グローバル変数とプリミティブ関数 </li></ul></ul><ul><ul><li>2. 関数を直接実行する </li></ul></ul><ul><ul><li>3. 末尾再帰の最適化 </li></ul></ul><ul><ul><li>4. クロージャをヒープに作らない </li></ul></ul><ul><ul><li>5. 継続をジャンプに </li></ul></ul>
  4. 4. 関数の直接実行? <ul><li>まず、Schemeでの関数呼び出しとは </li></ul><ul><ul><li>例:(func 1 2) </li></ul></ul><ul><ul><li>括弧の先頭(func)が関数、残り(1 2)が引数 </li></ul></ul>
  5. 5. 関数とは <ul><li>lambda式 </li></ul><ul><ul><li>(lambda (仮引数...) 本体...) </li></ul></ul><ul><ul><li>名なし関数 </li></ul></ul>
  6. 6. 関数の直接呼び出しとは? <ul><li>名前のついた関数ではなく、lambda式を関数呼び出し形式の先頭に書いて直接呼び出す </li></ul><ul><ul><li>((lambda (...) ...) 引数...) </li></ul></ul><ul><li>そんな書き方普通しないからwww </li></ul>
  7. 7. let式 <ul><li>Schemeでローカル変数を導入するしくみ </li></ul><ul><li>よく使う </li></ul>(let ((x 111)) (* x x))
  8. 8. let式(2) <ul><li>実はlet式はマクロによって変換されてる </li></ul>((lambda (x) (* x x)) 111) <ul><li>関数の直接呼び出しの形が頻繁に発生! m9(^Д^)ぷぎゃー </li></ul>(let ((x 111)) (* x x))
  9. 9. コンパイル結果 (CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1) <ul><li>クロージャを生成し、それを呼び出すコードが出力される。 </li></ul>((lambda (x) (* x x)) 111) (let ((x 111)) (* x x)) マクロ展開 コンパイル
  10. 10. 実行時の動作 (CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1) <ul><li>初期状態 </li></ul>
  11. 11. 実行 (1) (CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1) <ul><li>CONST, ARGで定数をスタックに積む </li></ul>
  12. 12. 実行 (2) <ul><li>CLOSEでクロージャ生成 </li></ul><ul><ul><li>(1 . 1)は受け付ける引数の数 </li></ul></ul><ul><ul><li>0はキャプチャする自由変数の数 </li></ul></ul><ul><ul><li>(LREF〜)がコード本体 </li></ul></ul><ul><ul><li>malloc が発生 </li></ul></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
  13. 13. 実行 (3) <ul><li>SHIFTで末尾呼び出し用にスタック操作 </li></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
  14. 14. 実行 (4) <ul><li>APPLY 1で、クロージャに引数1個を適用 </li></ul><ul><ul><li>適用した引数の数をスタックに積む </li></ul></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
  15. 15. 実行 (5) <ul><li>クロージャの本体に制御が移動 </li></ul><ul><li>LREF 0で最初の引数(111)を参照 </li></ul><ul><li>ARGでスタックに積む </li></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
  16. 16. 実行 (6) <ul><li>同様 </li></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
  17. 17. 実行 (7) <ul><li>GREF * で、グローバル関数*を参照 </li></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
  18. 18. 実行 (8) <ul><li>SHIFTで末尾呼び出し用にスタック操作 </li></ul><ul><ul><li>一つ上のスタックフレームを潰す </li></ul></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
  19. 19. 実行 (9) <ul><li>APPLY 2で、*に2個の引数を適用 </li></ul><ul><li>結果として 12321 を得る </li></ul><ul><li>スタックは空に </li></ul>(CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1)
  20. 20. 問題点 <ul><li>1ヶ所でしか使われない関数なのに、実行時にクロージャが生成されてしまうのは嬉しくない </li></ul><ul><li>関数のインライン展開のように、関数の本体を直接埋め込みたい </li></ul>
  21. 21. VMへの命令の追加 <ul><li>VMにEXPANDとSHRINKという命令を追加 </li></ul><ul><ul><li>EXPAND: スタックフレームを拡張 </li></ul></ul><ul><ul><li>SHRINK: スタックフレームを縮小 </li></ul></ul>
  22. 22. EXPAND <ul><li>EXPAND nで、n個の要素をスタックフレームに追加 </li></ul>現在の フレームm個 追加する 要素n個 フレームを拡張
  23. 23. SHRINK <ul><li>SHRINK nで、n個の要素をスタックフレームから取り除く </li></ul>
  24. 24. コンパイル時の動作 <ul><li>関数の直接呼び出しの形を見つけたら CLOSE〜APPLYの命令の代わりに、 EXPAND〜SHRINK命令を生成 </li></ul>
  25. 25. コンパイル結果 (CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) (CONST 111 ARG CLOSE (1 . 1) 0 (LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2) SHIFT 1 APPLY 1) Before After <ul><li>クロージャ生成がなくなった! </li></ul>((lambda (x) (* x x)) 111) (let ((x 111)) (* x x)) =
  26. 26. 実行時の動作 <ul><li>初期状態 </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
  27. 27. 実行 (1) <ul><li>CONST, ARGで定数をスタックに積む </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
  28. 28. 実行 (2) <ul><li>EXPAND 1でスタックフレームを1つの引数分拡張する </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
  29. 29. 実行 (3) <ul><li>(LREF 0, ARG) x 2 </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
  30. 30. 実行 (4) <ul><li>SHIFT, *関数を呼び出し、結果12321 </li></ul><ul><li>末尾呼び出しによってSHRINKが省略されている </li></ul><ul><li>実行時にmallocなしで済むように なった! </li></ul>(CONST 111 ARG EXPAND 1 LREF 0 ARG LREF 0 ARG GREF * SHIFT 2 APPLY 2)
  31. 31. 結論 <ul><li>関数の直接呼び出しを実装することで、実行時の無駄なクロージャ生成が省けた </li></ul><ul><li>めでたしめでたし </li></ul>

×