precise garbage collection for c
Post on 30-Jun-2015
3.414 Views
Preview:
TRANSCRIPT
Precise Garbage Collection for Cの紹介
CSNagoya 三浦英樹
Precise Garbage Collection for C
著者
Jon Rafkind, Adam Wick, John Regehr, Matthew Flatt
入手先http://www.cs.utah.edu/~regehr/papers/ismm15-rafkind.pdf
CのプログラムでもGC使いたい
• 普通、保守的 GCでしょう
• たとえば、Boehm GCとか
• でも、長い時間使ってるとメモリー使用量がすごく増大する場合があるみたいだよ
• 証拠は?
• それは、次のスライドの心だー
PLT Schemeにあった怖い話
• PLT Schemeを使用したプログラム環境DrSchemeは毎日再起動をしなければならなかった。でも、正確なGCに変えたら再起動が必要なくなった
• バイトコードとドキュメントを生成すると、正確なGCだと200MBで済むところが、保守的GCだと700MBも必要だった
• PLT Schemeで作ったmail clientを1日おきに再起動していたが、正確なGCに変えたら再起動しなくて済むようになった
正確なGCを実現するためには
• 次の2つが重要– 生きているポインタとそうでないものを分ける
– アロケーションしたものとその中身がなんなのかをしっかり関連付ける
• 簡単に言うけど難しいよーだってCだもん– コンパイラ相当のものを作って、もってる型情報とか駆使して頑張る
サポートしています
• 配列なんかの途中を指しているポインタ–効率が悪くなるから出来る限り使わない方がいいみたい
• 共用体–いまどのメンバーを使っているか記録する–代入したメンバーと違うメンバーを使う行儀の悪いことはサポートしない。
サポートしていません
• いったん、範囲外にポインタを動かしてもう一度範囲内に戻すp = malloc(1024)p -= 1024p[1025] /* access 1024 to 2047 */
• ポインタでxorをとってみたり– xor linked listとか。頭いい人っているねー
• 一旦整数にcastしてまたポインタにcast– CRubyはgive up
• ライブラリの中でアロケーションしちゃったりとか
ローカル変数
ポインタの情報を配列に入れる
→ プログラム変換を行う
// ORIGINAL
int cheeseburger(int* x) {
add_cheese(x);
return x[17];
}
// TRANSFORMED
int cheeseburger(int* x) {
void* gc_stack_frame[3];
/* chain to previous frame: */
void* last_stack_frame = GC_last_stack_frame();
gc_stack_frame[0] = last_stack_frame;
/* number of elements + shape category: */
gc_stack_frame[1] = (1 << 2) + GC_POINTER_TYPE;
/* variable address: */
gc_stack_frame[2] = &x;
/* install frame: */
GC_set_stack_frame(gc_stack_frame);
add_cheese(x);
/* restore old GC frame */
GC_set_stack_frame(last_stack_frame);
return x[17];
}
変換後プログラム拡大// TRANSFORMED
int cheeseburger(int* x) {
void* gc_stack_frame[3];
/* chain to previous frame: */
void* last_stack_frame = GC_last_stack_frame();
gc_stack_frame[0] = last_stack_frame;
/* number of elements+ shape category: */
gc_stack_frame[1] = (1 << 2) + GC_POINTER_TYPE;
/* variable address: */
gc_stack_frame[2] = &x;
/* install frame: */
GC_set_stack_frame(gc_stack_frame);
add_cheese(x);
/* restore old GC frame */
GC_set_stack_frame(last_stack_frame);
return x[17];
}
結局
• GCのシステムにどのローカル変数が生きているポインタを持っているかを教える
– コンパイラが変数の型を把握していることを利用
malloc部分の変換
• mallocってこんな感じで使うよねmalloc(sizeof(foo) * 5)
• これをみると、アロケーションするタイプ(foo)と数(5)が分かるわけだ。
• 正確なGCだと何の型のデータをアロケーションしたかの情報も必要なのでこんな感じで変換
GC_malloc(sizeof(foo) * 5, gc_foo_tag)
• で、foo_tagってなんなの?–それは次のスライドの心だー
gc_foo_tagって?
• gc_tag_struct型の構造体• アローケートしたオブジェクトと型を結びつける• 内容はmarkとrepair(コンパクション) 処理を行う関数へのポインタstruct foo { int *x; int *y;} とするとmarkを行う関数 void gc_mark_struct_foo (void * x_) {
struct foo *tmp = (struct foo *) x_;GC_mark(tmp->x);GC_mark(tmp->y);
}こんな関数をコンパイル時に自動生成する。
ユーザがmark/repair関数をカスタマイズすることも可能
いろいろやってみた(PTL Scheme)
• PLT SchemeのGCをConservative GCからPrecise GCにしてみた
• 最初に説明した通り、メモリ使用量が激減
• 実行速度は– メモリアロケーションが多いベンチマークは20%くらい速くなる
– メモリアロケーションが少ないベンチマークは10~20 %遅くなる
いろいろやってみた(Linuxカーネル!)
• カーネルなんて絶対メモリーリークしちゃいかんし、GCが欲しいよね
• なんかいろいろ面倒だったみたい。続きは論文で
• tmpfsからddコマンドでファイルアクセス– GC付きのものが4割ほど遅い
• HDDからddコマンドでファイルアクセス–あまり変わらない
まとめ
• この技術すげー。今後、いろんなところで使われるようになるはず。
• GC本第2版は保守的GCの章を書き換えないといかんかもしれませんねー
• 8ccでサポート予定だから楽しみ http://github.com/rui314/8cc
ご清聴
ありがとう
ございました
top related