27
メモリアロケーションからみた 拡張ライブラリ大切なこと または、Ruby内部構造の基礎知識 日本Rubyの会 etc. 樽家 昌也

メモリアロケーションからみた拡張ライブラリに大切なこと

Embed Size (px)

Citation preview

メモリアロケーションからみた

拡張ライブラリに大切なことまたは、Ruby内部構造の基礎知識

日本Rubyの会 etc.

樽家 昌也

自己紹介

• 樽家昌也 (Masaya TARUI)

• twitter id: @taru

• ircnet: tal_

• freenode: tarui

• 平日はふつうに?サラリーマン。

• 年に1,2度、networkと無縁な生活を求めて旅に

• 仕事で直接Rubyが対象になることは今のところない

• Rubyを使い始めてから13年目ぐらい?

これまで

1. Rubyを使い始める

2. よくわからない不具合はRubyレベルのWorkaroundで適当にごまかす

3. メモリリークとかSEGVに泣かされる

4. 仕方がなく、Cコードに手を入れる

5. Rubyの独特な構成が面白くてCコードに手を入れる ←Now!!

今日の対象者・・・

『NKT77さん「Hadoop with Ruby-僕がPythonを選んだ理由」』

『初心者からコミッターまで、更にPythonistaやらC#erなど幅広い言語

経験を元に、日頃のふとした疑問や事例を紹介できたらとおもいます。』

Σ(゚д゚;)

どうやらawayらしい。。。

.rbなのに、おかしくね?

