メソッド呼び出しループベンチにSmalltalkで参戦してみる


ただし手元の環境(MacBook Air 11", 1.6GHz Core 2 Duo, Win 7)は非力なので、ループ回数は 268435455回に減らしました。Cygwin環境であるせいか、Objective-C は元記事でとりざたされている結果と違い、C++ の5倍までは遅くなっていませんでした。かたや JavaC++ の2倍程度に遅くなっていて、Objective-C とはほとんど差がありませんでした。


Smalltalk はまずまずの結果といった感じでしょうか。正直、爆速を誇る商用処理系であるところの VisualWorks には、C++ とまではいかずとも Objective-C には肉薄して欲しかったところですが、前述の通り Objective-C に逃げ切られたかたちであえなく敗退。それでも C の 4〜5 倍ほどと、これはこれで順当な結果でした。言うまでもなく、PythonRuby に比べればはるかに高速です。追記Squeak の次世代 Cog VM も健闘しています。


















言語処理系時間
Cgcc 3.4.42.3 秒
C++gcc 3.4.42.2 秒
JavaJava 1.6.0 (HotSpot VM)4.2 秒

JavaJava 1.6.0 (インタプリタ)25.2 秒
Objective-C (IMP)gcc 3.4.42.2 秒
Objective-Cgcc 3.4.45.0 秒
JavaScriptNode.js 0.4.1 (v8)8.2 秒
SmalltalkVisualWorks7.7nc10.6 秒
SmalltalkSqueak4.1118.4 秒
SmalltalkSqueak4.1 (Cog VM)26.5 秒
SmalltalkPharo1.1.1 (Cog VM)27.9 秒
Common LispSBCL 1.0.3729.4 秒
RubyRuby1.8.6501.5 秒
RubyRuby1.9.2128.5 秒
PythonPython 2.7456.6 秒
PythonPython 3.1.2475.9 秒
SchemeGauche 0.9550.2 秒


なお、C、C++Objective-CJavaPython のコードは 公開されているものをループ回数の部分を変えて使わせて頂きました。SmalltalkRuby については、Java 版のをベースに次のようにそれぞれの言語向けに書きなおしたものを使用しています。

追記GaucheSBCL でも試してみました。
追記Squeak Cog VM、Pharo 1.1.1 Cog VM の結果を追加。
追記: Node.js (v8) の結果を追加



Smalltalk (VisualWorksSqueak 共通 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);