[lisp]использование compiler-macro
От: Аноним  
Дата: 12.06.08 11:17
Оценка:
Нужны функции, выполняющие арифметические операции как над числами, так и над последовательностями. Определяю так:
(defgeneric opAdd (x y))
(defmethod opAdd ((x number) (y number))
  (+ x y))

(defmethod opAdd ((xs list) (y number))
  (mapcar (lambda (x) (+ x y)) xs))

(defmethod opAdd ((xs array) (y number))
  (map 'vector (lambda (x) (+ x y)) xs))

Но на простом тесте (opAdd число число) в 20 раз проигрывал простому суммированию:
(let ((xs (make-array 10000 :initial-element 1 :element-type 'number))
      (ys (make-array 10000 :initial-element 2 :element-type 'number)))
  (time (dotimes (i 1000)
      (map nil #'opAdd xs ys)))) ; и #'+ вместо #'opAdd

Причем, если добавить методы opAdd по работе с другими классами, то время выполнения теста немного увеличивается, т.е. получается, что компилятор, несмотря на явное указание типа элемента массива, просматривает все методы, соответствующие вызову opAdd.
Для того, чтобы повысить скорость, добавил compiler-макрос, который пытается подставить обычное суммирование вместо генерик-функции, если аргументы являются числами:
(define-compiler-macro opAdd (&whole form x y)
  (if (and (numberp x) (numberp y))
      `(+ ,x ,y)
      form))

Но на времени исполнения теста это никак не сказалось. Единственно, что получилось — явно указать использование compiler-макроса:
(let ((compose-form (funcall (compiler-macro-function 'opAdd)
                 '(opAdd x y)
                 nil)))
  (let ((opAdd (funcall (compile nil `(lambda () (defun opAdd (x y) ,compose-form))))))
    (let ((xs (make-list 10000 :initial-element 1))
      (ys (make-list 10000 :initial-element 2)))
      (time (dotimes (i 1000)
          (map nil #'opAdd xs ys))))))

Время исполнения этого теста уже только в 4 раза больше, чем явное использование #'+
Есть ли, какой-нибудь способ подсказать компилятору по возможности использовать стандартные арифметические функции вместо генерик-функции, не меняя при этом код вызова?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.