書いた人: ささだこういち
筆者は YARV: Yet Another RubyVM という、 Ruby プログラムを高速に実行するためのソフトウェアを開発しています。そこで、本連載では、Ruby や YARV のアーキテクチャを中心に、言語処理系開発について書いていくつもりです。
そもそも言語処理系、それも Ruby の処理系である YARV なんかに興味を持つ人はマニアしか居ないような気がするのですが、まぁいいや、そんな感じで YARV に関係したりしなかったりすることについて、重箱の隅をつついてみようかな、というのがこの連載です。書いてみて、やっぱりマニアしか読まないよなぁ、と再認識 しています。というわけで、YARV Maniacs です。読む人がマニア。
YARV は次期 Ruby 処理系 Rite の実装として取り上げてもらいたいなぁと思っていまして、その辺のために少しでも知名度をあげておこうというのが本連載の目的です。
何か、本連載でとくに取り上げて欲しい話などありましたら筆者へご連絡ください。
YARV の入手やインストール、実行などは YARV のホームページ を参照してください。とくに難しいことは書いてありません。とくに YARV アーキテクチャ に簡単なアーキテクチャを日本語でまとめてあります。これ読めばわかるよね。Maniacs だから、とおり一遍の紹介としては、まぁこんなもんで。
YARV 開発については IPA 2004年度未踏ソフトウェア創造事業未踏ユースによる支援(2004年度未踏ソフトウェア創造事業(未踏ユース)採択概要 - Rubyプログラムを高速に実行するための処理系の開発)を受けて行なわれました。
よく聞かれるんですが、好きに読んでください。私は日本語っぽく(?)「やるぶ」と読みますが、別にどう読んで頂いても構いません。
もし Ruby 2.0 の処理系になりましたら YARV は Rite になるわけですし、もしそうならなかったら誰も YARV なんて覚えてないでしょうから。将来的には誰も YARV なんて名前覚えていないはずなのです。でも、変数名に残ったりして。そうして、未来の誰かが「なんだこの変数名は」と悩み、昔からやってる人が、「あぁ、それはね」などとしたり顔で教えてあげる光景が目に浮かぶようです。嘘ですけど。
本稿では、まず Ruby インタプリタの基本的なことを知らないとお話にならないよね、ということで、青木峰郎氏著『Rubyソースコード完全解説』を読んでください、と言って逃げよう、でもそれだけじゃあまりに手抜きだよな、ってことで、 『Rubyソースコード完全解説』の簡単な解説を行ないます。
ただただ、完全解説不完全解説という言葉を使ってみたかったということもあるんですが。
『Rubyソースコード完全解説』を不完全に解説し、Ruby のアーキテクチャを詳細に解説する代わりにしたいと思います。
『Rubyソースコード完全解説』は、青木峰郎氏が Ruby インタプリタの(主に C 言語による)ソースコードを詳細に解説した書籍です。
プログラムの内容を解説するだけでなく、どのようにそのソースコードを読んでいくか、その課程もかなり書き込まれているため、Ruby インタプリタに興味のない人にもオススメしたい一冊です。
そもそも、あるプログラムについてここまで詳細に解説している書籍自体、大変めずらしいと思います。
この書籍は、『Rubyソースコード完全解説』サポートページ で HTML 版を無料で見ることが出来ます。なんて太っ腹(売上にはどう影響したんだろう)。本不完全解説は、この HTML 版にリンクを貼る形でまとめてみます。
なお、『Rubyソースコード完全解説』は、Ruby Hacking Guide という名前で呼ばれることも多いですので、『Rubyソースコード完全解説』を以下 RHG と称します。
RHG では、ある機能についてそれがどのように実装されているか、ソースコードを含め詳細に解説していますが、Ruby のアーキテクチャの大枠を知るためだけなら全て詳細に読む必要はないかもしれません。大雑把な枠組みを読み取り(たとえば図など)、その枠組みの機能を操作するインターフェース(Ruby C API など)を抑えておけば、あとは必要なときに読み返すだけでもいいかもしれません。つまり、RHG 全部を無理して読む必要はないかも、という意味です。実際、私はパーサの部分はあんまり読んでません(読めないだけとも言う)。
ちなみに、私は RHG を読んではじめて Ruby のアーキテクチャを理解しました。 YARV の 60% くらいは RHG でできてます。
ここで述べられているのは Ruby のオブジェクトモデルです。オブジェクトがどのように表現されているか、管理されているか(ガーベージコレクションなど) について主に記述されています。これからも変わらない話だと思っていいでしょう(ただし、細かい仕様変更などはあります)。
Ruby、そして YARV を理解するには、この第 1 部の理解は必須です。必読。
Ruby の言語仕様についての解説です。
Ruby言語について、当面第一部を理解するのに必要な知識だけ簡単に解説しておく。プログラミング上のテクニックであるとか注意すべきところなんてものは全く指摘しないので、この章を読んだからと言ってRubyプログラムが書けるようになるわけではない。既にRuby言語の経験がある読者はこの章は飛ばして問題ないだろう。
と書かれていますが、普通に Ruby プログラムを書いている人は知らないような重箱の隅についてもいろいろ触れられているので、ご一読をオススメします。あなたの知らない言語仕様が書いてあるかもしれません。知らない言語仕様満載な Ruby はなんてすばらしいんでしょう。
ただ、たとえば定数検索ルールなど、Ruby 1.9 で変更される可能性があります。
Ruby がオブジェクトをどうやって表現しているかについての解説です。
VALUE 型や RBasic 型などの話やオブジェクトの表現などについて詳細に記述されています。
Fixnum クラスのインスタンスはオブジェクトをアロケートしない、などについても書いてありますね。つまり、(ある程度の)整数演算は特別に高速、省メモリ化されているということになります。しかし、浮動小数点数を表現する Float クラスのインスタンスは特別ではない(RFloat 型の構造体に格納される)ので、 Ruby プログラムでこれを利用すると遅い、ということが言えます。いや、YARV でもあんまり速くならないんだけど。この辺の最適化について苦悩する話はまた 今度。
オブジェクトを作ることはできましたが、じゃぁそれを Ruby プログラムから使うにするには、なんらかの方法で名前を付けてあげなければなりません。そこで、この章では Ruby インタプリタが利用している名前管理のための一般的な方法であるハッシュテーブルについて解説しています。
また、名前と言っているけどそもそも名前はどうやって管理しているのか、ということにも触れています(ID)。
クラスやモジュールが、いったいどうやって表現されているか解説しています。
えーと、この章はとても長いです。ソースコードはあんまり読まなくていいから、図を頑張って追うと、とりあえず雰囲気はわかるんじゃないかと思います。
まぁ、タイトルのとおりです。ガーベージコレクション(GC: Garbage Collection)の基本的な話も書いてありますので、Ruby に限らない話題で面白いです。 もちろん、GC の最新技術までは書いていませんが。とくに並列 GC の話とか。
おそらく、ここで説明している保守的 mark & sweep 方式は当分変わらないはずなので、見ておくといいと思います。Ruby C 拡張ライブラリで GC まわりで気をつけなければならないことも学ぶことができます。
あと、Ruby のオブジェクト管理についての解説もあります。GC と密接に関係しているのでこの章になるんですね。
ただ、まつもとさんがついに世代別 GC の導入に本腰を入れ始めたということで注意が必要です。このあたりの詳細も、機会があれば本連載でやってみたいですね。この辺の詳細は、もうすぐ明らかになるんじゃないかと思います。
変数の話をするうえで最大のポイントとなるのは、「変数はどこに、どうやって記憶されているか」である。つまりデータ構造だ。
ということで、この解説です。
ところで、グローバル変数アクセスごとに起動されるイベントが登録可能であることなんて、知ってましたか?
要するに $SAFE によるセキュリティモデルですが、あんまり面白くないので飛ばしましょう。気になる人は読んで、という感じ。
$SAFE によるセキュリティモデルは、いくつか欠点も指摘されていますので、自分で新しいモデルを提案するのも面白いのではないでしょうか。私は興味ないのでパス。
構文解析しにくいことで知られている Ruby ですが、じゃぁそれをどうやって構文解析器を実装してるんだ、ということにメスを入れるのが第 2 部です。無謀ですね。
YARV 的には最終的に抽象構文木になったもの(構文解析を終わらせたもの)を受け取る構造にしているので、YARV 視点から見ればここは重要ではありません。 読みたければどうぞ、という感じです。
「Ruby みたいな言語を作りたいんだ!」という人にはオススメです。読んで挫折してさっさと諦めて BNF が綺麗な言語を作りましょう。
ちなみに、筆者は、この第 2 部はほとんど理解してません。
詳細、といっても構文解析するときに大事なところ、この辺がイヤラシイよね、 という部分を並べています。知っていると(とても限られた)飲み会で話題 (「Ruby ってさー、こんなふうに書けるんだぜ。いやだよねぇ」「えー、信じ られない」)にできる話が満載、という章です。
ところで、defined? の説明はかなりしょぼいので、個人的には是非 defined? 完全解説を青木氏に書いて欲しいと思っております。たぶん、一章くらい書けそ う。
日本で一番 yacc の解説を書いていそうな青木氏による yacc の解説です。基本的なところをしっかり抑えており、長さ的にもつらくないのでぱっと学習するにはいいかと思います。
えーと、パーサの仕組みです。
えーと、状態つきスキャナの仕組みです。生きがいがあるかどうかは知りません。
えーと、構文木の作り方です。
構文木の構造(RNode)については重要です。あと、ローカル変数の解析とか。 あとで YARV のコンパイラが使いますから。
構文木にして、計算機が扱いやすい形にしたあと、では実際にどうやってそれを動かすのか、という解説が第 3 部です。
YARV 視点で考えると、本章の意義は二極化されます。
まぁ、次回以降の連載は、ここで解説している内容をどうやって改造していったか、置き換えていったかということに終始していますので、見ておくと面白いかもしれません。
あと、「えー、Ruby ってこんなふうに動いてたの? うわぁ……」 「おぞましい中に美しさがある」みたいな感想を持ちたいのなら是非読むことをお勧めします。
まず、評価器という言葉の定義から始めている時点でアレです。
評価器全体の構造と、簡単な評価の方法について解説してあります。要は rb_eval() を再帰させて実行しているだけなんですが。じゃぁ、例外はどうするの、とかまで書いてあります。
コンテキストという言葉はいろんなところで使われていて、たとえばこのコンテキスト(文脈)では計算機の状態のスナップショットみたいな意味になります。
Ruby ではコンテキストを表現するための情報をいろんなところにバラバラに管理してあって、そのために 7 本、主なスタックを利用しているよ、という話です。で、20倍 Ruby は難しいらしいです。これだけ抜き出しても意味不明ですね。詳細は本章を読んで下さい。
タイトルどおり、メソッドとはどうやって実行してるんだろう、ということを解説しています。
冒頭での解説は、引数と仮引数って分けることが多いですね。
仮引数の設定とかローカル変数の扱いとかが重要です。あと、メソッドディスパッチの仕組みやらメソッドキャッシュによる最適化とか、大事です。Ruby って実行時間のほとんどがメソッドディスパッチのオーバーヘッドなので、こいつを高速化すれば、全体の実行時間も結構速くなるはず。たぶん。こいつを速くするための「インラインメソッドキャッシュ」という手法も、今後ご紹介していきます。待ちきれない人は拙著 プログラム言語Ruby におけるメソッドキャッシング手法の検討(PDFファイル) というのを見てください。
最後に説明してる super って、簡単そうだけど実は実装難しいんですよね。
この章ではRubyスタック七本のうち最後の大物、BLOCKが登場する。これが終われば評価器の内部状態についてはわかったも同然だ。
ということなので、是非理解してください。ブロックをどうやって管理するか、実行するか、とかそんな話です。
たしかに Ruby において、ブロックの実装は難しいです。これがなければ、もうちょー簡単に作れます。なんでこんなの入れたんだ!(A. あると便利だから)
eval、別名 Evil eval(ごく一部でしか言いません) とか、その辺の解説です。
eval なんて使わないからどうでもいいよね。
というか、eval があるせいでほとんど最適化ができないのが Ruby です。eval なんて嫌いだ。でも、利用者としてはよく使ってしまう。
まぁ、その他ですね。
ファイルをロードするとき、何をどうやってるかを解説しています。
Ruby におけるスレッドの設計と実装について解説しています。
Ruby はいわゆるグリーンスレッド、ユーザレベルスレッドなどという、OS に頼らないで自分だけでスレッド管理を行なう手法をとっています。それはそれでいい点もあって、たとえば管理が柔軟だったりします。だけど、一般にユーザレベルスレッドモデルで言われるような利点である「軽量な」という言葉はあてはまりません。残念でした。
なんで従来のユーザスレッドモデルは軽量か、というと OS へ頼る必要がないのでシステムコールなどの無駄なオーバヘッドが必要ないからなんですが、Ruby の場合は移植性を考慮して、スレッド切り替え時にスタックを全部コピーするという方式のため、そのオーバヘッドがシステムコールなどのオーバヘッドを軽々と追い越してしまいます。だから、遅い、遅い、といわれてしまいます。
この部分の解説(つまり、スレッドモデル)は近い将来差し替えられるかもしれません。たぶん。きっと。もしかしたら。
タイトルのとおり、「執筆当時の」Ruby の未来について記述されています。RHG の出版が 2002年12月ですので、もう2年半も前なんですね。3年弱でこの展望がどうなったのか、ニヤニヤしながら見るのも面白いです。
いかがでしたでしょうか。
『Rubyソースコード完全解説』を読むと、Ruby のアーキテクチャ、内部構造が把握できますが、本稿がその一助となれば幸いです。
ウェブ版だと読みづらいと思いますので、是非 RHG の書籍を買ってね(本が売れる → 青木さんが儲かる → 青木さんがささだに酒をおごってくれる)。ただ、こんな記事をここまで読んでる人はすでに RHG は購入済みである気がしますが。 しかし、RHG なんてマニアックな本の紹介してるってのは、ほんとにマニアックな記事だよね。タイトルに偽りなし。
次回についてはまだ考えてません。なんか書きます。命令セットの説明でもしようかな。まぁ、濃いぃ話は次回以降ってことで。
最後に、この素晴らしい『Rubyソースコード完全解説』を書いてくれた青木氏に感謝します。おかげで連載一回分稼げました。
ささだ こういち(ko1 at atdot dot net)。研究をしないで Ruby ばかりを触ってしまう、ダメ学生。