Cog VM リリース記念: Squeak、Ruby、Python を恒例のフィボナッチベンチで戦わせてみる


Cog VM は、VisualWorks用の超高速 Smalltalk VM を手がけた Eliot Miranda 氏による Squeak Smalltalk 向けの新しい高性能 VM

Teleplace社(旧 Qwaq社)の製品である同名の仮想空間共有ソフトのベースである Croquet用に開発されたものですが、同社の厚意によりオープンソースとして公開され、Squeak のユーザーも利用可能になりました。


Win 向けのバイナリも公開されていたので、さっそく恒例のフィボナッチベンチ(39番目のフィボナッチ数 63245986 の再帰的な算出にかかる時間を計測。実装はナイーブなものにして、メモ化や遅延評価は使用しない)で人気のスクリプト言語RubyPython)と戦わせてみました。結果はこんなかんじ(Core2 Duo 2.4GHz, Win Vista を使用)。

追記: Python3.1.2、Gauche0.9 の結果を追加。
追記: リクエストにおこたえして C と v8 の結果も追加。

 処理系                       速度[秒]                      
 Ruby1.8  123
 Ruby1.9  20.8
 Python2.5  50.3
 Python3.1  66.2
 Gauche0.9  16.0
 Squeak4.1 normal VM  15.6 (メソッド版), 63.5 (ブロック版)
 Squeak4.1 Cog VM  4.79 (メソッド版), 5.36 (ブロック版)
 VisualWorks7.7  1.65 (メソッド版), 3.34 (ブロック版)
 C  1.76
 v8-2.1  1.88


商用で爆速の Smalltalk 処理系である VisualWorks には一歩及ばないものの、ノーマルVM で“よい勝負”どまりだった Ruby1.9 に対し、新しい Cog VM ではメソッド版(Integer>>#fib として定義。39 fib で呼び出し)のみならず、大きく水をあけられていたブロック版(Ruby風に言えば Proc版。fib value: 39 で呼び出し)でも圧勝しています。すばらしいですね。


使用したコードと出力などを以下に示します。

Ruby
$ cat fib.rb
def fib(n)
  return n if n < 2
  fib(n-2) + fib(n-1)
end

n = ARGV[0].to_i
start = Time.now
puts fib(n), (Time.now - start).to_s + " sec"



$ ruby -v fib.rb 39
ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin]
63245986
123.01 sec


$ ruby1.9 -v fib.rb 39
ruby 1.9.1p0 (2009-01-30 revision 21907) [i386-cygwin]
63245986
20.767 sec
Python(2、3 共通)
$ cat fib.py
from sys import argv
from time import time

def fib(n):
  if n < 2:
    return n
  else:
    return fib(n-2) + fib(n-1)

n = int(argv[1])
start = time()
print(fib(n))
print(str(time() - start) + " sec")


$ python -V
Python 2.5.2


$ python fib.py 39
63245986
50.3140001297 sec


$ python3 -V
Python 3.1.2


$ python3 fib.py 39
63245986
66.2220001221 sec
SmalltalkSqueakVisualWorks 共通)
| fib mTime mRes bTime bRes |
Integer compile: 'fib
  ^(self < 2) ifTrue: [self] ifFalse: [(self - 2) fib + (self - 1) fib]'.

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

mTime := Time millisecondsToRun: [mRes := 39 fib].
bTime := Time millisecondsToRun: [bRes := fib value: 39].
^Array with: mTime -> mRes with: bTime -> bRes
"Squeak VM 4.0.2 => {15573 -> 63245986. 63477 -> 63245986} "
"Cog VM          => { 4793 -> 63245986.  5364 -> 63245986} "
"VisualWorks 7.7 => { 1651 -> 63245986.  3335 -> 63245986} "
SchemeGauche
$ cat fib.scm
(define (fib n) (if (< n 2) n (+ (fib (- n 2)) (fib (- n 1)))))
(display (time (fib (string->number (car *argv*)))))


$ gosh -V fib.scm 10
Gauche scheme shell, version 0.9 [utf-8,pthreads], i686-pc-cygwin


$ gosh fib.scm 39
;(time (fib (string->number (car *argv*))))
; real  16.020
; user  15.054
; sys    0.015
63245986
C
$ cat fib.c
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

long fib(int n){
   if (n < 2) {
      return n;
   } else {
      return fib(n-2) + fib(n-1);
   }
}

int main(int argc, char *argv[]){
   clock_t startTime, endTime;
   long res;
   startTime = clock();
   res = fib(atoi(argv[1]));
   endTime = clock();
   printf("%ld, %f sec\n", res, (float)(endTime - startTime)/CLOCKS_PER_SEC);
   return 0;
}



$ gcc -Wall -O2 -o fib fib.c


$ ./fib 39
63245986, 1.762000 sec
JavaScript (v8)
$ cat fib.js
function fib(n) {
  if (n < 2) {
    return n;
  } else {
    return fib(n-2) + fib(n-1);
  }
}

var start = new Date();
print(fib(39));
print(new Date() - start + " msec");


$ v8-2.1/shell fib.js
63245986
1890 msec