ディスパッチ目的のインスタンスを都度生成しない


Tags: MOP, ライブラリ, closer-mop

主にCommon Lispの多重メソッドのディスパッチ機能のみを活用する場合、ディスパッチ目的のインスタンスは極力生成させたくない場合があります。 このような場合、MOP:class-prototypeが活用できます。

Common Lispのクラスは、各々インスタンスのプロトタイプを保持しており、 class-prototype はそのプロトタイプを取り出す総称関数です。
MOPのコンパチブルレイヤーとしてCLiki:Closer-MOPを利用します

(defclass level () ())
(defclass role () ())

(defclass R1 (role) ())
(defclass R2 (role) ())

(defclass L1 (level) ())
(defclass L2 (level) ())

(defmethod foo ((l level) (r role) arg)
  (values arg "default"))

(defmethod foo ((L1 level) (R2 role) arg)
  (values arg "[level 1] - [role 2]"))

class-prototype 利用と make-instanceを利用の場合を、それぞれ1,000,000回繰り返したものを計測してみます。

(defun foo/class-prototype ()
  (dotimes (i (expt 10 6))
    (foo (class-prototype 'L1) (class-prototype 'R2) 42)))

(defun foo/make-instance ()
  (dotimes (i (expt 10 6))
    (foo (make-instance 'L1) (make-instance 'R2) 42)))

(progn
  (time
   (foo/class-prototype))
  (time
   (foo/make-instance)))

Timing the evaluation of (foo/class-prototype)

User time    =        0.190
System time  =        0.000
Elapsed time =        0.184
Allocation   = 7896 bytes
0 Page faults
Timing the evaluation of (foo/make-instance)

User time    =        0.310
System time  =        0.000
Elapsed time =        0.318
Allocation   = 64000000 bytes
0 Page faults

1,000,000 回の繰り返しで、速度はそれ程変化がありませんが、アロケーションで約8100倍の差が出ています。
(例はLispWorks 7.0で計測)