ja ja ja ja ja ja ja ja ja en ja ja ja ja ja ja ja ja ja ja ja en ja ja ja ja ja ja ja ja ja zh ja ja ja en ja ja ja ja ja ja ja ja en en ja ja ja en ja ja ja ja ja ja ja en en ja ja ja ja ja ja ja ja ja ja ja ja en ja en ja ja ja ja ja ja ja ja ja ja ja ja pl en eu ja ja ja ja ja ja ja ja ja ja en ja ja en ja ja vi ja sv eu ja en en hu ja ja en en pl ja ja jbo hbs_lat en ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja
東京Ruby会議11での発表「アプリケーションへのRubyインタープリターの組み込み」とOSS Gateワークショップ2016-05-28 #tkrk11 #oss_gate - 2016-06-01 - ククログ
Initializes the process for ruby(1). This function assumes this process is ruby(1) and it has just started. Usually programs that embeds CRuby interpreter should not call this function, and should do their own initialization.
Rubyインタプリターを組み込んだアプリケーション例として紹介したmilter managerではruby_sysinit()を呼んでいません。コマンドライン引数は自前で処理しています。
ただ、ruby_sysinit()を呼ばないというのはmilter managerがWindowsをサポートしていないからできることです。Windows用の初期化をしているrb_w32_sysinit()をチラ見するとわかりますが、この関数がやっている処理のうち、自分に必要な分はどこかを判断してそれらを自前で実装することは難しいでしょう。(Windowsに詳しい人ならそうでもないかもしれません。)
そのため、Windowsでも動くアプリケーションにRubyインタプリターを組み込む場合はruby_sysinit()を呼ぶのがよいでしょう。(コメントでは呼ぶなと書いていますが。)
質問:共有ライブラリーの方にアプリケーションのメイン関数を渡せばアプリケーションでRUBY_INIT_STACKを呼ばなくていいんじゃない?
スライドで言うと以下のページの実装についての質問です。
アプリケーションへのRubyインタープリターの組み込み
以下のようにしてembedded_init()内でapplication_main()を呼ぶようにすればいいんじゃない?という話です。
int main(void) { embedded_ruby_module = dlopen(); embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init"); embedded_ruby_init(application_main); }
void embedded_init(main_func application_main) { RUBY_INIT_STACK; /* ... */ application_main(); }
これに対する回答は「RubyインタプリターとPythonインタプリターを一緒に組み込めないのでやりたいことを実現できない」でした。一緒に組み込む時のイメージは次の通りです。
void main(void) { embedded_ruby_module = dlopen(); embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init"); embedded_ruby_init(application_main); /* ↓はアプリケーションのメイン関数の前に実行しないといけないけど、 ↑でメイン関数が実行されちゃう。 */ embedded_python_module = dlopen(); embedded_python_init = dlsym(embedded_python_module, "embedded_init"); embedded_python_init(application_main); }
それに対する別案が「他のインタプリターの初期化もやる関数を渡せば?」でした。こんなイメージです。
void main(void) { embedded_ruby_module = dlopen(); embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init"); embedded_python_module = dlopen(); embedded_python_init = dlsym(embedded_python_module, "embedded_init"); init_func init_functions[] = { embedded_python_init, application_main, NULL }; embedded_ruby_init(run_init_functions, init_functions); } void run_init_functions(init_func *init_functions) { if (init_functions[0]) { init_functions[0](); run_init_functions(init_functions + 1); } else { application_main(); } }
質疑応答はここで時間切れでした。
たしかにこれで動きそうです。
会議後にはこんなアイディアもありました。
そういえば #tkrk11 の RUBY_INIT_STACK の話だけど、アドレスを引数として渡せるようなインターフェイスになれば万事解決なんじゃないかと思いましたまる
— Kazuho Oku (@kazuho) 2016年5月30日
これはどういうことかというと、アプリケーション側ではRUBY_INIT_STACKに必要なデータを用意するだけにして、実際の呼び出しは別の共有ライブラリーの方に移す、という実装にすればいいんじゃない?ということです。
RUBY_INIT_STACKはruby_init_stack()というアドレスを引数にとるAPIを呼び出しているだけなので、このアイディアも実現できます。こんなイメージです。
void main(void) { int address; embedded_ruby_module = dlopen(); embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init"); embedded_ruby_init(&address); application_main(); }
void embedded_init(int *address) { ruby_init_stack((VALUE *)address); /* ... */ }
milter managerでも実装できます。(アプリケーション本体に手を入れられないケースではこの方法の実装は難しいでしょう。)
「アプリケーションがRUBY_INIT_STACKを呼ばないといけない問題」は複数の方法で解決できますね!このような場で話をするといろんなアイディアを聞けて便利ですね!みなさんも積極的に実装の話をしてはいかがでしょうか。
RUBY_INIT_STACK問題が解決したので、複数言語のインタプリターを組み込んだケースのことを想像してみたところ、次はforkで問題にあたりそうです。Ruby以外の言語が複数のスレッドを作っている場合が問題になりそうです。
外部ライブラリーとGC
質疑応答では触れられませんでしたが、外部のライブラリーが使用しているメモリー量をRuby(mrubyもCRubyも)のGCシステムが知らないために適切なタイミングでGCが動かない問題についても補足します。
発表では「外部からRubyのGCシステムにメモリー使用量を通知する仕組みが必要だと思う」と話しました。
CRubyのTypedDataにはメモリー使用量を返すAPIがありますが、それは使えないはずです。それを使うと、GCシステム側が情報をpullする仕組みになるからです。pullする仕組みにすると、いつpullするの?pullした情報をどうやって管理するの?あたりが大変になりそうです。
通知する仕組みだとこれらの問題を解決できそうです。
ということで、gc-triggerという拡張ライブラリーを作ってみました。このライブラリーは他の拡張ライブラリーに使用メモリー通知用のAPIを提供します。他の拡張ライブラリーがこのAPIを使ってメモリー使用量を通知すると、適切なタイミングでGC.startを実行します。
本来はCで提供しているAPIを呼んだほうがよいですが、テスト用・説明用にRubyのAPIも用意したので、そっちを使って使い方を説明します。
ここでは、例としてrcairoを使います。rcairoは画像を扱うからです。画像を扱う拡張ライブラリーはRuby管理外のメモリー使用量が大きめなので、この問題に遭遇しやすいケースなのです。
次のコードは1000個画像オブジェクトを生成します。どの画像オブジェクトも参照されていないのですぐにGCで回収できます。
require "cairo" width = 6000 height = 6000 1000.times do |i| Cairo::ImageSurface.new(:argb32, width, height) end
gc-triggerでメモリー使用量の増減を通知するコードは次のようになります。
require "gc-trigger" require "cairo" def image_surface_finalizer(size) lambda do |id| GCTrigger.update_memory_usage(-size) end end def create_image_surface(width, height) size = 4 * width * height surface = Cairo::ImageSurface.new(:argb32, width, height) GCTrigger.update_memory_usage(size) ObjectSpace.define_finalizer(surface, image_surface_finalizer(size)) surface end width = 6000 height = 6000 1000.times do |i| create_image_surface(width, height) end
画像オブジェクトのサイズは、単純化して「1画素のサイズ(4バイト)×縦×横」で計算しています。
それぞれの場合でのメモリー使用量をグラフにすると次のようになります。従来の方はメモリー使用量が安定しませんが、gc-triggerを使った方はメモリー使用量が一定になります。
発表で話したアイディアを動くようにしてみました。これで通じるでしょうか。
OSS Gateワークショップ2016-05-28
発表でも紹介しましたが、OSS Gateワークショップ2016-05-28を東京Ruby会議11と同時開催しました。東京Ruby会議11でポスターを展示していたので、そこではじめて知ったという方も多かったのではないでしょうか。
OSS GateおよびOSS Gateワークショップを紹介していて痛感したことは「口頭での説明でないとちゃんと伝えられない」ということでした。たとえば、「私はすごくないのでメンターできないです。。。」に対して口頭では伝わるように回答できますが、資料では伝わらないです。これだとスケールしないので口頭での説明でなくても伝わる資料の作成が必要です。
ポスターを展示しての紹介は想像以上に効果があり、OSS Gateワークショップ2016-07-30の登録者が10人以上増えました。東京Ruby会議11実行委員会に協力してもらえて本当によかったです。ありがとうございました。
ちなみに、想像以上に登録者が増えたおかげでメンターが足りなそうです。OSS開発に参加したことがある人(技術力不問)でOSSの開発に参加する人が増えるといいなぁと思う人は「メンター」としてOSS Gateワークショップ2016-07-30に登録してください。すでに定員オーバーでキャンセル待ちでの登録になりますが、気にしないでください。キャンセル待ち扱いでも気にせずに当日来てください。入れます。
5月28日に開催された東京Ruby会議11での発表とOSS Gateワークショップ2016-05-28について補足しました。
東京Ruby会議11の参加者アンケートはまだ回答を受け付けているので参加した方はぜひ回答してください。
milter manager
現在クリアコードではソフトウェア開発者を募集しています! フリーソフトウェア開発での開発の仕方をベースに、成果や知識を公開し共有しながら業務に携われます。
Groongaについて開発者に質問できる「グルカイ!」ほぼ毎週火曜日12:15-12:45 Groongaチャンネルで開催中です。 Groongaについて知りたい人、ぜひご参加ください。
東京Ruby会議11でRubyの組み込み方とOSS Gateを紹介予定 #tkrk11 #oss_gate
MySQLとPostgreSQLと日本語全文検索2:初心者向けMroonga・PGroonga情報 #mypgft
Apache Arrow
milter manager
ククログの記事はCC BY-SA 4.0とGFDLのデュアルライセンスで自由に利用できます。
クリアコードはプログラミングが楽しいソフトウェア開発者を1名募集しています。
クリアコードは「クリアコードをいい感じにする人」を1名募集しています。
クリアコードはフリーソフトウェア開発で培った技術力を提供しています。特にMozilla製品(Mozilla FirefoxとMozilla Thunderbird)とRubyとGroonga(全文検索)に関連した開発を得意としています。