私が書いたにしては妙に反応のあった「Ruby なんて遅くて使えないよねって言ってみる」についてです。

釣りっぽいタイトルと整理されていない内容のおかげで誤読が出るとまずいので、念のため補足します。このエントリの趣旨は、Ruby は遅いけど、Web アプリにおいてはその遅さがシステムのトータルなパフォーマンスに与える影響はそれほど大きくは無いので、Ruby を使っても問題はないということです。ついでに言えば、私は「プロトタイプは Rails、本番はパフォーマンス/スケーラビリティを考慮して Java」とか「Rails を採用すると運用フェーズでコストが増大する」という意見も鵜呑みにはできないと考えています。本当に Ruby/Rails がボトルネックになっているのかと聞いてみたいところです。

ま、そんなことよりも、この記事に対応する記事を書いた方々を紹介します。いずれも、良いことが書いてあるので読んで見ると吉です。

■ 記事を書いてくださった方々
Ruby の作者であるまつもとさんに言及していただけるとは感激です。しかも的確なエントリに感謝です。

じゃあ、速度の問題はこれで解決かというとそんなことはない。 YARVになってもRailsの性能は改善されないからだ。要するにRailsが遅いのだとしてもそのボトルネックはインタプリタ性能にはないってこと。


Rails の性能の悪化はしないようですね。VM がからむと、eval のあたりで速度低下が起こっても不思議ではない気もしますが、処理系の性能向上と相殺されているのでしょうか。それとも eval は JIT コンパイルされないのかな。とにかく、Rails は eval だらけだった気がします。Kodougu はわりと eval を使っていないので、YARV の恩恵をダイレクトに受けられそうでわくわくしています。(ユーザーメリットは無いでしょうが、1ms でも高速になるとときめくのです。)どちらにしても、Rails のパフォーマンスのネックになっているところを調べないと。

航海日誌(2007-07-07) でも取り上げられているようです。Java 使いの方かな。2007 年度の未踏の方のようです。

JavaでGenerics,拡張for文,可変長引数,static import,anonymous classを上手く使うとびっくりするぐらい楽になる.このうまさを知るともう戻れない.


同意です。Java でも「上手くやれば」生産性(ここでいう生産性はコード数)において Ruby にそうそう負けるものではありません。そして、Java のパフォーマンスは心強いです。ただ、「上手くやれば」という条件付です。私は Java、C# で、(モデリングツールですが)コードの爆発的増量を経験しているので。今のところ Ruby では起こっていないようです。(私のスキルレベルでも生産性を発揮してくれる Ruby は大好きだ。)要は、私の Java/C# スキルの問題なんですが、Ruby よりは習熟しているはずなんですけどね。。。

やむにやまれず:Rubyは遅いから使えるのですというブログでも取り上げられていました。

逆でしょう。RubyやRailsは遅いから使えるんです。


あらら。読んでもらえなかったのかしら。結局使えるって書いたつもりだったのですが。私はもうちょっと整理したエントリを書かないとだめですね。orz それはともかく、遅いってことは、いろいろやってくれてるんだから、使えるにきまっているじゃないかってことですね。面白いエントリです。

またスケールアウト時にはマシン台数が増えて、メンテナンスのランニング費用が特に脹らむことでしょう。


ランニング費用をサーバ管理費中心にしているのが若干微妙です。ソフトウェア開発は保守の方がコストがかかるので、運用フェーズになっても Ruby/Rails のメリットはむしろ上がって行きます。一人のプログラマが一年あたりに書けるコード数が一定ならメンテナンスできるコード量も一定(※)です。そして、時代は永遠のベータ。(少なくとも Rails が対象とするアプリは運用しながら開発が続けられていきます。Kodougu みたいに。)

と、異議は唱えてみたものの、基本的には面白いエントリでオススメです。(面白いから異議を唱えるんだな、これが。)

※ こういうことを書くと、動的言語は本当にメンテナンスしやすいのかどうなのか疑問を投げかける人が出てきそうです。この辺、誰か解説して欲しいなぁ。今のところ私は保守しにくさは感じていませんが、それは規模が小さなアプリだからでしょう。でも、数千、数万人月とかになるような巨大システムの基幹に Ruby/Rails を据えるような人はいないですよね?

