Сравнение скорости выполнения кода

Не всегда, но бывает необходимо проверить - какой вариант функции работает быстрее. Здесь мы и рассмотрим уже готовый вариант.

Но перед рассмотрением: какой вариант функции будет работать быстрее?

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 не всегда корректно и сразу "очищает" память от мусора, поэтому результаты могут немного различаться.

Размещено в Код LISP, Функции LISP · Метки: ,



Комментарии

Есть 4 коммент. к “Сравнение скорости выполнения кода”
  1. ElpanovEvgeniy пишет:

    Хотелось бы добавить, что не всегда необходимо такое тщательное тестирование сравнения скорости. Обычно, разница составляет довольно много и необходимо просто проверить время выполнения. Самой удобной функцией, для получения текущего времени, я считаю
    (_vl-times)
    Другими словами, замер времени выполнения программы будет выглядеть:

    1
    2
    3
    (setq time (car (_vl-times)))
    ;;... здесь ваша тестовая функция ...
    (princ (strcat "\n " (rtos (/ (- (car (_vl-times)) time) 1000.) 2 3) " sec."))
  2. ElpanovEvgeniy пишет:

    Стоит указать источник вышеописанной функции benchmark.lsp.
    Просмотреть код, смогут только зарегистрированные на форуме...

    http://www.theswamp.org/index.php?topic=3952.0;all

  3. Кулик Алексей aka kpblc пишет:

    Евгений, если честно, то я попросту забыл источник. Прошу прощения.

Трэкбэки

Узнайте, что другие говорят про эту заметку...
  1. [...] слоя). Сравним скорость работы по методике, описанной здесь (предупреждаю сразу – время проверки весьма [...]



Поделитесь своим мнением


Я не робот.