2015-12-05-01-思い出したのでファンクタを再び - project-enigma

2015-12-05-01-思い出したのでファンクタを再び

>> Site top >> weblog >> 月別アーカイブ >> 2015年12月のlog >> 2015-12-05-01-思い出したのでファンクタを再び

最終更新日付:2015/12/05 17:13:35


思い出したのでファンクタを再び

2015 年 12 月 05 日

2 年くらい前、まだ現在のかたちになる前の CL-STL で、ファンクタ呼び出しのパフォーマンスについてぐだぐだ書いていた記事があった。それを読み返していて、現状はどうなっているのか、そこが気になった。今日はそんな話。

やることは2年前と変わらない。ただ、現在の CL-STL で動作するようなコードを書いて動かしてみるだけだ。まず、基準となる関数 foo-function を用意する。

(locally (declare (optimize speed))
  (defun foo-function (n)
    (declare (type fixnum n))
    (the fixnum (* 2 n))))

 

で、これに対応するファンクタクラス foo-functor は以下のようになる。やっぱりアレだな。なんとかなんないかなこれ。

(defclass foo-functor (#-cl-stl-0x98 stl:functor
                       #+cl-stl-0x98 stl:unary-function) ())

(declare-constructor foo-functor (0))

(locally (declare (optimize speed))
  (labels ((__foo-fnctr-ctor ()
             (make-instance 'foo-functor
                            :closure (lambda (n) (* 2 n)))))
    
    (define-constructor foo-functor ()
      (__foo-fnctr-ctor))

    (defmethod operator_clone ((obj foo-functor))
      (__foo-fnctr-ctor))))

 

次に、指定された回数だけファンクタ(または関数)をコールするテスト関数を作成する。

(locally (declare (optimize speed))
  (defun function-test (fnc n)
    (declare (type cl:function fnc))
    (declare (type fixnum n))
    (dotimes (i n)
      (funcall fnc i))))

(locally (declare (optimize speed))
  (defun functor-test1 (fnc n)
    (declare (type fixnum n))
    (dotimes (i n)
      (stl:functor-call fnc i))))

(locally (declare (optimize speed))
  (defun functor-test2 (fnc n)
    (declare (type fixnum n))
    (let ((fnc (stl:functor-function fnc)))
      (declare (type cl:function fnc))
      (dotimes (i n)
        (funcall fnc i)))))

 

みればわかると思うが、概ね以下の通り。

  1. function-test:関数を直接コールする(これが基準になる)
  2. functor-test1stl:functor-call を繰り返しコールする
  3. functor-test2stl:functor-function で関数を(一度だけ)取得してから繰り返し funcall する

 

これを以下の要領で実行する。繰り返し回数は1億回。

(defparameter *count* 100000000)

(time (function-test #'foo-function *count*))
(time (functor-test1 #'foo-function *count*))
(time (functor-test1 (new foo-functor) *count*))
(time (functor-test2 #'foo-function *count*))
(time (functor-test2 (new foo-functor) *count*))

 

5種類のテストについて10回程度実行し、time が表示する processor cycles の結果が最も良いものを採用した結果は以下の通り。

テスト関数 オペランド processor cycles 比率
function-test #'foo-function 1,068,515,087 1.000
functor-test1 #'foo-function 4,887,013,104 4.574
functor-test1 (new foo-functor) 5,932,309,691 5.552
functor-test2 #'foo-function 1,043,676,628 0.977
functor-test2 (new foo-functor) 1,282,054,753 1.200

 

まぁなんというか、以前と対して変わらない。直接 stl:functor-call するのはおおむね5倍遅いし、一度 stl:functor-function で関数を取得してから繰り返しコールする functor-test2 はまぁ関数の直接呼び出しと変わらない。この結果には安心していいと思うが、一方で満足してもいけない。特に、5倍ってのがなんとかならないかと思うし、それよりも気になるのはファンクタを自作する時の煩雑さだ。本当に、なんとかならないだろうか。

 

コメント

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

 

このページのタグ

Page tag : STLとその移植

Page tag : Common Lisp

 

 


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