Как заставить SBCL оптимизировать возможный вызов FDEFINITION? - PullRequest
1 голос
/ 19 октября 2019

Извинения: у меня нет достаточных знаний, чтобы переделать это в простой для понимания фрагмент кода.

Я использовал примечания компилятора SBCL в качестве знака того, что может быть улучшено, но я в порядкеиз моей глубины с этим -

; compiling (DEFUN EXECUTE-PARALLEL ...)
; file: /home/dunham/8000-benchmarksgame/bench/spectralnorm/spectralnorm.sbcl-8.sbcl
; in: DEFUN EXECUTE-PARALLEL
;     (FUNCALL FUNCTION START END)
; --> SB-C::%FUNCALL THE 
; ==>
;   (SB-KERNEL:%COERCE-CALLABLE-FOR-CALL FUNCTION)
; 
; note: unable to
;   optimize away possible call to FDEFINITION at runtime
; because:
;   FUNCTION is not known to be a function

-

#+sb-thread
(defun execute-parallel (start end function)
  (declare (type int31 start end))
  (let* ((num-threads 4))
    (loop with step = (truncate (- end start) num-threads)
          for index from start below end by step
          collecting (let ((start index)
                           (end (min end (+ index step))))
                       (sb-thread:make-thread
                        (lambda () (funcall function start end))))
          into threads
          finally (mapcar #'sb-thread:join-thread threads))))

#-sb-thread
(defun execute-parallel (start end function )
  (funcall function start end))

(Программа здесь . Измерения для аналогичных программ здесь .)

Практично ли заставить SBCL "оптимизировать возможный вызов FDEFINITION" или компилятор отмечает объяснение, а не возможность?

Ответы [ 3 ]

3 голосов
/ 19 октября 2019

Причина возможного вызова fdefinition заключается в том, что он не знает, что function является функцией: это может быть имя единицы: в общем случае это может быть обозначение функции а не функция. Чтобы компилятор молчал, объясните ему, что это функция с подходящим объявлением типа, которое (declare (type function function)): вам просто нужно объявить, что его тип function).

Райнер прав:Существует вероятность того, что это когда-либо будет проблемой производительности, если вы начинаете новый поток. В частности, весьма вероятно, что добавление объявления не будет иметь никакого значения вообще:

  • без объявления, вызов funcall будет скомпилирован как что-то вроде 'проверьте тип объекта: еслиэто функция, вызовите ее;если это не так, вызовите fdefinition для него и вызовите результат; ';
  • с объявлением, тогда общая функция выглядит так:' проверьте, что объект является функцией, сигнализируя об ошибке, если нет ... вызовфункция '.

В обоих случаях, если объект является функцией, существует одна проверка типа и один вызов: проверка типа находится просто в другом месте. В первом случае код все равно будет работать, если объект является просто именем функции, тогда как с проверкой типа это не будет.

И в обоих этих случаях это код, в котором вы заботитесь о вызовеmake-thread: если это что-то вроде столь же быстрое, как и вызов функции, даже через fdefinition, я был бы очень впечатлен системой потоков! Почти наверняка производительность этой функции полностью зависит от затрат на создание потоков.

2 голосов
/ 19 октября 2019

В реальном коде избегайте подобных оптимизаций - если это действительно не нужно

Практично ли заставить SBCL "оптимизировать возможный вызов FDEFINITION" или компилятор отмечает, чтообъяснение, а не возможность?

Как правило, это не имеет значения, тем более что большая часть кода на Лиспе не должна компилироваться с оптимизацией (speed 3) (safety 0) (space 0), поскольку она может открыть программное обеспечениеошибки во время выполнения и сбои в зависимости от реализации и используемой программы. Вызывать вещи без проверки (без safety), кроме функций или функций именования символов, через funcall может быть достаточно опасно, чтобы вызвать сбой программы.

Для конкретного теста можно проверить с помощью таймингов, является ли типобъявление и специализированная fdefinition компиляция дают любое преимущество.

объявление типа

Объявление типа, чтобы прояснить, что переменная с именем fn ссылается на объекттипа function будет иметь значение:

(declare (type function fn))

в конкретной программе тестирования FDEFINITION в любом случае не будет вызываться

В приведенном вами примере fdefinition не будет вызван в любом случае.

(setf foo (lambda (x) x))       ; foo references a function object

(funcall foo 3)

funcall, вероятно, реализован примерно так:

(etypecase f
  ((or cons symbol) (funcall (fdefinition f) ...))
  (function         ...))

Поскольку ваш код передает функциональный объект, никогда не требуется вызывать fdefinition.

Преимущество оптимизации заключается в том, что диспетчеризация типа времени выполнения может быть удалена, а мертвый код для аргумента "против" или символа ...

1 голос
/ 21 октября 2019

Вы задаете вопрос об удалении определения fdefinition, но на самом деле ваш вопрос основан на предпосылке, что примечания sbcl являются хорошим способом стимулирования оптимизации и улучшений. Заметки - хороший способ выявить очевидные проблемы и места, где могут помочь объявления типов. Они не говорят вам, что на самом деле делает вашу программу медленной. Правильный способ улучшить производительность программы: 1. Подумайте, есть ли более быстрый алгоритм, и 2. Измерьте его производительность и определите, что медленное.

Один вызов fdefinition будет иметь значение, только если онпроисходит в узком цикле (т. е. он не одиночный, а очень множественный)

В этом случае случается запуск потока. Если вы запускаете потоки в замкнутом цикле, тогда ваша проблема производительности связана с запуском потоков в узком цикле. Не делайте этого.

Если вы не запускаете потоки в узком цикле (глядя на ваш код, кажется, что вы этого не делаете), есть более крупная рыба, которую нужно жарить. Зачем тратить время на fdefinition, который может вызываться 4 раза за вызов execute-parallel, когда вместо этого можно оптимизировать внутреннюю функцию.

...