最適化メモ。
関数をバイトコンパイル時に最適化するには、シンボルに compiler::optimize-form 属性を設定する。マクロを書く要領で optimizer を与えるとコンパイル前にフォームが optimizer に渡され、戻り値がコンパイルされる。バイトコードを直接いじることはたぶんできない。
例。まずはテスト用の関数を定義。
(defun add (x y) (+ x y)) (defun test1 () (dotimes (_ 100000) (add 1 1))) (defun test2 () (dotimes (_ 100000) (+ 1 1))) (time (test1)) => 5079 (time (test2)) => 4687
time は実行時間(ms)を計測するマクロ。あらかじめ適当に定義しておく。コンパイルしなければこんなものか。
(progn (compile 'test1) (compile 'test2)) (time (test1)) => 532 (time (test2)) => 16
コンパイルしたらはっきりと差が出た。add をインライン展開してみる。
(setf (get 'add 'compiler::optimize-form) #'(lambda (x) `(+ ,@(cdr x)))) (defun test1 () (dotimes (_ 100000) (add 1 1))) (compile 'test1) (time (test1)) => 16
めでたく test2 と同じ実行時間に。
macroexpand-all が間違っていたことに今更ながら気付いたので直してみた。やっぱりいろいろいい加減。
(defun macroexpand-all (x)
(if (atom x) (return-from macroexpand-all x))
(setq x (macroexpand x))
(case (car x)
((interactive save-restriction save-excursion save-window-excursion
si:*byte-code unwind-protect progn tagbody if block catch
multiple-value-call multiple-value-prog1 setq)
(list* (car x) (mapcar 'macroexpand-all (cdr x))))
((let let*)
(list* (car x)
(mapcar (lambda (b)
(if (atom b) b
(list (car b) (macroexpand-all (cadr b)))))
(cadr x))
(mapcar 'macroexpand-all (cddr x))))
((macrolet flet labels)
(list* (car x)
(mapcar (lambda (b)
(list* (car b) (cadr b)
(mapcar 'macroexpand-all (cddr b))))
(cadr x))
(mapcar 'macroexpand-all (cddr x))))
((throw return-from)
(if (cddr x)
(list (car x) (cadr x)
(macroexpand-all (caddr x)))
x))
((multiple-value-setq)
(list 'multiple-value-setq (cadr x) (macroexpand-all (caddr x))))
((multiple-value-bind eval-when)
(cons (car x) (mapcar 'macroexpand-all (cdr x))))
((function quote go)
x)
(t
(cons (car x) (mapcar 'macroexpand-all (cdr x))))))
macroexpand-all を書いてみた。かなり投げやりな感じがしなくもないが、たぶんこれでいいと思う。
(defun macroexpand-all (x) (if (atom x) (return-from macroexpand-all x)) (setq x (macroexpand x)) (cons (car x) (mapcar 'macroexpand-all (cdr x))))