型宣言とかメソッドのディスパッチとか
木曜日, 3月 3rd, 20112週間ほど前、複数のプログラミング言語(処理系)でメソッドの起動時間を比べるという、
エントリが一部で盛り上がっていたらしいです。
- 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))
意外なことに、クロージャ版より遅くなってしまいました。
ダイナミックスコープに備え、なにか面倒なことをしてるんでしょうか。
もっと速くする方法を知ってる方がいましたら、ぜひとも教えてください。