RubyMotion開発者インタビュー記事 - naoyaのはてなダイアリー
を読んで元記事を読んでみた。
元記事
wktkしたのでたくさん質問しちゃったそうで、記事を分割しています。
前半
RubySource | Getting To Know RubyMotion With Laurent SansonettiRubySource
前半は、#inspect 2013 - RubyMotion Conferenceについてや、
RubyMotionをどうして始めたかや、RubyMotionのランタイムの概要だったり
他のRubyとの違いだったりについて話しています。
冒頭にrubyコミュニティのハートをガッチリキャッチみたいな前フリがあるけど
海の向こうでは流行ってんのかなぁ。この辺がいまいちよくわかんないなぁ。
Why I loved building Basecamp for iPhone in RubyMotion by Nick of 37signals
この記事読んで、個人的には結構期待はしているんだけど。
話の内容も面白くてAppleの経営層がMacRubyに興味が無いのが明らかになったり、個人的な状況も重なって
RubyMotion始めたとか、コンパイルのためにrequireが使えないようなっているとか
eval使用不可だけどinstance_eval、class_evalは可とか
コンパイラ・ランタイムがopen sourceじゃない理由とか
MacRubyの経験からInvestmentなしでやっていくのが目標とか。
MRIとの違い
Require isn’t supported because of the build system, Eval of a string doesn’t work, Proc#binding has been removed, and Overriding of operators on Fixnum and Float is disabled.
あと、カンファレンス楽しそう。チョコレート工場、ビール工場見学。
ランピックの工場とかかなんかなー、やっぱり修道院ビールなんかなー。
これ行ってみたいわ。
HipByte/RubyMotionSamples · GitHub
sampleの1つめがBeer(修道院ビールの紹介)アプリなんですよね。
後半
RubySource | Laurent Sansonetti on RubyMotion InternalsRubySource
後半は、内部的な動作について。
どうやってRubyをコンパイルするためにLLVM frameworkを使っているか、MacRubyやRubiniusとどう違うのかについて。
Proc#binding のくだりとか、算術演算子のoverrideを未サポートしているとかは面白かったです。
LLVMのあたりはさっぱりなので
きつねさんでもわかるLLVM - 達人出版会
を読むときなのかもしれません。
参考
前半参考になりました。元記事読んで、「?」と思ったらこちらで日本語確認すると
わかりやすかったです。
Pat Shaughnessy「rubymotionクリエータに一問一答(前編)」 - 以下斜め読んだ内容
後半メモ
色々間違ってるとは思うが、勢いで書いてみた。
RubyMotion and LLVM
Q:あなたのサイトのどこかでrubymotionのコンパイラのバックグラウンドにLLVMを使っているというのを目にしたんだけど、
どうやってるのかなと思って。LLVMのIRの命令セットにRubyのコードをコンパイルするの?
A:そうです。ASTに解析しようとしています。
そこでruby1.9のprserを使っています。
Q:MRIからparse.yファイルを持ってきた?
A:yes.
ruby1.9のparserを使ってRuby文法用のASTノードを取得しています。
それから、LLVM言語で同等のものを作っています。
LLVMでは以下の2つの事柄が言えます。
・1つ目はプロセッサに依存しない抽象的なアセンブリ言語であること。
限定された命令セットを持っているという理由から、クロスプラットフォームであると言えます。
自分で書けるしAPIを使って生成できる。(LLVMが提供する命令を生成するためのAPI)
・2つ目は言語のためのコンパイラであること。
IRまたは"internal representation"と呼ばれるLLVMの命令を変換するために使用できる一連のモジュールであるということ。
Q:仮想マシンがありませんが?LLVMの中でVMが無い?LLVMとしては紛らわしいですよね?
A:そうですね。実際にLLVMの中にVMはありません。
時々JVMだと思われる。「RubymotionをJVMからLLVMへ移植する必要がある」とね。
だけど、LLVMは単なるコンパイラ。ランタイムはないんんです。
次の2つの方法でLLVMを使用できます。
1つ目は静的なahead-of-timeコンパイラとして。
それをLLVM IRに渡す。そしてbitcodeから低レベルのtoolsをもっている。
特定プロセッサのためにassembly instructionsへっコンパイルすために使うことができる。
Intel 32-bit, 64-bit, ARM, PowerPCのように。
LLVMによってたくさんのプロセッサがサポートされています。
Q:iOSデバイスも含まれる?
A:うん。
これがLLVMを使う最も一般的なやり方だと思うよ。
Clang projectがLLVMを使っている方法。
ClangはApple製の新しいC-levelコンパイラなんだ。
これはgcc置き換えることになっていて、RubyMotionがそうであるようにLLVMを使っている。
2つ目はLLVMはJIT(Jsut In Time コンパイラ)として使えるんだ。
これは私の知る限りRubinuisがやっているやり方であり、MacRubyがやっているやり方なんだ。
Q:どう違うの?
A:唯一の違いは、LLVMは実行時にコンパイル処理を行うためのC++APIを持っていること。
IR命令を作成し特定のAPIを呼び出しマシンコードへのポインタを得るというのを
自分のプログラム内で呼び出せるんだ。
Q:では、同じコンパイラだけど、別のタイミングでやってる?
A:その通り。実行時にそれをやっている。MacRubyだとこれがdefault。
"macruby foo.rb"とやると、parseされて実行されて、全ファイルのjus-in-timeコンパイルが実行されてランタイムで実行される。
Rubiniusでも同じことをやってると思うよ。Rubiniusプロジェクトをfollowしてないから
間違ってるかもしれないけど。
そうやって、LLVMを使用してると思うよ。
MacRubyではRubiniusと同時期だと思うけど、非常に早い段階でLLVMを使用していました。
Rubiniusはprototypeを持っていたと思う。ある時点で、これは非常に遅かった。
MacRubyでは少し後でLLVMを試そうとしていて、うまくいったんだ。
それからEvan Phonenixは私に実際にどうにかして動作させるようにと言ったんだ。
そのときはまだLLVMはバグが沢山あって不安定なものだった。
Q:これが2007か2008年のこと?
A:これは2009年のこと。
そのときLLVMの開発者は、LLVMは素晴らしい jsut-in-timeコンパイラだと言ったんだ。
なぜならAppleはOpenGL stackでそれを使っていたからだと思う。
プログラミング言語はJITとして使われるべきなんだよ。
それはMacRubyやRubiniusでやったことなんだ。
それから、Googleからそれを使ったUnladen Swallowと呼ばれるパイソン実装のプロジェクトがありました。
数年後にはLLVMがjsut-in-timeコンパイラではなかったことが明らかになった。
とても遅くてbit unstableだった。また、適切に例外処理を行うのがとても面倒だった。
と同時に、LLVMはいまだ静的コンパイル、ahead-of-timeコンパイルを行う素晴らしいプラットフォームだった。
これがRubyMotionで使った理由なんだ。
Q:すごいことをやったと思います。
Rubyみたいな動的言語を対象に機械語命令にコンパイルするってのは印象的です。
RubyMotionがシンプルなRubyメソッドをLLVM IR命令及びそれ以降のアセンブリ言語に変換する方法を見せていただけないでしょうか?
A:もちろん、いいですよ。基本的なhellor.rbで始めた場合
def hello(what) puts "Hello #{what}!" end
コンパイラは次のLLVM bitcodeを生成します。(LLVM IR言語のための命令):
ここで面白いのが補完文字列(rb_str_new* calls) や
#puts message 送信(vm_dispatch_call)の組み立てです。
LLVM IRからコンパイラはassemblyを生成します。これはi386バージョンです(simulator用)。
Optimizing local variables and basic arithmetic
Q:(さきほど)、あなたはRubyのProc#bindingメソッドが嫌いとおっしゃいました。どうしてでしょうか?
A:Ruby実装してる人みんなが嫌いなんじゃないかと思ってるよ。
最高の引数はおそらくCharles Nutterによって作られた。
このメソッドはevilだ。それが変数を最適化することができないから。
例を示すよ。
def foo x = 1 y = 2 bar {} x + y end
ここでは、そのメソッドの戻り値を知ることができない。
通常3が返る。だけど、もしbarメソッドが以下のようなら
def bar(&b) eval('x = 42', b.binding) end
#44になっちゃいます。
Q:まさか、bindingのcontextで"x=42"を評価しちゃうの?
A:そ。なぜならbarメソッドは、procのbindingで評価されるから。
それは、ローカル変数の値を変える可能性がある。
だからRubyMotinoでは、Proc#bindingを使用しようとすると例外を発生させて
サポート外だよと言うのさ。
そのおかげでローカル変数を最適化することができるんだ。
Q:なるほど。これらの方法で言語を変更しているのが
RubyMotionをJRubyやRubiniusや他のrubyは異なるようにしているというのが面白いです。
JRubyとRubiniusはRubySpecの仕様を使ってMRIと同一の言語にしようとしています。
A:これはMacRubyでも同じでした。Proc#bindingをサポートしていました。
あなたが言うように、私はRubyMotinoで異なるアプローチを試みている。
まぁ、これらのメソッドは実際には使われていません。
私の知っている限りRailsでは使用されていません。RailsはRubyの機能の最も大きなユースケースです。
ほとんど全てのものを使っています、しかしProc#bindingは使っていない。
また、Proc#bindingはセキュリティの問題もありますしね。
ローカル変数を最適化させるというパフォーマンスの問題から
RubyMotionから削除することにしました。
RubyMotionではFixnumクラス上の演算子の最適も同時にできなくしています。
オープンクラスでFixnumのplusを最適することはできません。
Q:nativeコードを追加することがしたかったからでしょうか?
A:その通り。算術演算子をできるだけ速くコンパイルするためです。
Fixnumメソッドが再定義されているかどうかをチェックする余裕がありません。
MacRubyではこれら全てをサポートしていますが、RubyMotionでは確実に遅くなるいくつかの機能を削除しています。
Q:とてもいいトレードオフのようですね。
あなたの技術を簡略化し、target applicationをはるかに高速にし、
ほとんどの人が使用していない少しのマイナーな機能を削除しただけです。
A:唯一の不満は"require"についてです。
"require"のような何かを持つべきだと言われます。
ですので、この点で実際に何かをするかもしれません。
How RubyMotion debugging works
Q: DWARFって何?
A: DWARFはC言語レベルのデバッグフォーマットです。
DWARFファイルはバイナリ内の全てのアドレスへのannotationを含んでいます。
例えば、(C++クラスを含んでいる領域で特定の型のローカル変数を保存する)これは機能です。
(このへんよくわかんない)
これはバイナリと一緒に存在するデバッグフォーマットです。
GDBやプロファイラなどのデバッガはバイナリについての詳細を知るために
このフォーマットをロードすることができます。
Q:だから全てのバイナリにその情報を含めているのでしょうか?もしくは特定の場合だけでしょうか?
A:開発時のみです。DWARFファイルは実際にはバイナリの一部ではありません。
実際には別のファイルです。
だから、RubyMotionアプリケーションのGDBへ接続する際に、実際にファイルとbacktraceフレームのための行情報を参照することができます。本当に素晴らしいことに、次の行にjumpするには"next"を使うことができます。
これはGDBをの使い方を知っている人にとって最適です。
しかし実際には多くのRuby開発者はGDBで苦労している。
私たちの計画の一つは、実際にこの上に高レベルのRubyデバッガを書くことです。
Q:RubyMotinoにおけるデバッグの将来は?
A:低レベルなツールであるGDB, MallocDebug, GuardMalloc, sampleに精通する必要があるため現状アプリケーションをprofileするのが困難です。
これは簡単ではありません。だから私たちはその上にある種のabstractionを作りたい。