liveness-based garbage collection for lazy …tau/lecture/...lazy 怠惰な...
TRANSCRIPT
Liveness-Based Garbage Collection for Lazy Languages
Prasanna Kumar K., Amitabha Sanyal, Amey Karkare
(ISMM’16, June 14, 2016)
発表者:工学部電子情報工学科4年 03-164003 小泉透
Lazy
怠惰な
プログラマの三大美徳の一つ(laziness)
「明日できることを今日やらない」
Lazy languages
遅延評価(lazy evaluation)を行う言語のこと
遅延評価は、必要呼び(call-by-need)とも言う
要するに、必要になるまで計算を行わないということ
Lazy languages (cont.)
Scheme や Haskell が有名
後回しにした計算に関する情報はサンク(thunk)と呼ばれる構造体にしまわれる
この分オーバーヘッドがあるが、最終的に使われない部分を計算しなくていい得で稼ぐ作戦
Liveness-Based GCと Reachability-based GC
スタックや大域変数から辿っていけるものが「到達可能」(Reachable)
この後実際に使われるものが「生きている」(live)
「到達可能」といっても実際には「生きていない」データが多いのではないか?
如何に生きていないかの簡単な例
如何に生きていないかの簡単な例
z
如何に生きていないかの簡単な例
如何に生きていないかの簡単な例
実験用言語の仕様
実験用言語の制限
関数に関数を渡せない(一階の関数型言語)
let rec x ← (xを含む式) みたいにできない(せっかくlazyなのに無限リストが扱えない)
関数にはlet束縛した変数しか渡せない(これは解析を簡単にするため)
×(let z ← (length (cons x y)))
○(let l ← (cons x y) in (let z ← (length l)))
実験用言語の操作的意味論
Liveness解析の作戦
(let z ←(cons x y))と書いてあって、そのあとに(cdr z)みたいなのが出てくる場合は、yをGCしてはいけない
lengthみたいな関数を考えると、この推論の連鎖がいつまでも止まらないから困る
なんらかの「無限」を扱える理論が必要になりそう
Liveness解析の作戦
(car z)が出てくる場合は、z.0と書く
(cdr z)が出てくる場合は、z.1と書く
(let x←(cdr z) in (car x))みたいになると、 x.0になり、連鎖して、z.10になる
この変数名の後ろに出てくるもの(アクセスパターン)を解析することにする
Length関数に適用してみる
length関数は、おおざっぱに(length (cdr l))みたいな感じの再帰になっている
つまり、lへのアクセスパターンは l.ε l.1 l.11 l.111 l.1111……
これは、正規表現 / 1* /で表せそうだ!
落とし穴
(cons x y)と出てきたらx.??やy.??はどう埋めたらいいだろうか?
Liveness解析(真)
(cons x y)と出てきたらx.??やy.??はどう埋めたらいいだろうか?
場合分けになってしまってうまくいかなそう
0と対消滅する0 や1と対消滅する1 を導入するとうまくいく
忘れがちだがクロージャーに含まれるということを表す2を定義しておく
Liveness解析(真)
Length関数のアクセスパターンには、以下の方程式が成り立つ
𝐿𝐹𝑙𝑒𝑛𝑔𝑡ℎ 𝜎 = 2𝜎 ∪ 1𝐿𝐹𝑙𝑒𝑛𝑔𝑡ℎ 2𝜎 ∪ 2𝐿𝐹𝑙𝑒𝑛𝑔𝑡ℎ 2𝜎
これは、以下の文脈自由文法で表されるアクセスパターンを生成する
𝑆 → 𝐷𝜎,𝐷 → 2 1𝐷2 2𝐷2
このアクセスパターンに入っているセルは生きているし、入っていなければ死んでいる
扱いづらい0 や1 と2
lengthの例では運よく0 や1 がなかったが、一般には出現しうる。また、2に関しても適切な正規化が必要
出現した場合、以下の方法で対消滅させないといけない
0 0 → 𝜖, 1 1 → 𝜖, 20 → 2, 21 → 2, 2$ → $
これは標準的な文脈自由文法ではない(終端記号から終端記号への変換規則になっている)
Liveness解析
例えば、「私はz.10001110にいるオブジェクトです。」をごみと見なして良いかは、10001110が文脈自由文法+αで認識できるかという問題に帰着された
しかし、残念ながらこの問題は決定不能! (+αの部分が非常に悪さをしている)
((というか元の問題が停止問題以上に難しいのだから決定不能なのは当然))
ここで近似
文脈自由文法Gの言語L(G)を含むそれより大きい正規言語L(R)が作れる
𝑆 → 𝐷𝜎,𝐷 → 2 1𝐷2 2𝐷2を近似してみると
𝑆 → 𝐷𝜎,𝐷 → 2𝐷′ 1𝐷 2𝐷, 𝐷′ → 𝜖 | 2𝐷′
元の言語は 12 𝑛2𝑛+1|𝑛 ≥ 0 だったのが12 𝑚2𝑛+1|𝑛 ≥ 0,𝑚 ≥ 0 に広がった
GCにおける健全性と完全性
健全性:生きているのにごみにしてしまわないこと
これが満たされないのはGCとして認められない
ただし、”全く働かない”GCは健全性を満たす
完全性:死んでいるごみをすべて集められること
これを満たすGCは作れない(停止問題が解ける)
如何に完全性を近似できるかが腕の見せ所
“全く働かない”GCは完全性を全く近似できていない
この近似は安全か?
先の近似は、「生きている」判定を緩めた(実際には死んでいるものにも「生きている」と言ってしまう可能性がある)方向の近似
つまり、完全性は失われるものの、健全性を失っていない
簡単な例
aが生きているかを考える aは、b.2に相当する bは、c.0 に相当する cは、w.1*に相当する(簡単化した) よって、aはw.1* 0 2に相当する
で、速いの?
前処理に時間がかかる
これは、NFAをDFAに変換するときに最悪指数時間かかることによるらしい
前処理は一回しかしないため問題ないと主張している
メモリを回収する性能は?
メモリを回収する性能は?
Reachability-based GC(RGC; これも筆者らの作ったプログラム)を下回ることはない(当然)
ただし、DFAをおいておくスペースが余分にかかっているのを除けばの話
GCにかかる時間は、RGCに勝っているものもあるし、負けているものもある(負けの方が多い)
特定の問題において、RGCを圧倒する回収性能を誇る
実行中のメモリ使用量の様子
関連研究
コンパイル時の静的解析技術、エイリアスの問題が常に付きまとう
線形型(何回その変数が使われるかまで含めた”型”)を使って生き死にを判定する技術、ただしプログラマが注釈をつける必要あり
末尾再帰等のそもそもクロージャーを作らない技術、ただしそれでも到達可能だが死んでいるクロージャーは必然的にできる