Сравнение скорости выполнения кода
Не всегда, но бывает необходимо проверить - какой вариант функции работает быстрее. Здесь мы и рассмотрим уже готовый вариант.
Но перед рассмотрением: какой вариант функции будет работать быстрее?
1 2 3 | (defun test() (mapcar '1+ '(1 2 3)) ); _end of defun |
или
1 2 3 | (defun test1() (mapcar (function 1+) '(1 2 3)) ); _end of defun |
А если сравнивать компилированные коды? А если сравнивать компилированный и некомпилированный код? И подобных "если" может быть несколько десятков.
Сравнение быстродействия функций выполняется отдельным кодом, который мне показал Евгений Елпанов (код залит сюда; а здесь - вариант сразу для сохранения):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | (defun benchmark ;;;================================================================= ;;; ;;; 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.
Для нашего примера получаем:
1 2 3 4 5 | _$ (benchmark '((test) (test1))) Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s): (TEST)......1219 / 1.27 <fastest> (TEST1).....1547 / 1 <slowest> |
Неужели описанная разница между function и ' неправда? Или просто использование function в настолько простом примере оказывается более медленным, чем '? Выполним компиляцию fas-файла из функций test и test1 (вопросы порядка и настроек компиляции пока не рассматриваем, оставив все "по умолчанию") и повторно проверим быстродействие:
1 2 | _$ (setq test nil test1 nil) nil |
Выполняем загрузку fas-файла и возвращаемся в VLIDE:
1 2 3 4 5 6 7 8 9 10 | _$ test ; проверяем, а загружена ли функция test #<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 медленнее, но обратите внимание: значения времени выполнения одинаковы.
Усложним функции:
1 2 3 4 5 6 7 | (defun test2 () (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 |
И проверим быстродействие некомпилированного кода:
1 2 3 4 5 6 | _$ (BENCHMARK '((test2) (test3))) Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s): (TEST2).....1250 / 1.1 <fastest> (TEST3).....1375 / 1 <slowest> _$ |
Результат поражает, не правда ли? function опять медленнее '. Но опять же выполним компиляцию в fas, затем
1 2 3 | _$ (setq test nil test1 nil test2 nil test3 nil) nil _$ |
Загружаем fas и повторно выполняем проверку:
1 2 3 4 5 6 | _$ (BENCHMARK '((test2) (test3))) Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s): (TEST3).....1219 / 2.65 <fastest> (TEST2).....3235 / 1 <slowest> _$ |
Обратите внимание: быстродействие компилированного кода с использованием function выше скорости работы ' больше чем в 2,5 раза!
Одно "но": подобные проверки крайне желательно выполнять несколько раз (а то и в разных сессиях AutoCAD'a). AutoCAD не всегда корректно и сразу "очищает" память от мусора, поэтому результаты могут немного различаться.
Хотелось бы добавить, что не всегда необходимо такое тщательное тестирование сравнения скорости. Обычно, разница составляет довольно много и необходимо просто проверить время выполнения. Самой удобной функцией, для получения текущего времени, я считаю
(_vl-times)
Другими словами, замер времени выполнения программы будет выглядеть:
2
3
;;... здесь ваша тестовая функция ...
(princ (strcat "\n " (rtos (/ (- (car (_vl-times)) time) 1000.) 2 3) " sec."))
Стоит указать источник вышеописанной функции benchmark.lsp.
Просмотреть код, смогут только зарегистрированные на форуме...
http://www.theswamp.org/index.php?topic=3952.0;all
Евгений, если честно, то я попросту забыл источник. Прошу прощения.