(http://kawasakirb.doorkeeper.jpから引用)

なに話そう…

Rubyがよくわからない人にもわかるプレゼン=C言語コードについてのプレゼン。

これしかない

メモリアロケーションからみた

拡張ライブラリに大切なことアジェンダ

• 拡張ライブラリ書き方復習

• Ruby のGC

• 拡張ライブラリ by C++ 準備不足によりキャンセルホントウニスイマセン

• おまけ:最近のGC事情

拡張ライブラリ

• RubyにC言語など他言語で書かれたプログラムをリンクさせて動かす仕組み

• Rubyは拡張ライブラリが書きやすい言語と言われている [誰]

•プロトタイピングをPure Rubyで行ってその後不満があれば拡張ライブラリを書けばいいと思います!

はじめての拡張ライブラリ

1. Cコードを用意する

2. ruby –r mkmf –e ”create_makefile(’sample’)”

3. make

4. ruby –r ./sample –e”p JUST_CLASS”

5. 出来上がり

サンプルライブラリAYB

• 全ての基地を頂くライブラリ

• AYBのオブジェクトのbelong_toメ

ソッドに文字列を渡すと、その中のbaseをXXXXと塗りつぶして最後に”All your base are belong to us”とのたまってくれます

• C拡張ライブラリなのに中でsplitとかconcatとか呼んでますが今回は気にしないでください・・・

• “base”で分割してforループで”XXXX”つけて最後だけ特別なメッセージを付ける

実行してみる

を実行

めでたく全部の基地をいただけました。

もっと基地がほしい

error!これはrubyのbugですか?いいえ。拡張ライブラリのです。

悪いところ

RubyのGCってmark&sweepだから保守的でmark漏れな

んて起こらないんじゃないの!?

大体この辺が悪くて、利用中のデータがGarbage Correct されてしまっている

Arrayの中身を走査している最中にArrayが消されてお亡くなりに。

※こんな恣意的なコードになったのはなかなかチェックから漏れなかったから・・・

ここで、あまり世間で聞かない話をひとつ

• RubyのGCはRubyオブジェクトの先頭に対してしか調査をしない!

• ArrayとかStringの大きな配列はGCで管理されていない領域に確保される。

(多分、もう当たり前すぎてだれも言及しない…のか?)

RubyObject

ただ単にmallocで

確保された領域

保護対象ここだけ

RARRAY_PTR

どうしてmark漏れが発生し、Rubyが落ちるのか

RubyObject

ただ単にmallocで

確保された領域

保護対象ここだけ

RARRAY_PTR

1. RARRAY_PTRで配列の先頭アドレスをもらう

2. もうこちらのアドレスは使わないので、レジスタorスタックは再利用可能!

4. このオブジェクトは使われてないと判断。オブジェクトに関係するものをRARRAY_PTR先も含めて全部回収。

3. GC発生

5. RARRAY_PTR先の内容が壊れる

Why?

RubyObject

normal

• 単純に言えば効率のため

RubyObject

RubyObject

RubyObject

normal normal

normal

normal

normal

小さい固定長

heaps_slot

名前すらない?malloced memory?

こちらの領域のデータが何を意味するかはRubyObjectが管理

こちらのアドレスに関係するデータだけを見ればいい固定長なので簡単に枝切可能

対処方法

RubyObject

ただ単にmallocで

確保された領域

保護対象ここだけ

RARRAY_PTR

1. RARRAY_PTRで配列の先頭アドレスをもらう

2. もうこちらのアドレスは使わないので、レジスタorスタックは再利用可能!

4. このオブジェクトは使われてないと判断。オブジェクトに関係するものをRARRAY_PTR先も含めて全部回収。

3. GC発生

5. RARRAY_PTR先の内容が壊れる

ちょっと待った!!

使っているよマークの挿入

RB_GC_GUARDを挿入することにより、そのオブジェクトを利用する事をコンパイラに通知

無事に基地をいただけた(忘れかけてたけど)

RB_GC_GUARD() とは

•そこに広がっているのは黒魔術。余人は決して手を出してはいけない。。。

• 正直、何をしているのかよくわからない

• コンパイラに値をスタックに残しておくように出せる指示はない

• いつコンパイラの最適化によって無効になるかわからない過酷な世界

気をつけるべき点

• RubyのAPIにはいくつかchar *を引数にとるものがありますが…(rb_str_newやrb_reg_newなど)

• ふつうにあります

• ただ、これに関してはRubyのAPI側で何とかすべきじゃないかと思わなくもない。 malloced memoryに対して一部保護を入れるなど

GC!Object Aの中身

Object Aの回収

Ruby Core

Object Aの中身を使おうとする

死亡!

所で500000回廻さないと出てこないBugとか

やってられないGC.stress = true

1回で十分に

まとめ• 拡張ライブラリは簡単にかけるよ

• たまには気をつけておかないと、GCで痛い目をみるよ

• でも、たまにだけだよ

• RubyのGCは完全に保守的なわけではなくて、Ruby Objectが入ってるHeapsしかみないよ

• RB_GC_GUARDは今の所、コンパイラの最適化とのおにごっこ

• 拡張ライブラリを書いたら一度はGC.stress=1で走らせておこう

• mallocで確保しているだけの領域にもなんらかの手当てがほしいなー

• (最近もRuby本体で今回の例と同じバグがありました…)

Rubyの書きやすさ+C言語のスピードの世界へようこそ!

おまけ: 最近のGC事情

•がんがん手が入ってます

•Bitmap marking すばらしい

•RGenGC すばらしい

hashベンチマーク(ソースは後)

mark user system total real

193p374 22.004 30.46 0.54 31 31.08143

200p195 17.09916 25.79 0.3 26.09 26.1964

r41094 14.4588 28.12 1.34 29.46 29.62575

r41095 12.13761 25.9 1.29 27.19 27.27527

r41097 7.250059 18.98 1.05 20.03 20.10459

r41128 6.633022 18.35 1.12 19.47 19.59778

r41325 1.272689 10.79 0.64 11.43 11.52074

r41634 0.870261 9.07 0.37 9.44 9.456588

0

5

10

15

20

25

30

35

193p374 200p195 r41094 r41095 r41097 r41128 r41325 r41634

mark

user

system

total

real一部

回数 r41634 Python 2.7.4

1000000 2.809293 1.559357

2000000 5.932282 3.421049

3000000 8.812863 5.801263

Pythonと適当に比較してみた(ソースは後)

Oprofile 取ってみたところ、支配的なのはst_lookupとsiphashが半々ぐらい

ご静聴ありがとうございました

hashベンチマーク ソース

Pythonとの比較用ソース