メソッド呼び出しループベンチにSmalltalkで参戦してみる
ただし手元の環境(MacBook Air 11", 1.6GHz Core 2 Duo, Win 7)は非力なので、ループ回数は 268435455回に減らしました。Cygwin環境であるせいか、Objective-C は元記事でとりざたされている結果と違い、C++ の5倍までは遅くなっていませんでした。かたや Java は C++ の2倍程度に遅くなっていて、Objective-C とはほとんど差がありませんでした。
Smalltalk はまずまずの結果といった感じでしょうか。正直、爆速を誇る商用処理系であるところの VisualWorks には、C++ とまではいかずとも Objective-C には肉薄して欲しかったところですが、前述の通り Objective-C に逃げ切られたかたちであえなく敗退。それでも C の 4〜5 倍ほどと、これはこれで順当な結果でした。言うまでもなく、Python や Ruby に比べればはるかに高速です。追記:Squeak の次世代 Cog VM も健闘しています。
言語 | 処理系 | 時間 |
C | gcc 3.4.4 | 2.3 秒 |
C++ | gcc 3.4.4 | 2.2 秒 |
Java | Java 1.6.0 (HotSpot VM) | 4.2 秒 |
Java | Java 1.6.0 (インタプリタ) | 25.2 秒 |
Objective-C (IMP) | gcc 3.4.4 | 2.2 秒 |
Objective-C | gcc 3.4.4 | 5.0 秒 |
JavaScript | Node.js 0.4.1 (v8) | 8.2 秒 |
Smalltalk | VisualWorks7.7nc | 10.6 秒 |
Smalltalk | Squeak4.1 | 118.4 秒 |
Smalltalk | Squeak4.1 (Cog VM) | 26.5 秒 |
Smalltalk | Pharo1.1.1 (Cog VM) | 27.9 秒 |
Common Lisp | SBCL 1.0.37 | 29.4 秒 |
Ruby | Ruby1.8.6 | 501.5 秒 |
Ruby | Ruby1.9.2 | 128.5 秒 |
Python | Python 2.7 | 456.6 秒 |
Python | Python 3.1.2 | 475.9 秒 |
Scheme | Gauche 0.9 | 550.2 秒 |
なお、C、C++、Objective-C、Java、Python のコードは 公開されているものをループ回数の部分を変えて使わせて頂きました。Smalltalk と Ruby については、Java 版のをベースに次のようにそれぞれの言語向けに書きなおしたものを使用しています。
追記: Gauche、SBCL でも試してみました。
追記: Squeak Cog VM、Pharo 1.1.1 Cog VM の結果を追加。
追記: Node.js (v8) の結果を追加
Smalltalk (VisualWorks、Squeak 共通 Squeak 用。VisualWorks 向けには Looping class >> new ^super new initialize の追加が必要)
Object subclass: #Looping instanceVariableNames: 'n0' Looping >> initialize n0 := 0 Looping >> calc: n | n1 | n1 := n0 + (1 - (2 * (n \\ 2))). n0 := n. ^n1 Looping class >> benchmark "self benchmark" | l n t1 t2 | l := Looping new. n := 1. t1 := Time millisecondClockValue. 268435455 timesRepeat: [ n := l calc: n ]. t2 := Time millisecondClockValue. Transcript cr; show: n printString. Transcript cr; show: 'Smalltalk'; tab; show: (t2 - t1 / 1000.0) printString
Ruby
class Looping def initialize @n0 = 0 end def calc(n) n1 = @n0 + (1 - 2*(n%2)) @n0 = n return n1 end end l = Looping.new n = 1 t1 = Time.now 268435455.times{ |c| n = l.calc(n) } t2 = Time.now print "#{n}\nRuby\t#{t2 - t1}\n"
Scheme (Gauche)
(use srfi-19) (define-class <looping> () ((n0 :init-value 0 :accessor n0-of))) (define-method calc ((self <looping>) (n <integer>)) (let ((n1 (+ (n0-of self) (- 1 (* 2 (modulo n 2)))))) (set! (n0-of self) n) n1)) (let ((l (make <looping>)) (n 1) (t1 (current-time))) (dotimes (c 268435455) (set! n (calc l n))) (display (time-difference (current-time) t1)))
Common Lisp
(defclass looping () ((n0 :initform 0 :accessor n0-of))) (defmethod calc ((self looping) (n integer)) (let ((n1 (+ (n0-of self) (- 1 (* 2 (mod n 2)))))) (setf (n0-of self) n) n1)) (let ((l (make-instance 'looping)) (n 1) (t1 (get-internal-real-time))) (dotimes (c 268435455) (setq n (calc l n))) (print (float (/ (- (get-internal-real-time) t1) internal-time-units-per-second))))
JavaScript (Node.js)
function Looping(){ this.n0 = 0; this.calc = function(n){ var n1 = this.n0 + (1 - 2*((n+2)%2)); this.n0 = n; return n1; } } var sys = require('sys'); var n = 1; var l = new Looping(); var t1 = new Date(); for(c=0;c<268435455;c++){ n = l.calc(n); } sys.print(n, "\n", (new Date() - t1) / 1000);