9月8日から10日の3日間RubyKaigi 2016が開催されます。
クリアコードは例年RubyKaigiのスポンサーをしています。去年のRubyKaigi 2015のスポンサーに引き続き、RubyKaigi 2016もスポンサーになりました。今年はブースを出します。ブースではOSS Gateとクリアコード自体のことを紹介する予定です。クリアコードが気になる人は遊びに来てください。
また、須藤と沖元がスピーカーとして話します。どちらも2日目の9月9日です。須藤は10:30からのセッションで最新のバインディングの作り方について話します。沖元は14:20からのセッションでRubyリファレンスマニュアルについて話します。
ところで、8月21日にリリースされたRubyist Magazine 0054号では東京 Ruby 会議 11 運営記録がよかったです。この記事の中に事前インタビューの目的が書いてあります。4つ目の目的として「発表者に事前インタビューすることで、発表の内容を事前に考えて貰う」が挙がっています。RubyKaigi 2016では事前インタビューはありませんが、ここに発表内容を事前にまとめることで同じ目的を達成できるはずです。ということで、須藤の発表内容を紹介します。
2016年のバインディングの作り方
バインディングとはRuby以外の言語(主にC言語とC++言語)で実装された機能をRubyで使うためのライブラリーです。バインディングを使うと、Rubyでその機能を実装しなくてもその機能を使えるようになるため、短時間で高品質の機能が手に入(ることがあ)ります。また、C言語での実装はRubyでの実装よりも10倍100倍速いことがざらにあるので、性能面でも有利です。画像処理・動画処理・音声処理・全文検索・機械学習・統計処理・暗号処理などは実装コスト・性能の両面からバインディングを利用して既存実装を活用するのが向いている分野です。
というような背景があり、RubyをWebだけでなく様々な分野で活用するためにはバインディングは重要です。そんなバインディングの作り方を紹介するのが須藤の話です。
以下、説明が続きますが、文章だけで図がないため関係がわかりにくいかもしれません。RubyKaigi 2016までには図も用意する予定です。
拡張ライブラリーとしてバインディングを作る方法
バインディングを作るために使える仕組みが拡張ライブラリーです。拡張ライブラリーはC言語で実装された機能をRubyに組み込む仕組みです。C言語で既存の機能をラップする拡張ライブラリーを作ることでバインディングになります。
拡張ライブラリーの難点の1つに「インストールするためにはCコンパイラーが必要なこと」があります。拡張ライブラリーを使うためにはコンパイルする必要があるため、Cコンパイラーがないと拡張ライブラリーを使えません。GNU/LinuxやBSD系などCコンパイラーを用意しやすいOSもあればそうでないOSもあります。たとえば、WindowsではCコンパイラーを用意することの敷居が高く、インストールが難しいです。
この問題を解決する方法としてfat gemというやり方があります。これはgemの中にビルド済みバイナリーを入れるというやり方です。こうすることにより、ユーザーの環境にCコンパイラーがなくても拡張ライブラリーを使えるようになります。fat gemはWindowsユーザー向けだけに用意すればよいです。Windows以外のよく使われている環境ではCコンパイラーを用意することは比較的簡単だからです。
拡張ライブラリーの難点はもう1つあります。バインディング対象の機能数に応じて実装コストがあがる点です。Rubyが拡張ライブラリー用に提供しているAPIは比較的使いやすいため、拡張ライブラリーとしてバインディングを作ることは技術的にそれほど難しくありません。ただし、対象機能を1つずつラップするため、対象機能が多いとその分手間がかかるのです。
この難点を解決する方法としてバインディングを生成する方法があります。拡張ライブラリーによるバインディングの実装にはある程度パターンがあるので生成することができるのです。SWIGというツールはそれを実現するためのツールです。
SWIGを使って拡張ライブラリー用のコードを生成してバインディングを作る方法
SWIGを使うと(多くの場合は)少ない記述でバインディング用の拡張ライブラリーのコードを生成できます。これで、少ない手間でたくさんの機能のバインディングを作ることができます。ただ、SWIGを使った場合でもいくつか難点があります。
SWIGも拡張ライブラリーを使ったバインディングなのでインストールが難しい問題があります。この問題をfat gemというやり方で解決できるのは前述のとおりです。しかし、SWIGを使った場合は別の問題もあります。インストール時にCコンパイラーとSWIGが必要になる点です。通常、SWIGはシステムにインストールされていないので別途インストールする必要があります。インストール時にSWIGを必要なくする方法があります。SWIGで生成した拡張ライブラリーのコードをgemに含める方法です。こうすることでインストール時にSWIGは必要なくなります。ただし、gemに含めたときに生成した機能しか含まれません。gemに含めたときの対象ライブラリーはバージョン1で、ユーザーがインストールしようとしたときはバージョン2になっていた、という場合、バージョン2で追加された機能は使えない、ということです。
性能面でも幾分オーバーヘッドがあります。手書きでシンプルに実装したバインディングとSWIGで自動生成したバインディングではシンプルに実装したバインディングの方が高速です。バインディングのメソッドを大量に呼び出す(何万回とか)場合は影響があるでしょう。
これまでのバインディングの作成方法ではインストールの難しさが難点としてでていました。これはC言語を使っていることが原因です。つまり、C言語を使わずにバインディングを作れればインストールが簡単になります。その方法はFFI(Foreign Function Interface)を使う方法です。なお、最近のRubyで使えるFFIの実装はすべてlibffiというライブラリーを使っています。
FFIを使ってバインディングを作る方法
FFIを使うとRubyでバインディングを書くことができます。拡張ライブラリーでやっていたことをRubyで書けるようになっただけで、1つずつ機能をラップしていかなければいけない点は変わりませんが、それをCではなくRubyで書けるようになることで書きやすくなっています。
バインディングにC言語を使わなくなったことによりバインディングのインストールは確かに簡単になるのですが、バインディングが動くためにはラップ対象のライブラリーは依然として必要です。そのため、Windowsユーザーがインストールしにくいという点はそれほど解決されていません。
また、バインディングを書く言語がCからRubyになっているので手間は減ってはいますが、対象の機能がたくさんあると結局手間なのは変わりません。
なお、FFIを使うとSWIGを使ったときよりも性能が落ちます。
SWIGが拡張ライブラリーのコードを生成するように、対象ライブラリーの情報を元にFFIを使ったバインディングを生成する方法があります。それがGObject Introspectionというライブラリーを使う方法です。
GObject Introspectionを使ってバインディングを作る方法
GObject Introspectionを使うと実行時に対象ライブラリーの情報を使ってバインディングを作成できます。古いバージョンと新しいバージョンを同時にサポートするというSWIGではインストールの難易度が上がるために難しかったことを容易に実現できます。
対象ライブラリーをgemに含めなければインストールが難しいという点はFFIを使ったバインディングと同じです。ただし、対象ライブラリーもgemに含める実装がRuby-GNOME2プロジェクト内にあるため、これを活用することで対象ライブラリーも含んだfat gemを提供できます。
GObject IntrospectionはRubyだけで使えるライブラリーではなく、PythonやJavaScriptなど他の言語でも使えるライブラリーです。これは、対象ライブラリーがGObject Introspectionをサポートしていればいろんな言語のバインディングがすぐに用意できるということです。これまで、バインディングは各言語ごとにそれぞれがんばって作成するものでしたが、GObject Introspectionベースにすることにより他の言語のユーザーと協力してバインディングを作成することができます。
以上のように、バインディング作成コスト(自動生成ベース+他言語ユーザーと協力でコスト削減)とインストールの容易さという観点からGObject Introspectionを使ったバインディングの作り方が魅力的です。しかし、難点もあります。それは、(現時点ではまだ)遅いこととGObject Introspection対応ライブラリーは限られているという点です。
性能に関してはRubyKaigi 2016までに改善する予定です。
というような話をする予定です。
まとめ
クリアコードは9月8日から10日にかけて開催されるRubyKaigi 2016にスピーカー・スポンサーとして参加します。この機会に多くの人と交流できることを期待しています。それでは、RubyKaigi 2016で会いましょう。