LINUX.ORG.RU

[lisp]использование compiler-macro

 


0

0

Нужны функции, выполняющие арифметические операции как над числами, так и над последовательностями. Определяю так:
(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 раза больше, чем явное использование #'+
Есть ли, какой-нибудь способ подсказать компилятору по возможности использовать стандартные арифметические функции вместо генерик-функции, не меняя при этом код вызова?

anonymous

Для начала, какому конкретно компилятору?

mv ★★★★★
()

1. Не понятно, чем стандартный #'+ не угодил? Он тоже умеет складывать больше двух чисел.

2. CLOS-программа практически не оптимизируется, потому что CLOS очень гибкий, и какой-нибудь инвариант там усмотреть и соптимизировать сложно. Если ООП не нужно, лучше не используй его.

mv ★★★★★
()
Ответ на: комментарий от mv

цель была - написать универсальную функцию суммирования - для чисел, массивов, списков, многомерных массивов.
Определение через методы с этим справляется - но скорость при сложение простых чисел падает на порядки по сравнению с встроенной..
Оператотные функцции и шаблоны в С++ дают требуемый функционал

anonymous
()
Ответ на: комментарий от anonymous

Ну так и используй C++. На чисто расчётных задачах компилятор, выдающий статический код, будет всегда быстрее.

mv ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.