型宣言とかメソッドのディスパッチとか
2週間ほど前、複数のプログラミング言語(処理系)でメソッドの起動時間を比べるという、
エントリが一部で盛り上がっていたらしいです。
- Objective-Cの『遅さ』を計測したら、JavaやC++の5倍も遅かった: ニュースの社会科学的な裏側
- メソッド呼び出しループベンチにSmalltalkで参戦してみる: Smalltalkのtは小文字です
既ににいろいろな言語で計測が行われているので、元のエントリとは趣向を変えて、
Common Lispのみについて、書き方による速度の変化を見てみました。
計測にはMac Book (Core 2 Duo 2GHz)で SBCL 1.0.44を使用しました。
計測結果はこちら。
| 元のプログラム | 20.43 秒 |
| 型宣言を追加 | 14.897 秒 |
| 総称関数を普通の関数に変更 | 8.179 秒 |
| インスタンスをクロージャに変更 | 3.558 秒 |
| スペシャル変数を使用 | 3.805 秒 |
元のプログラムはこれです。
(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))))
手始めに型宣言を付けました。
(defclass looping () ((n0 :initform 0 :accessor n0-of :type fixnum)))
(defmethod calc ((self looping) (n integer))
(declare (optimize (safety 0) (speed 3)))
(declare (fixnum n))
(let ((n1 (the fixnum (+ (the fixnum (n0-of self)) (- 1 (* 2 (mod n 2)))))))
(declare (fixnum n1))
(setf (n0-of self) n)
n1))
型をfixnumに固定するだけで結構速くなります。
次に、defmethodをdefunに変更。
(defun calc (self n)
(declare (optimize (safety 0) (speed 3)))
(declare (fixnum n))
(declare (looping self))
(let ((n1 (the fixnum (+ (the fixnum (n0-of self)) (- 1 (* 2 (mod n 2)))))))
(declare (fixnum n1))
(setf (n0-of self) n)
n1))
元のエントリの趣旨から完全に外れてしまった感じですが、
ディスパッチが不要になるとかなり速くなります。
本来の趣旨からどんどん離れてクロージャとか使っちゃいます。
(let ((self 0))
(declare (fixnum self))
(defun calc (n)
(declare (optimize (safety 0) (speed 3)))
(declare (fixnum n))
(let ((n1 (the fixnum (+ (the fixnum self) (- 1 (* 2 (mod n 2)))))))
(declare (fixnum n1))
(setf self n)
n1)))
(time (let ((n 1) (t1 (get-internal-real-time)))
(dotimes (c 268435455)
(setq n (calc n)))
(print (float (/ (- (get-internal-real-time) t1) internal-time-units-per-second)))))
めちゃくちゃ速くなりました。
最後に、クロージャすら使わずにスペシャル変数を使いました。
(defvar *self* 0)
(declaim (fixnum *self*))
(defun calc (n)
(declare (optimize (safety 0) (speed 3)))
(declare (fixnum n))
(let ((n1 (the fixnum (+ (the fixnum *self*) (- 1 (* 2 (mod n 2)))))))
(declare (fixnum n1))
(setf *self* n)
n1))
意外なことに、クロージャ版より遅くなってしまいました。
ダイナミックスコープに備え、なにか面倒なことをしてるんでしょうか。
もっと速くする方法を知ってる方がいましたら、ぜひとも教えてください。