■ そういえばこんなツッコミはなかったなぁ・・・
例:えらそうなこと言ってる割には Kodougu 重いじゃねーか。だから Ruby はだめなんだよ。今からでも Java で書き直すか?

えーと、あの、その、Kodougu が重いのは JavaScript で書いた部分がいけていないからです。はい。サーバは結構余裕です。あぁ、JavaScript を書き直したい。時間が欲しいな。

■ 追記(2007/7/21 12:04)
こちらからさらにお返事いただきました。感激。意図が伝わらないのは、釣りっぽい書き方をした私の責任です。スンマセン。丁寧なお返事ありがとうございます。

Posted by あかさた
「Ruby なんて遅くて使えない」という意見が出ます。(昔、Java も似たようなことを言われましたっけ。)これに対して、Ruby 好きな人からは、「大抵の Web アプリではボトルネックは IO になるからアプリの言語は遅くても構わない」「CPU 時間よりも開発者の時間の方が重要」というような反論が展開されます。

Rails 厨にならないためにも、ここは Ruby に批判的な目を持って、この問題を考えてみたいと思います。

■ 前提
Ruby を採用するとなると Rails 絡みで Web アプリでしょうから、Web アプリについて考えてみます。(でも、DLR とか話に出てくるわけですから、クライアントで使う場合もそろそろ検証した方がいいと思いますけどね。)

■ Ruby は遅い
以下のサイトを見るとわかりますが、場合によっては、C 言語の数十倍から数百倍遅くなります。(Ruby 1.9/YARV が出てくれば、少しは事態は改善されるに違いない・・・と信じていますが。)

The Computer Language Benchmarks Game
http://shootout.alioth.debian.org/

本当に Web アプリでは言語の速度は問題にならないのでしょうか? Kodougu(Rails で開発している Web 上で動作するモデリングツール)で図を開く処理を計測してみました。Rails のログを見ると、サーバの処理時間のうち、40% は DB 待ちでした。一回のアクセスで、少なくとも数十の SQL が乱れ飛びます。html や javascript の生成は 40% 程度を占めています。残りは、フレームワークやコントローラで消費されています。(このログは、Rails がとっているログなので Rails の中しか計測されません。)

この手のアプリは、ネットワークがボトルネックになることが多いのですが、今回は ab(Apache Bench)を使って、並列性 1 でベンチマークをとっているので、ネットワークはそれほどボトルネックになっていないと考えています。(処理は重いので、CPU はいっぱいいっぱいでした。Kodougu がそこそこ人気が出たら、今のサーバは即パンクしますね。パンクすると困るのですが、人気が出なくても困るので・・・どうしよう。)

言語の処理速度は一概には言えないのですが、上記のベンチマークによれば、Python だと Ruby の 2 ~ 3 倍、Java だと 20 ~ 30 倍になります。たとえば、Kodougu をそのまま Java で実装すれば、パフォーマンスは今の 2.5 倍くらいにはなります。(プログラミング言語の処理速度の向上がそのままトータルなパフォーマンスには現れないところがミソです。)

もっとも、サーバを 3 台余分に準備すれば(運用コストはサーバ代、人手も合わせて年間 100 万増くらい?)、Ruby のままで同等のパフォーマンスを得られるということでもあります。最近のアプリはステートレスなので、台数はわりと簡単に増やせます。DB サーバは増やすのは難しいですが。

実際のところ、Web アプリはキャッシュを使ったりして高速化をするので、言語のパフォーマンスはアプリのトータルなパフォーマンスの中ではそれほど大きな位置を占めません。まじめに最適化してしまえば、Kodougu を Java で実装してもパフォーマンスは 20 ~ 30% 位の差しか出ないのかなと考えています。Kodougu はモデリングツールということもあって、Web アプリの中では例外的に計算的な(≒ Ruby に不利な)種類のアプリです。通常の Web アプリではもっと言語間の差が小さくなるんじゃないかなと感じています。

■ Ruby の生産性
以下のような意見があります。

日本 Ruby 会議 2007 の Dave Thomas のスピーチのログより

