Ruby で URL エンコーディングを行ってくれる CGI.escape メソッドでは、スペースをプラス(+)に変換します。(仕様どおりの動作だと思います。)しかし、JavaScript でデコードを行う decodeURIComponent() 関数では、+ をスペースには変換してくれないようです。したがって、Ruby でスペースを含む文字列を URL エンコードをして JavaScript でデコードすると、プラスがデコードされずに残ってしまいます。

Kodougu でこの問題にあたってバグが発生したので、とりあえず、Ruby で URL エンコードした文字列のプラスを %20 に置き換えました。%20 はスペースに変換してくれるようです。なにやらこの手の問題は微妙にエンジニアを悩ませますね。。。

Posted by あかさた
私が書いたにしては妙に反応のあった「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 あかさた
長いこと積読になっていた「JavaからRubyへ」をようやく読み終わりました。(※ この本を買ったのは 4 月か 5 月。)この本は、日本で Java の案件をこなしているマネージャが読んでピンを来るものなのでしょうか。マネージャ級の人に聞いてみたいところです。

大筋要約してしまえば以下の通り。
・ Java で対処するとコストが大きくなるプロジェクトもある。なんでも Java じゃリスクが高いよ
・ Ruby(というか Rails)はその中のいくつかのプロジェクトに対処できる
・ Ruby の導入にあたって、パイロットプロジェクトの実施方法
・ Ruby の導入手順(教育、リクルーティングなど)
・ そのほか Ruby や Rails の紹介(ある意味、どうでもいい)

多分、そこそこの規模のある SI 企業であれば、将来有望そうな新技術のパイロットプロジェクトを走らせて、定量的、定性的なデータの収集を行っていることでしょう。この本の位置づけとしては、その対象に Ruby(というか Rails)を含めた方が良いという啓発本なのかも。

Amazon のレビューより(tDiary のたださんがかいてらっしゃる?):

だいたいマネージャ層には、本書に登場するDave ThomasやMartin Fowlerの名前などなんの威光も持たない。


この本がこうしたヴィジョナリーを挙げているということは、海外では威光をもつということなのでしょうか。ちょっと気になります。誰かわかる人いないかな。

日本ではこうした改善はボトムアップに上がっていくところがあるので、この本は末端の開発者が手にとったらいいのかもしれませんね。

■ 追記(2007/6/28 5:41)
はてブにコメントがついていました。どうやら、訳者の方のようです。

はてブコメントより、「この本は、日本で Java の案件をこなしているマネージャが読んでピンを来るものなのでしょうか。」:

弊社経営陣にはリーチしました


これは素晴らしい話ですね。確かに、永和システムマネジメントさんは、Ruby に対して非常に熱心(会社として熱心)という印象があります。まったく同じというわけにはいかないのでしょうが、この本で書かれているような段階を踏んで、会社として「Ruby やりますよ」って宣言しているわけなのでしょうから、やはり凄い話です。

Posted by あかさた
Ruby.NET Compiler ですが、C# などの言語との相互接続性(interoperability)を確立したようです。つまり、Ruby 上で C# で宣言したクラスのインスタンスを生成したり、C# 上で Ruby で宣言したクラスのインスタンスを生成したりできるようです。継承などがどうなるのかは試していません。

eval、パフォーマンス、日本語はどうなるのかなど、気になる点は多いですが、5 月には VS.NET 上での開発も実現しており、機能的には使えるレベルまできた気がします。

Rails を移植するつもりもあるようです。個人的には全然うれしくないですけど。長期的には意味があるかもしれませんが、向こう数年間では、.NET で Rails が動いてもそれほど意味がない気がします。Mono があるとはいえ動作する OS が制限される、gem は?、数々のプラグインは?、などなど心配事は尽きません。メリットがあるとすれば、ウェブインタフェース(オンライン処理)は Rails、バッチは C# というような使い分けかもしれません。

それに、Ruby はパフォーマンスがあまり良くないのでマシンを並列することも多いと思いますが、有料の OS はマシンごと or CPU ごとに課金することが多いので、そういう意味でも難しいと感じています。

