2016-05-24-01-ファンクタをfuncallableにする問題について - project-enigma

2016-05-24-01-ファンクタをfuncallableにする問題について

>> Site top >> weblog >> 月別アーカイブ >> 2016年05月のlog >> 2016-05-24-01-ファンクタをfuncallableにする問題について

最終更新日付:2016/05/24 21:21:45


ファンクタをfuncallableにする問題について

2016 年 05 月 24 日

さて、そろそろアレに取り組んでみようと思った。任意のクラスを funcallable にできるよ、と教えて頂いたアレだ。試行錯誤の末、とりあえず以下のような実験コードになった。

(defclass functor (sb-mop:funcallable-standard-object)
  ((closure :type     cl:function
            :initform nil
            :initarg  :closure
            :accessor functor-closure))
  (:metaclass SB-MOP:FUNCALLABLE-STANDARD-CLASS))

(defmethod initialize-instance :after ((fnctr functor) &key)
  (sb-pcl::set-funcallable-instance-function fnctr (functor-closure fnctr)))

 

まだ可搬性は意識していないので、SBCL に固有の sb-mop:funcallable-standard-object とか sb-pcl::set-funcallable-instance-function とかが露出している。また、functor クラスは実行コード自体をメンバとして保有し、initialize-instance において登録をするようになっている。これがベストの方法なのかどうかはわからない。今回は実験なので、これでひとまずパフォーマンスを見てみようかと思う。

用意したのは以下の関数。簡単だけど、まぁこんなもんでいいでしょう。

(locally (declare (optimize speed))
  (defun check-time (fnc &optional (repeat-count 100000000))
    (time (dotimes (n repeat-count)
            (funcall fnc n)))))

 

で、まずは関数を使ってみよう。結果は以下の通り。何度も実行してみたが、大体同じ結果だった。

(let ((func (locally (declare (optimize speed))
              (lambda (n)
                (declare (type fixnum n))
                (the fixnum (* n 2))))))
  (check-time func))

; Evaluation took:
;   0.850 seconds of real time
;   0.842406 seconds of total run time (0.842406 user, 0.000000 system)
;   99.06% CPU
;   2,883,561,919 processor cycles
;   0 bytes consed

 

では次。同じことをする関数を functor にして実行してみた。結果は以下の通り。これも何度も実行してみて、大体同じ結果になっている。

(let ((fnctr (make-instance 'functor
                            :closure (locally (declare (optimize speed))
                                       (lambda (n)
                                         (declare (type fixnum n))
                                         (the fixnum (* n 2)))))))
  (check-time fnctr))

; Evaluation took:
;   0.930 seconds of real time
;   0.936006 seconds of total run time (0.936006 user, 0.000000 system)
;   100.65% CPU
;   3,173,165,095 processor cycles
;   24,896 bytes consed

 

同じ funcall で呼び出しているとはいえ、やはり少し遅いようだ。では、functor から『中身』を取り出してみたらどうなるだろう? これは CL-STL が現状でパフォーマンス対策のためにやっていることだ。もちろん実験コードなのでまったく同じではないが、以下のような感じ。

(let ((fnctr (make-instance 'functor
                            :closure (locally (declare (optimize speed))
                                       (lambda (n)
                                         (declare (type fixnum n))
                                         (the fixnum (* n 2)))))))
  (check-time (functor-closure fnctr)))

; Evaluation took:
;   0.840 seconds of real time
;   0.842405 seconds of total run time (0.842405 user, 0.000000 system)
;   100.24% CPU
;   2,833,332,510 processor cycles
;   0 bytes consed

 

まぁコレは、やっぱりというかなんというか、最初の関数の例と同じくらいのパフォーマンスとなった。しかし、半月くらい前に書いた時は、ファンクタを(現状の方式である)stl:functor-call で呼び出すと、関数の場合に比べて5倍くらい遅かった。それに比べると全然速いし、functor から『中身を取り出す』仕組みを残しておけば、わずかな性能劣化も避けたい時には役立つだろう。加えて、ファンクタクラスのインスタンスであろうと関数であろうと同じように funcall できる。これは非常に大きい。うん、大きいよね。

‥‥‥というわけで、これは実際に『やる』方向で進めたいと思う。それに伴う諸々の問題については、明日以降整理していくとしよう。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : STLとその移植

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.