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

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

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

(defun test()
  (mapcar '1+ '(1 2 3))
  ); _end of defun

или

(defun test1()
  (mapcar (function 1+) '(1 2 3))
  ); _end of defun

А если сравнивать компилированные коды? А если сравнивать компилированный и некомпилированный код? И подобных “если” может быть несколько десятков.
Сравнение быстродействия функций выполняется отдельным кодом, который мне показал Евгений Елпанов (код залит сюда; а здесь – вариант сразу для сохранения):

(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.
Для нашего примера получаем:

_$ (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 (вопросы порядка и настроек компиляции пока не рассматриваем, оставив все “по умолчанию”) и повторно проверим быстродействие:

_$ (setq test nil test1 nil)
nil

Выполняем загрузку fas-файла и возвращаемся в VLIDE:

_$ 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 медленнее, но обратите внимание: значения времени выполнения одинаковы.
Усложним функции:

(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

И проверим быстродействие некомпилированного кода:

_$ (BENCHMARK '((test2) (test3)))
Benchmarking .................Elapsed milliseconds / relative speed for 16384 iteration(s):

    (TEST2).....1250 / 1.1 <fastest>
    (TEST3).....1375 / 1 <slowest>
_$

Результат поражает, не правда ли? function опять медленнее ‘. Но опять же выполним компиляцию в fas, затем

_$ (setq test nil test1 nil test2 nil test3 nil)
nil
_$

Загружаем fas и повторно выполняем проверку:

_$ (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)
    Другими словами, замер времени выполнения программы будет выглядеть:

    (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. [...] слоя). Сравним скорость работы по методике, описанной здесь (предупреждаю сразу – время проверки весьма [...]



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


Я не робот.