個人的には、クライアントに可能性を求めて欲しいところです。ただ、.NET DLR の Ruby(IronRuby)との位置づけはどうなるのでしょうか。(処理系が増えること自体は、歓迎すべきことでしょう。)

(相反することをいうようですが、個人的には JRuby 上では Rails は動作して欲しいです。Kodougu を移植したいので。EMF とか使いたいものがたくさんあります。)

ま、今後に期待ということで。

Posted by あかさた
いまさら、Rails の Scaffold の話です。

コントローラの宣言時に以下のように書くと、アクション(メソッド)にモデル名をサフィックスとして追加してくれます。(edit なら、edit_model_name になります。)
scaffold :model_name, :suffix => true

便利ですね・・・。AdminController とか作って、一時的に各種データの出し入れの管理画面を作るときに使用しています。

そもそも、私は Scaffold はコード生成時にしか使っていませんでした。サフィックスをつけられるなんてことも知らずに今まで過ごしていました。なんて損していたんだろうか。(--;

Posted by あかさた
日本 Ruby 会議 2007 の Dave Thomas のスピーチが載っていました。すばらしい内容。こんなことをしゃべれる人間になりたいものです。こんなことなら行けばよかったな~。。。

さて、変なところに反応してみます。

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

でも、あるプログラマに着目すれば、そのプログラマが時間あたりに書けるコードの行数は、プログラミング言語によらず決まっている、たとえば一日に50,000行なのだそうだ。


先生! 私は一日に 500 行も書けません!!
(Kodougu なんて 5000 行くらいで出来ています。)

えー、なんと言うかあれです。プログラマの生産性は少なくとも 100 倍は格差がある・・・と。私は自分が最底辺でないことを願っていますが(苦笑)。しかし驚くに値しないことです。私はピアノが趣味(20 年以上やってる)ですが、ピアノの初心者が 1 曲弾けるようになる間に、同程度の曲を同程度の品質で 10 ~ 50 曲くらいは習得できるでしょう。才能のある人なら、その数倍 ~ 数十倍になってもまったくおかしくありません。

要はそういうこと・・・なんでしょうねぇ。はぁぁ・・・。

■ 追記(2007/6/13 23:39)
一日に 50000 行ではなく、一年に・・・だそうです。ミスプリかな。ま、この手の数字は話のネタなので、そんなにまじめに考える必要は無いですよ。

でも、Ruby や Lisp で 50000 行も書いたら、世界征服できるんじゃないかって考えるのは私だけですかねー。(大袈裟)

Posted by あかさた
最近、.NET の事情を追っかけていなかったので、今日は少し追っかけていました。最近、Java の世界では JRuby が盛り上がっていますが、.NET の世界でも動的言語の勢いがかなり強いようです。Jython や IronPython の開発者である Jim Hugunin のブログを読んでいたら、.NET における動的言語の取り扱いがなんとなく見えてきました。

Jim Hugunin's Thinking Dynamic
http://blogs.msdn.com/hugunin/archive/2007/04/30/a-dynamic-language-runtime-dlr.aspx

上記のブログで、私が気になった点を抜き出しておきます。

1. CLR(Common Language Runtime)について
上記のブログでは、CLR の利点を二つ挙げています。一つは、プログラミング言語を簡単に作れるようになったことです。これは、CLR が言語を作るうえで技術的に難しい点(GC だとか JIT だとかセキュリティモデルだとか)をすでに実装しているからだそうです。もう一つの利点は、異なるプログラミング言語をシームレスにつなげるようにもなったこととのことです。

2. CLR と DLR
IronPython で示したとおり、CLR は動的言語をよくサポートしています。さらに、CLR 上で動的言語を作りやすくするために、動的型システムなどを実装した DLR(Dynamic Language Runtime)というものを作るそうです。DLR の上で、Python、JavaScript、VB Script、Ruby を実装するそうです。

3. DLR と Silverlight
DLR は Silverlight にも搭載されるそうです。サーバもクライアントも Ruby で書けるなら、全てを Ruby に・・・と、夢のような世界が!

※ Silverlight とは、MS 版 Flash みたいなものです。

もちろん、DLR だけであれば、Java の世界にも似たような動きはすでにあったのでそれほど驚きはしませんが、Silverlight に乗っかってくるとは知りませんでした。(って、どれだけ世情に疎いんだ私は。orz)Kodougu を書いているときも、Ruby と JavaScript を行ったり来たりして最悪だったのですが、この世界が実現されればそういうことからも解放されそうです。

技術的なコンセプトはすばらしいので、後は政治的な理由から Linux は仲間はずれ(Silverlight は Mac の Safari にも対応するらしい)とかそういう馬鹿馬鹿しい現象がおきなければ良いのですが。。。


Posted by あかさた
Ruby では i++ はサポートしていないようです。これまで気づいていませんでした。イテレータが準備されるようになると for 文を書かなくなるので、これまで i++ のような表記をしなかったということでしょうか。

Ruby での書き方:
i += 1

ruby-list におけるまつもと氏の見解:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/126

この文法は驚き最小の法則からは外れる・・・気がするのですが、Delphi 出身の私にとってはむしろ自然に感じたりします。(笑)

Delphi での書き方:
Inc(i);

なぜか、Delphi では上記のように書くと高速でした。Delphi はインライン展開をサポートしていなかったと思いますが、こういう関数は特別扱いされていて、アセンブラか何かで実装されていて、その処理をインライン展開していたのかもしれません。
# 今となってはどうでも良いことですが。

Posted by あかさた
act_as_authenticated に自動生成された UserTest の test_should_reset_password が失敗するという現象が発生しています。具体的には以下のコードで失敗します。
# 前からこんな現象あったかなー???

def test_should_reset_password
  users(:quentin).update_attributes(:password => 'new password', 
    :password_confirmation => 'new password')
  assert_equal users(:quentin), User.authenticate('quentin', 'new password')
end

エラーの発生条件:RadRails 0.7.1 上で Run Unit Tests を実行する、rake test:units を実行する

ただし、上記テストメソッド単体を実行すると、テストは成功することから、他のテストメソッドの実行結果に影響されて失敗していることがわかります。DB の内容を見ると、users(:quentin) の login(ユーザ名)は 'quentin' ではなく 'quentin2' になっています。このことから、test_should_reset_password は、test_should_not_rehash_password に依存しているようです。

Ruby(と Rails)の UnitTest フレームワークの性質をよく知らないのですが、テストメソッドを実行するたびに DB の初期化が実行されればこういった問題は発生しないと考えられるので、DB の初期化を行うタイミング(=fixtures メソッドが呼ばれるタイミング)はテストクラスを読み込む一度きりということでしょう。

とはいえ、原因がわかっても、テストコードの修正は簡単ではありません。他のテストの実行の有無で結果が変わってしまうテストを変わらないコードにするというのはなかなか難しいことです。とりあえず以下のように考えてみました。

このテストのやりたいこと:レコード更新時にパスワードを変更する(:password_confirmation があるときは、パスワードを更新することを確認したい。※)

※ そういう意味では、更新時に :password のみを変更して :password_confirmation を変更しない or 間違っているテストが無いことは少し疑問です。もっとも、生成時のテストはあるからそれで問題はありませんが。

修正案1:レコード更新時に login も更新する
def test_should_reset_password
  users(:quentin).update_attributes(:login => 'quentin', 
    :password => 'new password', :password_confirmation => 'new password')
  assert_equal users(:quentin), User.authenticate('quentin', 'new password')
end

メリット:修正は簡単
デメリット:テストコードの意図がやや不明瞭になる

修正案2:更新したユーザのインスタンスから名前を取得して、認証を行う
def test_should_reset_password
  users(:quentin).update_attributes(:password => 'new password', 
    :password_confirmation => 'new password')
  assert_equal users(:quentin), User.authenticate(users(:quentin).login, 'new password')
end

メリット:テストの意図はそのまま
デメリット:元と比べてコードがわかりにくい?(というか、なんか気持ち悪い・・・)

とりあえず、修正案2で行きます。


Posted by あかさた