フィボナッチベンチ


ゲキ遅だった Ruby が、Ruby 1.9 で人が変わったようにチョー速くなっているという話。


ナイーブな実装のフィボナッチでの比較はしたことがなかったように思ったので、手元の各種 Smalltalk 処理系と比べた場合どうかを試してみました。PythonRuby を含め、各処理系は最新版ではないので、最新版での結果が違っていたらごめんなさい。


とりあえず PythonRuby について、Holy Shmoly, Ruby 1.9 smokes Python away! | Programming Zen のコードを拝借して測定。ただ、ウチの骨董品のような環境(PowerPC 1.0GHz, OS X) では 35 とか無理なので(… orz)、31 までにして、PythonRuby の比較から(単位はミリ秒)。

Python 2.4 14617
Python 2.5 12864
Ruby 1.8 51724
Ruby 1.9 5871


たしかに Ruby の 1.9 での改善が際だちます。これではネタ元ブログの記事で、勝手に比較対象にされてしまった Python のファンは心おだやかではないでしょうね…(^_^;)。


対する Smalltalk 勢はどうか? コードは各処理系で使い回しが出来るよう、各処理系独自の機能は封印してオーソドックスなかたちで、こんなふうにしてみました。

● ブロック版
| fib |

fib := [:n |
    (n = 0 or: [n = 1])
        ifTrue: [n]
        ifFalse: [(fib value: n - 1) + (fib value: n - 2)]].

Time millisecondsToRun: [
    0 to: 31 do: [:i |
        Transcript cr; show: ('n=', i printString, ' => ', (fib value: i) printString)]
]
● メソッド版
!Integer methodsFor: 'mathematical functions'!
fib
    ^(self = 0 or: [self = 1])
        ifTrue: [self]
        ifFalse: [(self - 1) fib + (self - 2) fib]! !

Time millisecondsToRun: [
    0 to: 31 do: [:i |
        Transcript cr; show: ('n=', i printString, ' => ', i fib printString)]
]


GNU Smalltalk はそれぞれの末尾に ! を追加したあと、 fib32b.st、fib32.st などとして保存し、

$ time gst fib32.st

として実行、計測します。Time millisecondsToRun: [...] の出力は無視されますが、実行時の time が兼ねてくれます。


Cincom Smalltalk (VisualWorks) では、ブロック版は、ワークスペースにコピペして(必要なら改めて全コードを選択したのち)右ボタンメニューで Pint It します。メソッド版のほうは、Print It の前に ! から ! ! までを選択して同じく右クリックメニューの File it In をしておく必要があります。

http://squab.no-ip.com/collab/uploads/61/fileitin.png


その後、改めて Time millisecondsToRun: [ ... 以降を選択して Print It します。

http://squab.no-ip.com/collab/uploads/61/fibprintitvw.png


Squeak Smalltalk (Squeak) では、ざんねんながらブロック(無名関数オブジェクト)の再帰・再入ができないので、ブロック版はそのままでは動かせません。New Compiler をインストールして #compileBlocksAsClosures、#compileUseNewCompiler フラッグをオンにするか、次のように書き直すかする必要があります。

| fib |

fib := [:n |
    (n = 0 or: [n = 1])
        ifTrue: [n]
        ifFalse: [(fib copy fixTemps value: n - 1) + (fib copy fixTemps value: n - 2)]].

Time millisecondsToRun: [
    0 to: 31 do: [:i |
        Transcript cr; show: ('n=', i printString, ' => ', (fib copy fixTemps value: i) printString)]
]


メソッド版については、VisualWorks 同様に、メソッド定義部分を選択後、cmd + shift + G (Win 版では alt + shift + G)で file it in します。

http://squab.no-ip.com/collab/uploads/61/fileitinsq.png


ワークスペースからファイルインなんかしたことないから怖い…という向きは、操作に慣れているクラスブラウザで Integer クラスに #fib をすなおに定義した方が面倒がなくてよいかもしれませんね(^_^;)。コピペするときは、末尾の ! ! を省くのをお忘れなく。

http://squab.no-ip.com/collab/uploads/61/fibbrowsersq.png


なお、コンソール出力に比べて、Smalltalk のトランスクリプト出力表示のコストは、ハンデにしてはちょっと重すぎる(し、本質ではないとの勝手な判断をした(^_^;))ので、 VisualWorksSqueak では、トランスクリプトウインドウは、あらかじめ閉じた状態で計測しています。あしからず。



結果は次のようになりました。

GNU Smalltalk 2.3.3 ブロック版 5050
メソッド版 4654
Cincom Smalltalk 7.4NC ブロック版 992
メソッド版 539
Squeak Smalltalk 3.9 ブロック版 (New Compiler) 25289
ブロック版 (copy fixTemps) 33857
メソッド版 4262

やはりカネをかけて高速化技術の粋を極めた Cincom Smalltalk はダントツ。GNU Smalltalk も健闘しています。メソッド版がちょっと遅いのがが気になりますね(あるいはブロック版が速いのか?)。我らが Squeak Smalltalk は、無茶やり再帰もどきのブロック版でも Ruby 1.8 に勝っていて、メソッド版なら Ruby 1.9 を凌駕しているので、まあよしとしましょう。w