ある研究によれば、生産性はそれぞれのプログラマでそれぞれ違う。でも、あるプログラマに着目すれば、そのプログラマが時間あたりに書けるコードの行数は、プログラミング言語によらず決まっている、たとえば一年に50,000行なのだそうだ。行数が決まっていたら、どの言語で一番多くのことを達成できる?そう、Rubyだよね。



なるほど。そういう意味では、Ruby の生産性は高いです。通常、モデリングツールのコード行数は Java や C# で 10 ~ 100 万行程度(製品コードのみ)になります。Kodougu は 5000 行程度(製品コードのみ)です。Kodougu は Web であるがゆえに、機能的に優れた面とそうでない面とがあるので単純に比較することはできません。少なくとも生産性が 10 倍になることはありませんが、50 ~ 100% 程度の向上はあると思います。

(ただ、アプリの規模が大きくなればなるほど、Java でも Ruby でも変わらなくなる気がしますが。)

■ トータルなコストパフォーマンス
Java を使った場合と比較して、サーバ運用コスト増が開発費の 20% ~ 30% 位に収まってくれるなら、Ruby はアリだと私は考えています。保守フェーズに入ったら、サーバ運用コスト増がもっと大きくても大丈夫なんじゃないかとも考えています。

■ まよめ
さて、破滅的に遅い Ruby ですが、Web アプリで Ruby が許される本当の理由は、Web アプリの負荷の質にあります。たとえば、ゲームのリアルタイム 3DCG の処理を Ruby で書くのは自殺行為です。こうしたアプリは常に CPU に負荷をかけるアプリです。(CPU でやらせること自体が自殺行為といううわさもありますが。)Web アプリは、動的コンテンツとの割合次第ですが、キャッシュなどを仕掛ければ、たまに CPU に負荷がかかるけど、いつもは比較的 CPU が暇なアプリに分類されます。今のところ、Ruby はそういう負荷の質を持った用途で使うべきものです。

クライアントでも、Ruby が使えるかどうかの判断基準は、結局負荷の質をどうとらえるかという問題に落ち着くのかもしれません。ただ、総じていうと、サーバの CPU はいつもそこそこ忙しいですが、クライアントの CPU はいつも暇です。ゲームとかやってない限り。CPU の高速化に伴い、多くの処理が負担でなくなってきていることも事実です。Web アプリのクライアントが JavaScript でも書けることから、クライアントで Ruby が活躍できる範囲はむしろ大きいのではないかなと感じています。

そうそう、Twitter もRails を使っているそう(参考)です。リクエスト数が多そうなサービスなので、Rails を使うのはチャレンジャーだと思うのですが、意外といけているようです。凄い・・・。

Posted by あかさた
Apache のログを眺めていたら、AB(ApacheBench)のアクセスは HTTP 1.0 クライアントとして認識されているようです。このため、AB では、コンテンツ圧縮など HTTP 1.1 を前提にした処理は実行されない可能性があるため、正確には速度を計測できないかも・・・。(少なくとも、AB からのアクセスでは、mod_deflate は走っていませんでした。)

これ・・・私の勘違いですかね???

Kodougu のサーバは回線がしょぼい(10Mbps を数台で共有している)ので、結構重要な気がしているのですが・・・。

Posted by あかさた
私は Kodougu について語るとき、いつも「重い、遅い」と言っているのですが、ベンチマークをとったことはなかったので、手始めに AB(Apache に付属するベンチマークソフト)を使って速度を計測してみることにしました。

実行したコマンド
ab -n 100 -c 10 http://www.kodougu.net/rendering/diagram/1

計測したのは一番重くなると思われるモデリングツールを表示するページで、10 のコネクションで、合計 100 リクエストを送ることを試してみました。(もっとも、このベンチマークは、あくまでリクエストを送ってレスポンスを返すまでの時間を計測するものなので、たとえば、script タグで関連付けた外部 JavaScript ファイルの DL 時間などは計測できない・・・と思います。今は敢えてその辺を無視しています。)

ベンチマーク結果
Benchmarking www.kodougu.net (be patient).....done

Server Software:        Mongrel
Server Hostname:        www.kodougu.net
Server Port:            80

Document Path:          /rendering/diagram/1
Document Length:        24522 bytes

