Сравнение скорости выполнения кода
Не всегда, но бывает необходимо проверить – какой вариант функции работает быстрее. Здесь мы и рассмотрим уже готовый вариант.
Но перед рассмотрением: какой вариант функции будет работать быстрее?
(mapcar '1+ '(1 2 3))
); _end of defun
или
(mapcar (function 1+) '(1 2 3))
); _end of defun
А если сравнивать компилированные коды? А если сравнивать компилированный и некомпилированный код? И подобных “если” может быть несколько десятков.
Сравнение быстродействия функций выполняется отдельным кодом, который мне показал Евгений Елпанов (код залит сюда; а здесь – вариант сразу для сохранения):
;;;=================================================================
;;;
;;; Benchmark.lsp | © 2005 Michael Puckett | All Rights Reserved
;;;
;;;=================================================================
;;;
;;; Purpose:
;;;
;;; Compare the performance of various statements.
;;;
;;; Notes:
;;;
;;; I make no claims that this is definitive benchmarking. I
;;; wrote this utility for my own purposes and thought I'd
;;; share it. Many considerations go into evaluating the
;;; performance or suitability of an algorythm for a given
;;; task. Raw performance as profiled herein is just one.
;;;
;;; Please note that background dramatically affect results.
;;;
;;; Disclaimer:
;;;
;;; This program is flawed in one or more ways and is not fit
;;; for any particular purpose, stated or implied. Use at your
;;; own risk.
;;;
;;;=================================================================
;;;
;;; Syntax:
;;;
;;; (Benchmark statements)
;;;
;;; Where statements is a quoted list of statements.
;;;
;;;=================================================================
;;;
;;; Example:
;;;
;;; (BenchMark
;;; '(
;;; (1+ 1)
;;; (+ 1 1)
;;; (+ 1 1.0)
;;; (+ 1.0 1.0)
;;; )
;;; )
;;;
;;;=================================================================
;;;
;;; Output:
;;;
;;; Elapsed milliseconds / relative speed for 32768 iteration(s):
;;;
;;; (1+ 1)..........1969 / 1.09 <fastest>
;;; (+ 1 1).........2078 / 1.03
;;; (+ 1 1.0).......2125 / 1.01
;;; (+ 1.0 1.0).....2140 / 1.00 <slowest>
;;;
;;;=================================================================
(statements / _lset _rset _tostring _eval _princ _main)
;;;=================================================================
;;;
;;; (_LSet text len fillChar)
;;;
;;;=================================================================
(defun _lset (text len fillchar / padding result)
(setq
padding (list (ascii fillchar))
result (vl-string->list text)
) ;_ setq
(while
(< (length
(setq padding
(append padding padding)
) ;_ setq
) ;_ length
len
) ;_ <
) ;_ while
(while
(< (length
(setq result
(append result padding)
) ;_ setq
) ;_ length
len
) ;_ <
) ;_ while
(substr (vl-list->string result) 1 len)
) ;_ defun
;;;=================================================================
;;;
;;; (_RSet text len fillChar)
;;;
;;;=================================================================
(defun _rset (text len fillchar / padding result)
(setq
padding (list (ascii fillchar))
result (vl-string->list text)
) ;_ setq
(while
(< (length
(setq padding
(append padding padding)
) ;_ setq
) ;_ length
len
) ;_ <
) ;_ while
(while
(< (length
(setq result
(append padding result)
) ;_ setq
) ;_ length
len
) ;_ <
) ;_ while
(substr
(vl-list->string result)
(1+ (- (length result) len))
) ;_ substr
) ;_ defun
;;;=================================================================
;;;
;;; (_ToString x)
;;;
;;;=================================================================
(defun _tostring (x / result)
(if
(< (strlen
(setq result
(vl-prin1-to-string x)
) ;_ setq
) ;_ strlen
40
) ;_ <
result
(strcat (substr result 1 36) "..." (chr 41))
) ;_ if
) ;_ defun
;;;=================================================================
;;;
;;; (_Eval statement iterations)
;;;
;;;=================================================================
(defun _eval (statement iterations / start)
(gc)
(setq start (getvar "millisecs"))
(repeat iterations (eval statement))
(- (getvar "millisecs") start)
) ;_ defun
;;;=================================================================
;;;
;;; (_Princ x)
;;;
;;;=================================================================
(defun _princ (x)
(princ x)
(princ)
;;; forces screen update
) ;_ defun
;;;=================================================================
;;;
;;; (_Main statements)
;;;
;;;=================================================================
(defun _main
(statements / boundary iterations timings slowest fastest lsetlen rsetlen index count)
(setq
boundary 1000
iterations 1
) ;_ setq
(_princ "Benchmarking ...")
(while
(or
(< (apply 'max
(setq timings
(mapcar
'(lambda (statement)
(_eval statement iterations)
) ;_ lambda
statements
) ;_ mapcar
) ;_ setq
) ;_ apply
boundary
) ;_ <
(< (apply 'min timings)
boundary
) ;_ <
) ;_ or
(setq iterations
(* 2 iterations)
) ;_ setq
(_princ ".")
) ;_ while
(_princ
(strcat
"\rElapsed milliseconds / relative speed for "
(itoa iterations)
" iteration(s):\n\n"
) ;_ strcat
) ;_ _princ
(setq
slowest (float (apply 'max timings))
fastest (apply 'min timings)
) ;_ setq
(setq lsetlen
(+ 5
(apply 'max
(mapcar (function strlen)
(setq statements
(mapcar (function _tostring)
statements
) ;_ mapcar
) ;_ setq
) ;_ mapcar
) ;_ apply
) ;_ +
) ;_ setq
(setq rsetlen
(apply 'max
(mapcar
'(lambda (ms) (strlen (itoa ms)))
timings
) ;_ mapcar
) ;_ apply
) ;_ setq
(setq
index 0
count (length statements)
) ;_ setq
(foreach pair
(vl-sort
(mapcar 'cons statements timings)
'(lambda (a b) (< (cdr a) (cdr b)))
) ;_ vl-sort
((lambda (pair / ms)
(_princ
(strcat
" "
(_lset (car pair) lsetlen ".")
(_rset
(itoa (setq ms (cdr pair)))
rsetlen
"."
) ;_ _rset
" / "
(rtos (/ slowest ms) 2 2)
(cond
((eq 1 (setq index (1+ index))) " <fastest>")
((eq index count) " <slowest>")
("")
) ;_ cond
"\n"
) ;_ strcat
) ;_ _princ
) ;_ lambda
pair
)
) ;_ foreach
(princ)
) ;_ defun
;;;=================================================================
;;;
;;; Program is defined, let's rock and roll ...
;;;
;;;=================================================================
(_main statements)
) ;_ defun
Код запускается в консоли VLIDE.
Для нашего примера получаем:
Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s):
(TEST)......1219 / 1.27 <fastest>
(TEST1).....1547 / 1 <slowest>
Неужели описанная разница между function и ‘ неправда? Или просто использование function в настолько простом примере оказывается более медленным, чем ‘? Выполним компиляцию fas-файла из функций test и test1 (вопросы порядка и настроек компиляции пока не рассматриваем, оставив все “по умолчанию”) и повторно проверим быстродействие:
nil
Выполняем загрузку fas-файла и возвращаемся в VLIDE:
#<SUBR @0ab6c730 TEST>
_$ test1 ; аналогично, для test1
#<SUBR @0ab6c71c TEST1>
_$ (BENCHMARK '((test) (test1)))
Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s):
(TEST)......1187 / 1 <fastest>
(TEST1).....1187 / 1 <slowest>
_$
Хотя в данном случае benchmark и написал, что test1 медленнее, но обратите внимание: значения времени выполнения одинаковы.
Усложним функции:
(mapcar '(lambda (x) (sqrt (+ x 0.5))) '(1 2 3))
) ;_ end of defun
(defun test3 ()
(mapcar (function (lambda (x) (sqrt (+ x 0.5)))) '(1 2 3))
) ;_ end of defun
И проверим быстродействие некомпилированного кода:
Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s):
(TEST2).....1250 / 1.1 <fastest>
(TEST3).....1375 / 1 <slowest>
_$
Результат поражает, не правда ли? function опять медленнее ‘. Но опять же выполним компиляцию в fas, затем
nil
_$
Загружаем fas и повторно выполняем проверку:
Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s):
(TEST3).....1219 / 2.65 <fastest>
(TEST2).....3235 / 1 <slowest>
_$
Обратите внимание: быстродействие компилированного кода с использованием function выше скорости работы ‘ больше чем в 2,5 раза!
Одно “но”: подобные проверки крайне желательно выполнять несколько раз (а то и в разных сессиях AutoCAD’a). AutoCAD не всегда корректно и сразу “очищает” память от мусора, поэтому результаты могут немного различаться.
Материалы для проектирования, работы и разработки (и не только в AutoCAD)
Хотелось бы добавить, что не всегда необходимо такое тщательное тестирование сравнения скорости. Обычно, разница составляет довольно много и необходимо просто проверить время выполнения. Самой удобной функцией, для получения текущего времени, я считаю
(_vl-times)
Другими словами, замер времени выполнения программы будет выглядеть:
;;... здесь ваша тестовая функция ...
(princ (strcat "\n " (rtos (/ (- (car (_vl-times)) time) 1000.) 2 3) " sec."))
Стоит указать источник вышеописанной функции benchmark.lsp.
Просмотреть код, смогут только зарегистрированные на форуме…
http://www.theswamp.org/index.php?topic=3952.0;all
Евгений, если честно, то я попросту забыл источник. Прошу прощения.