Ruby、Python、Javaの実行速度比較

前置き
最近のRubyとPythonの速度って、どんな感じになってるのかなぁと思ってググってみたところ、最新のバージョンに対してはあまりちゃんとした比較が見つけることができませんでした。まぁ、1回検索して20ページくらい見て回っただけなので、単に見落としているだけかもしれませんが。

で、パンが無ければケーキを食べれば良いように、欲しい情報が見つからなかったなら作れば良い、ということでRubyとPythonの比較表らしきものを作成してみました。

このコンテンツのコードは、PythonとRubyの処理速度を比較する目的で作成しました。この時、先にどちらかの言語で作成してしまうと、先に書いた言語に対して最適化してしまいそうな気がしたので、最初にJavaで書いて、それと同じ処理をRuby・Pythonに置き換えることによって作成しました。

実行時間は3回実行して平均を取ってます。取り方は割と適当です。コードも割と適当なので、各言語に対して最適化すれば速度も変わると思いますが、とりあえずはJavaで書いたコードに似た記述をすることにしました。
概要
使用している言語のバージョン
・Python3.1
・Ruby1.9.1(mswin32版)
・Java1.6.0

使用したマシン
・WindowsXP SP3
・CPU PhenomX4 9550
・メモリ 3GB

比較するプログラム
・空ループ
・フィボナッチ数(ループ版)
・フィボナッチ数(再帰版)
・バブルソート
・リニアサーチ
・文字列生成
・ファイル出力

使用したプログラムのソース
Ruby版
Python版
Java版

注意点
ソースを見てもらえると分かると思いますが、そんなに厳密な測定はしていません。
いろいろツッコミどころはあると思うので、その辺を加味した上で参考までにご覧ください。
空ループ
単純に空のループを1億回行った際の実行時間です。
Javaではfor文でiをインクリメント、Rubyはi.times、Pythonはfor i in range(x)を使用しています。

Python Win : 56.2%

見ての通り、Rubyがえらく遅いです。timesが悪いのかと思い、普通にインクリメントさせてもみたのですが、結果はもっと遅くなりました。

Rubyは数値も全てクラスなので、単純な数値のインクリメントとかは苦手そうではあります。
フィボナッチ数(ループ版)
ループしながらフィボナッチ数を求めます。intの精度は楽に越えるので、JavaではBigIntegerを使用しています。Pythonは勝手に長整数が使われているはず(記憶が確かなら)。

Python Win : 81.4%

空ループの時はJavaがプリミティブなデータでかっ飛ばしてましが、BigIntegerになった場合はどの処理も内部的には似たようなものになります。それでもRubyとPythonの間には若干の開きがありますが。
フィボナッチ数(再帰版)
再帰を使ってフィボナッチ数を求めています。再帰回数は引数の二乗に比例するので、恐ろしい数の再帰が発生します。


Python Win : 31.7%

どうも予想以上に速度に差が出ています。これだけの差が出ると、自分のコードが悪いんじゃないかとも思いますが、別に変態的な記述はしてないはず。
ランダム数値リスト作成
リストに大量のランダム生成された数値を追加していく処理です。
JavaはArrayList、RubyはArray、Pythonはlistを使用しています。


Ruby Win : 90.1%

Ruby初勝利です。単純に配列に値を追加していくだけの処理であれば、パフォーマンスが悪くなる要素は無いようです。
バブルソート
全件順に比較→入換えを繰り返す単純なソートです。
JavaはCollections.swapを使用しています。RubyとPythonは「list[i], list[i - 1] = list[i - 1], list[i]」のような形で入換えています。

Python Win : 3.3%

これはさすがに何かの間違いかもしれません。信じられない人はコードを見直したり自身の手でテストすることをオススメします。
リニアサーチ
上のバブルソート時に生成したリストに対して、全要素で自分自身をサーチするとかいう意味の無い行為をしています。
ところでリニアサーチって、最初に聞いた時、なんか速く動きそうな印象受けませんでした?


Python Win : 3.4%

まただいぶ差が出ました。これは単純な記述なので、間違いでは無いと思われます。ループ回数が多い処理なので、そもそもループ自体に時間がかかりやすいRubyには厳しいのかも。
文字列連結
ランダムな数値(0~26)を使ってアルファベットを生成し、それを連結して文字列を作る処理です。
Rubyは >> で、JavaはStringBufferで、Pythonは += で連結しています。Pythonはこのやり方が正なのかは分かりません。どこかの記事で実は += が一番速いとか書いてあった気がしたのでこうしました。


Python Win : 11.6%

数値から文字列を生成する際、Javaは「(char)i」、Rubyは「pack("c*")」、Pythonは「chr(i)」を使用しています。この部分も速度に影響しているかも。
ファイル出力
ファイルを1万個ほど生成し、文字列連結で生成した文字列を書き込んでいます。
JavaはFileOutputStream、RubyはFile::open、Pythonはopenを使用しています。


Python Win : 72.7%

ほとんどの処理はOSがファイルを書き込む箇所に費やされるので、大きな差は出ませんでした。Javaが初めてPythonに負けてます。Pythonのファイル操作は組み込み関数ですし、ネイティブにより近いのかもしれません。
総括
思っていたよりも大きな差が付く処理もありましたが、概ね予想した通りの結果になりました。

処理速度的には、やはりPythonの方が一段上と言えるようです。

ただ、1億回空ループした時の差を見ると分かるように、単純にループするだけで処理に差がついてしまうので、今回のように何百万回も処理させて実行時間を計測するという方式は、あまり公平ではなかったのかもしれません。

OSによって実装は変わるので、Linuxで計測すればまた違った値になりそうな気もします。