Common Lisp Компиляция и время выполнения - PullRequest
9 голосов
/ 05 марта 2012

У меня есть файл LISP, который делает много выборок, файлов ввода-вывода и арифметики в цикле. (Я делаю фильтрацию частиц в общем lisp.) Я компилирую свой файл lisp с помощью команды compile-file. Я также использую (declaim (optimize (speed 3) (debug 0) (safety 0))) в начале своего файла lisp, так как хочу, чтобы мои результаты были как можно быстрее.
Я использую (time (load "/....../myfile.lisp") и (time (load "/......./myfile.dx64fsl") для измерения скорости. Проблема в том, что компиляция не приносит мне никакого преимущества. Там нет улучшения. Я делаю что-то не так? Есть ли способ улучшить вещи? Скорость - самый важный критерий, поэтому я могу многим пожертвовать, чтобы получить быстрый ответ. Я понятия не имею об этом типе вопросов, поэтому любая помощь будет оценена.
Более того, когда я увеличиваю количество частиц (каждая частица является вектором размером ~ 40) до примерно 10000, код становится очень медленным, поэтому могут возникнуть некоторые проблемы с памятью.
Заранее большое спасибо.

edit: это результаты профилирования с 1000 частиц и 50 итераций.

(LOAD "/.../myfile.dx64fsl") took 77,488,810 microseconds (77.488810 seconds) to run 
                    with 8 available CPU cores.
During that period, 44,925,468 microseconds (44.925470 seconds) were spent in user mode
                    32,005,440 microseconds (32.005440 seconds) were spent in system mode
2,475,291 microseconds (2.475291 seconds) was spent in GC.
 1,701,028,429 bytes of memory allocated.
 1,974 minor page faults, 0 major page faults, 0 swaps.
; Warning: Function CREATE-MY-DBN has been redefined, so times may be inaccurate.
;          MONITOR it again to record calls to the new definition.
; While executing: MONITOR::MONITOR-INFO-VALUES, in process repl-thread(10).


                                                               Cons   
                             %      %                          Per      Total     Total
Function                    Time   Cons    Calls  Sec/Call     Call     Time      Cons
------------------------------------------------------------------------------------------
SAMPLE:                    25.61   26.14  2550000  0.000005      174    13.526   443040000
DISCRETE-PARENTS:          19.66    3.12  4896000  0.000002       11    10.384    52800000
LINEAR-GAUSSIAN-MEAN:       8.86    3.12  1632000  0.000003       32     4.679    52800000
DISCRETE-PARENT-VALUES:     7.47   12.33  3264000  0.000001       64     3.946   208896000
LIST-DIFFERENCE:            6.41   25.69  6528000  0.000001       67     3.384   435392000
CONTINUOUS-PARENTS:         6.33    0.00  1632000  0.000002        0     3.343           0
PF-STEP:                    5.17    0.23       48  0.056851    80080     2.729     3843840
CONTINUOUS-PARENT-VALUES:   4.13    7.20  1632000  0.000001       75     2.184   122048000
TABLE-LOOKUP:               3.85    8.39  2197000  0.000001       65     2.035   142128000
PHI-INVERSE:                3.36    0.00  1479000  0.000001        0     1.777           0
PHI-INTEGRAL:               3.32    1.38  2958000  0.000001        8     1.755    23344000
PARENT-VALUES:              2.38   10.65  1122000  0.000001      161     1.259   180528016
CONDITIONAL-PROBABILITY:    1.41    0.00   255000  0.000003        0     0.746           0
------------------------------------------------------------------------------------------
TOTAL:                     97.96   98.24  30145048                       51.746  1664819856
Estimated monitoring overhead: 21.11 seconds
Estimated total monitoring overhead: 23.93 seconds

с 10000 частицами и 50 итерациями:

(LOAD "/.../myfile.dx64fsl") took 809,931,702 microseconds (809.931700 seconds) to run 
                    with 8 available CPU cores.
During that period, 476,627,937 microseconds (476.627930 seconds) were spent in user mode
                    328,716,555 microseconds (328.716550 seconds) were spent in system mode
54,274,625 microseconds (54.274624 seconds) was spent in GC.
 16,973,590,588 bytes of memory allocated.
 10,447 minor page faults, 417 major page faults, 0 swaps.
; Warning: Funtion CREATE-MY-DBN has been redefined, so times may be inaccurate.
;          MONITOR it again to record calls to the new definition.
; While executing: MONITOR::MONITOR-INFO-VALUES, in process repl-thread(10).


                                                               Cons    
                             %      %                          Per       Total     Total
Function                    Time   Cons    Calls  Sec/Call     Call      Time      Cons
-------------------------------------------------------------------------------------------
SAMPLE:                    25.48   26.11  25500000  0.000006       174   144.211  4430400000
DISCRETE-PARENTS:          18.41    3.11  48960000  0.000002        11   104.179   528000000
LINEAR-GAUSSIAN-MEAN:       8.61    3.11  16320000  0.000003        32    48.751   528000000
LIST-DIFFERENCE:            7.57   25.66  65280000  0.000001        67    42.823  4353920000
DISCRETE-PARENT-VALUES:     7.50   12.31  32640000  0.000001        64    42.456  2088960000
CONTINUOUS-PARENTS:         5.83    0.00  16320000  0.000002         0    32.980           0
PF-STEP:                    5.05    0.23       48  0.595564    800080    28.587    38403840
TABLE-LOOKUP:               4.52    8.38  21970000  0.000001        65    25.608  1421280000
CONTINUOUS-PARENT-VALUES:   4.25    7.19  16320000  0.000001        75    24.041  1220480000
PHI-INTEGRAL:               3.15    1.38  29580000  0.000001         8    17.849   233440000
PHI-INVERSE:                3.12    0.00  14790000  0.000001         0    17.641           0
PARENT-VALUES:              2.87   10.64  11220000  0.000001       161    16.246  1805280000
CONDITIONAL-PROBABILITY:    1.36    0.00  2550000  0.000003         0     7.682           0
-------------------------------------------------------------------------------------------
TOTAL:                     97.71   98.12  301450048                       553.053  16648163840
Estimated monitoring overhead: 211.08 seconds
Estimated total monitoring overhead: 239.13 seconds

Ответы [ 5 ]

4 голосов
/ 05 марта 2012

Типичная арифметика в Common Lisp может быть медленной.Улучшение возможно, но требует немного знаний.

Причины:

  • Обычные числа Лиспа - это не то, что дает машина (bignums, рациональные, сложные, ...)
  • автоматическое изменение от fixnum к bignum и обратно
  • общие математические операции
  • тегирование использует биты размера слова
  • , состоящие из чисел

Одна вещь, которую вы можете видеть из результатов профилирования, это то, что вы генерируете 1,7 ГБ мусора.Это типичный намек на то, что ваши числовые операции минусы.Избавиться от этого часто не так просто.С моей стороны это только предположение, что это числовые операции.

Кен Андерсон (к сожалению, он умер несколько лет назад) на своем веб-сайте дает несколько советов по улучшению числового программного обеспечения: http://openmap.bbn.com/~kanderso/performance/

Обычным решением является передача кода опытному разработчику на Лиспе, который немного знает об используемом компиляторе и / или его оптимизации.

4 голосов
/ 05 марта 2012

Прежде всего, никогда никогда не объявляйте (speed 3) вместе с (safety 0) на верхнем уровне, т. Е. Глобально.Это рано или поздно вернется и откусит вам голову.На этих уровнях большинство распространенных lisp-компиляторов выполняют меньше проверок безопасности, чем C-компиляторы.Например, некоторые списки сбрасываются для сигналов прерывания в коде (safety 0).Далее (safety 0) очень редко дают заметные выгоды.Я бы объявил (speed 3)(safety 1)(debug 1) в «горячих» функциях, возможно, перейдя к (debug 0), если это принесет заметный выигрыш.

В противном случае, фактически не просматривая некоторый реальный код, довольно сложно выдвинуть предложения.Глядя на time (), кажется, что давление ГХ довольно высокое.Убедитесь, что вы используете арифметику с открытым кодом в горячих функциях, и не напрасно вставляйте плавающие числа или целые числа.Используйте (disassemble 'my-expensive-function), чтобы внимательно посмотреть код, сгенерированный компилятором.SBCL обеспечит много полезного вывода при компиляции с высоким приоритетом по скорости, и может быть целесообразно попытаться устранить некоторые из этих предупреждений.

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

2 голосов
/ 05 марта 2012

Если весь код, содержащийся в «myfile.lisp», - это те части, где вы выполняете вычисления, нет, компиляция этого файла заметно не улучшит ваше время выполнения.Разница между этими двумя случаями, вероятно, заключается в том, что «мы компилируем несколько циклов», вызывая функции, которые либо компилируются, либо интерпретируются в обоих случаях.

Чтобы получить улучшение от компиляции, вам также необходимо скомпилировать кодэто называется.Вам также может потребоваться ввести аннотации в коде, чтобы компилятор мог оптимизировать работу.SBCL имеет неплохую диагностику компилятора для пропущенных аннотаций (до такой степени, что люди жалуются, что он слишком многословен во время компиляции).

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

1 голос
/ 05 марта 2012
Welcome to Clozure Common Lisp Version 1.7-r14925M  (DarwinX8664)!
? (inspect 'print)
[0]     PRINT
[1]     Type: SYMBOL
[2]     Class: #<BUILT-IN-CLASS SYMBOL>
        Function
[3]     EXTERNAL in package: #<Package "COMMON-LISP">
[4]     Print name: "PRINT"
[5]     Value: #<Unbound>
[6]     Function: #<Compiled-function PRINT #x30000011C9DF>
[7]     Arglist: (CCL::OBJECT &OPTIONAL STREAM)
[8]     Plist: NIL

Inspect> (defun test (x) (+ x 1))
TEST
Inspect> (inspect 'test)
[0]     TEST
[1]     Type: SYMBOL
[2]     Class: #<BUILT-IN-CLASS SYMBOL>
        Function
[3]     INTERNAL in package: #<Package "COMMON-LISP-USER">
[4]     Print name: "TEST"
[5]     Value: #<Unbound>
[6]     Function: #<Compiled-function TEST #x302000C5EFFF>
[7]     Arglist: (X)
[8]     Plist: NIL
Inspect> 

Обратите внимание, что # 'print и #' test указаны как 'compiled'.Это означает, что единственная разница в производительности между загрузкой файла .lisp и загрузкой скомпилированного файла - это время компиляции.То, что я предполагаю, не является узким местом в вашем сценарии.Обычно это не так, если только вы не используете кучу макросов, и выполнение преобразования кода является основной целью вашей программы.

Это одна из основных причин, по которой я никогда не имею дело со скомпилированными файлами LISP.,Я просто загружаю все общие библиотеки / пакеты, которые мне нужны, в свой основной файл, а затем загружаю несколько конкретных .lisp функций / файлов поверх этого, когда я работаю над конкретным проектом.И, по крайней мере, для SBCL и CCL для меня все указано как «скомпилированное».

1 голос
/ 05 марта 2012

Несколько баллов:

  1. Попробуйте вывести файловый ввод-вывод из цикла, если это возможно; читать данные в память в пакетном режиме до итерации. Файловый ввод / вывод на несколько порядков медленнее, чем доступ к памяти.

  2. Попробуйте SBCL , если скорость выполнения важна для вас.

  3. Десятикратное увеличение ваших входных данных приводит к примерно десятикратному увеличению времени выполнения, которое является линейным, поэтому ваш алгоритм кажется нормальным; просто нужно работать над своим постоянным фактором.

  4. Воспользуйтесь преимуществами рабочего процесса Lisp: функция редактирования, функция компиляции и тестовый прогон вместо файла редактирования, файла компиляции и теста. Разница станет заметной, когда ваши проекты станут больше (или когда вы попробуете SBCL, что займет больше времени на анализ / оптимизацию ваших программ для получения более быстрого кода).

...