Concurrency Level:      10
Time taken for tests:   22.140625 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      2479000 bytes
HTML transferred:       2452200 bytes
Requests per second:    4.52 [#/sec] (mean)
Time per request:       2214.063 [ms] (mean)
Time per request:       221.406 [ms] (mean, across all concurrent requests)
Transfer rate:          109.30 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       15   21   8.1     15      46
Processing:   516 2145 2189.3   1390   11765
Waiting:      421 2025 2163.7   1281   11640
Total:        531 2166 2188.7   1421   11796

Percentage of the requests served within a certain time (ms)
  50%   1421
  66%   2046
  75%   2453
  80%   2671
  90%   5453
  95%   7062
  98%  10031
  99%  11796
 100%  11796 (longest request)

サーバは Mongrel となっていますが、間に Apache Proxy が挟まっています。秒間 4.52 リクエストを処理することができるようです。これは、あくまで Web にあげているバージョンで、ローカルで動作させている最新版はさらに重くなっていて、この半分くらいの性能しか出ません。まだ分析していないので、回線、Web アプリ(Rails)、DB(MySQL)のどれがボトルネックになっているのかはわかりません。

まあ、ユーザが少ないうちは、この程度の処理能力でも問題ありません。でも、100 ユーザくらいが同時に使ったら、図を表示するだけで何十秒も待たされることになり、現実的に使えるものではなくなってしまいます。現在は、サーバは一台(CPU:Core2Duo/Mem:1GB)なので、サーバ台数を増やしていくという選択肢もありますが、アプリケーションだけでも 10 倍くらいは改善しないと使い物になりませんね。

機能拡張が落ち着いたら、パフォーマンス改善をしないとなー・・・。

Posted by あかさた
dojo は複数の JavaScript ファイルを一つにまとめることができます。

dojo は、dojo.require(名前空間名); というような書き方をすると、ダイナミックに JavaScript をロードしてくれます。しかし、個別のファイルをダイナミックにロードすると時間がかかってしまいます。そこで、ビルドしてファイルを一つにすると、Web ページの読み込みを高速化できます。(また、ダイナミックロード時は JavaScript がブラウザにキャッシュされていないようです。)

Kodougu を試された方はお気づきかもしれませんが、Kodougu は起動に数秒かかります。環境によりけりですが、起動時間のうち、JavaScript の DL 時間が 0.5 秒 から 5 秒程度かかっています。時間を計ったわけではありませんが、Kodougu では dojo に関するファイルを一本化するだけで八割方削ることができました。

以下、ビルド手順です。dojo 本家のリファレンスはこちらです。

■ 前提条件
JDK 1.4.2 以降(1.5 以降推奨)
Ant 1.6.5 以降

■ 手順
1. ソースコードのダウンロード
私が調べた限りでは、dojo の HP から DL できる Ajax Edition にはビルドスクリプトが含まれていませんでした。Subversion から チェックアウトしてください。
http://svn.dojotoolkit.org/dojo/tags/release-0.4.2/
(2007/5/14 時点での最新タグ)

2. hoge.profile.js の作成
以下のような Profile を作成します。ビルド時の依存関係を記述するためのファイルです。dependencies には、dojo.require(); で指定した名前を記述してください。
var dependencies = [
 	"dojo.io.*",
	"dojo.event.*",
	"dojo.xml.*",
	"dojo.graphics.*",
	"dojo.io.BrowserIO",
	"dojo.widgets.*",
	"dojo.widgets.Button"];
load("getDependencyList.js");


プロファイルは {{Dojo Dir}}/buildscripts/profiles においてください。命名規則(*.profile.js)を守ると作業が楽です。

3. ビルドの実行
{{Dojo Dir}}/buildscripts/ に移動して、以下のようなコマンドを実行してください。
ant -Ddocless=true -Dprofile=kodougu clean release intern-strings strip-resource-comments

  • Dprofile という引数にプロファイル名(*.profile.js の * の部分)を指定してください。

{{Dojo Dir}}/release/dojo/dojo.js が生成されます。このファイルに、全ての JavaScript が含まれます。

dojo のリファレンスの「Including Non-Dojo Resources」を参照すれば、自作の JavaScript をビルドに含める方法もわかります。(dojo.provide() してやる必要がありますが・・・。)

以上、dojo のビルド手順でした。

Posted by あかさた