Hatena::ブログ(Diary)

このブログは証明できない。

2008-10-23

[]Rubyはなぜ遅いのか?Ruby 1.9は速いのか?

私はWebアプリならRuby on Rails、ちょっとしたCGIならERBで作っています。RubyでWebをやる問題点としては、パフォーマンスの問題とデプロイの問題が挙げられるでしょう。Railsに関して言えば、2つの問題はPassengerによって改善されたと思っています。

と言ってもパフォーマンスについては、CGIApacheモジュールになったというだけで、プログラムの実行速度が上がるわけではありません。CGIが遅いのは外部プログラムの呼び出しが遅いためで、プログラムの実行速度とは関係がありません。

ちなみに、CGIの外部プログラム呼び出しが遅いのは、子プロセスを生成する時に親プロセスのメモリやらハンドルやらをすべてコピーするためらしいです。


Rubyは遅いのか?

ここからが本題、Rubyプログラム自体のパフォーマンスについて。まずは、Rubyは本当に遅いのか?

 性能面では「Rubyは遅い」という批判が根強い。安定版の1.8系でPythonと比較すると、ベンチマークのほとんどの項目で大幅な差をつけられてしまう。これに対するRubyコミュニティやまつもと氏の典型的な反論は、「PCより開発者の時間(生産性)のほうが重要」、「WebアプリケーションではIO性能がボトルネックになっていてCPU はあまり使わない」、「ハードウェアの性能は年々上がっている」、「スケールアウトすればいい」というもので、「実は反論のどれもRubyが速いとは言ってない。遅いのは事実」(前田氏)という。

私が作るWebアプリくらいではRubyの遅さは気になりません。ですから、私は生産性の高さを重視してRubyを使っています。ただ、実行環境(レンタルサーバーとか)の問題があるので、PHPに戻ろうかとも検討中。。。


Rubyはなぜ遅いのか?

Rubyはなぜ遅いのでしょうか?


変数に静的型がない

コンパイル時に型が決まらないため、最適化が効きづらい。これは、すべての動的言語に当てはまることなので、スルー。CGIもCで書けという話です。Schemeコンパイラ型なら速いのかな?SchemeでWebアプリについては、「ハッカーと画家 コンピュータ時代の創造者たち」を参照。


すべてメソッド

他の言語では、トップレベルの関数(メソッド)にはレシーバーがありません。Rubyでトップレベルで関数を定義すると、暗黙的にmainオブジェクトのメソッドとして定義されます。Rubyではすべてがメソッドなので、メソッド探索のコストがかかるというわけです。

書くまでもないサンプルコード。

def puts_self
  puts self
end

puts_self #=> main

mainですね。


オープンクラス

Rubyオープンクラスを採用しており、標準で提供されるメソッドも書き換え可能です。例えば、+演算子(と見せかけたメソド)をオーバーライドされている可能性があるので、「1 + 1」をコンパイル時に計算できません。

演算子(と見せかけたメソッド)のオーバーライドの例。

class Fixnum
  alias add +

  def +(other)
    self.add(other).add(1)
  end
end

puts 1 + 1 #=> 3

実は、ここがRuby 1.9で変わるところです。


Ruby 1.9は速いのか?

Ruby 1.9では平均して基本機能の実行が5倍程度、テスト項目によっては最大20倍程度速くなるそうです。


Ruby 1.9はなぜ速いのか?

YARVの採用ですね。


仮想マシン

Ruby 1.9笹田耕一氏による「YARV」という仮想マシンVM)で動作します。

Ruby 1.8までは、内部的に抽象構文木(AST:Abstract Syntax Tree)を作って、そのツリー構造をたどりながら処理を実行しています。ASTのツリー構造をたどるのが、意外に重たい処理だったということです。

Ruby 1.9からは、ソースコードをいったんバイトコード(ワードコード?)に変換して、それを実行するようになりました。気になるのが変換のオーバーヘッドですが、起動時に100分の何秒か遅くなるという程度だそうです。

プログラムが10万行もあればコンパイルに時間がかかる可能性があるが、Rubyでそこまで大規模なものはあまりない、という前提だそうです。この平均値ではなく最頻値を最適化するというのが、Ruby 1.9のコンセプトのようですね。


固定長整数演算最適化

Rubyが遅い理由のオープンクラスのところですね。単純な整数演算であっても、演算子(メソッド)をオーバーライドされている可能性があるから、コンパイル時に計算できません。

YARVでは、該当するメソッドの呼び出しを省略してダイレクトに演算することで大幅に高速化しています。

では、演算子をオーバーライドした場合にはどうなるのでしょう?この場合は、余分な処理が発生するためパフォーマンスが落ちます。先ほど出てきたコンセプトですね。一般的なコードで効率が上がる方法を採用するというのがYARVのアプローチだそうです。


Ruby 1.9で何が速くなるのか?

Cで書かれたライブラリが速くなるわけではないので、正規表現を使ってがんがん検索するような処理は速くならないそうです。変数の代入とかループなど普通の処理が速くなるそうで、そこがボトルネックになっているようなものは速くなります。「フィボナッチ数列の計算とか(笑)」

「普通の処理」が速くなるというのはウレシイですね。アルゴリズムを効率化して対策とかが通じない部分なので。


Railsは速くなるのか?

Railsって、まだRuby 1.9には対応してなかったですよね。対応した場合、YARVのメリットを享受できるのか。

ここからは、資料はなくて個人的な考えなのですが。Railsは基本ライブラリオーバーロードや黒魔法を使いまくって成り立ってるわけです。そうなると、一般的な処理に最適化されたYARVでは、逆に遅くなってしまう気がします。

それ以前に、あれだけのコードになると、コンパイルオーバーヘッドもバカにならないんじゃないかと。これに関しては、プリコンパイルのところで。

話を戻して、YARVでは遅くなってしまう。解決策は。。。Rails専用VMとか!Java VMっていろいろチューニングできるんですよね。YARVチューニングとかないようなので、ここはもう、Rails専用VM作ってしまえばいいんじゃないかと。


プリコンパイル

プリコンパイル。つまり、コンパイル済みのバイトコードデプロイするという方法。いちおう視野には入っているようです。やればできると。ただ、「少なくともわれわれの興味ではないです。」だそうな。

Railsが速くなるなら、やる価値はあると思います。「RubyRails用言語じゃない!」と叫んだところで、RubyRailsの恩恵を受けているのは確かですし。美川憲一コロッケのモノマネに文句を言えないようなものです。

でも、Railsがやってることは、Javaや.NETで言うReflectionを多用してるようなものですよね。これだとプリコンパイルしたところで、やはり効果は限定的なのでしょうか。


さいごに

以上、去年の記事から引っ張ってきた情報です。Rubyが遅い理由を知りたくて調べました。Ruby 1.9で高速化すると言っても、互換性とか完成度の問題があって、すぐに乗り換えるものではないようです。これからの10年の間に、Rubyがどうなっているのか。と言うくらいの長い目で見守って行くことにします。並列分散処理だとかエンタープライズだとか含めて。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証