Як виміряти продуктивність коду elisp?


26

Як я міряю ефективність свого коду elisp? Які інструменти / зовнішні пакети мені доступні для вимірювання часу?

Крім загального часу, чи можу я бачити профіль, який показує час, відведений на функцію? Чи можна також профілювати використання пам'яті?


1
Питання занадто широке. Який виступ? Де? Коли? " Продуктивність Emacs " може означати все і все.
Дрю

@Drew Багато інших мов програмування мають набір орієнтирів (наприклад, Python: speed.pypy.org , JS: Sunspider тощо), і я сподівався, що для інтерпретатора Elisp є еквівалент.
Вільфред Х'юз

Бенчмаркінг, такий як функція benchmarkта профілер, не вимірює продуктивність Emacs . Він вимірює ефективність, оцінюючи конкретні вирази. Це корисно для порівняння виступів у Emacs. Для вимірювання продуктивності самого Emacs вам потрібно порівняти його з продуктивністю чогось іншого, ніж Emacs. І ось тут розпочинається широта Емака. Ви можете виміряти Emacs проти XYZ для того чи іншого, але для вимірювання продуктивності Emacs в цілому вам знадобиться незрозуміло таких порівнянь.
Дрю

Можливо, ви мали на увазі " Як я вимірюю ефективність роботи в Emacs "?
Дрю

2
Гаразд, я відкрив emacs.stackexchange.com/q/655/304, щоб говорити про тестування Emacs, і переосмислив це питання про тестування / профілювання програм elisp.
Вільфред Х'юз

Відповіді:


31

Орієнтир

Найпростіший варіант - це вбудований benchmarkпакет. Його використання надзвичайно просте:

(benchmark 100 (form (to be evaluated)))

Він завантажується автоматично, тому навіть не потрібно цього вимагати.

Профілювання

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

  1. Почніть з цього M-x profiler-start.
  2. Зробіть кілька трудомістких операцій.
  3. Отримайте звіт за допомогою M-x profiler-report.

Вас слід перенести в буфер із навігаційним деревом викликів функцій.
Скріншот Profiler


benchmarkФункція, здається, не працює: коли я працюю всередині відкритого .cфайлу (benchmark 100 (c-font-lock-fontify-region 0 17355)), я продовжую отримувати void-function jit-lock-bounds.
Привіт-Ангел

1
FTR: в якості альтернативи benchmarkє функції benchmark-runі benchmark-run-compiled. Для мене головна відмінність полягала в тому, що обидві функції насправді працюють (див. Попередній коментар) : Ь
Привіт-Ангел

14

На додаток до відповіді @ Malabara, я схильний використовувати with-timerмакрос на замовлення, щоб постійно вводити різні частини мого коду (наприклад, мій init.elфайл).

Різниця полягає в тому, що, хоча benchmarkдозволяє вивчити продуктивність певного біта коду, який ви інструментуєте, with-timerзавжди дає вам час, витрачений у кожній інструментальній частині коду (без особливих накладних витрат на досить великі частини), що дає вам інформацію яку частину слід дослідити далі.

(defmacro with-timer (title &rest forms)
  "Run the given FORMS, counting the elapsed time.
A message including the given TITLE and the corresponding elapsed
time is displayed."
  (declare (indent 1))
  (let ((nowvar (make-symbol "now"))
        (body   `(progn ,@forms)))
    `(let ((,nowvar (current-time)))
       (message "%s..." ,title)
       (prog1 ,body
         (let ((elapsed
                (float-time (time-subtract (current-time) ,nowvar))))
           (message "%s... done (%.3fs)" ,title elapsed))))))

Приклад використання:

(with-timer "Doing things"
  (form (to (be evaluated))))

даючи наступний вихід у *Messages*буфері:

Doing things... done (0.047s)

Варто зазначити, що це максимум натхнення макросом Джона Віглі use-package-with-elapsed-timerв його відмінному use-packageрозширенні.


Якщо ви вимірюєте init.el, вам, мабуть, буде цікавий стартовий профайлер emacs .
Вільфред Х'юз

Макроси - приголомшливі. Це заслуговує на більше голосів.
Малабарба

2
Emacs записує загальний час init. Ви можете показати це за допомогою команди emacs-init-time.
Джо

1
@WilfredHughes так, я використовую esupі мені це подобається. Але ще раз, інтерес до такої речі, як with-timerне стільки до того, щоб профайлювати щось ретельно. Справжній інтерес полягає в тому, що ви завжди маєте інформацію про профілювання. Щоразу, коли я запускаю emacs, у моєму *Messages*буфері є ряд ліній, які підказують, яка частина займала скільки часу. Якщо я виявляю щось ненормальне, я можу використовувати будь-який з більш адекватних інструментів для профілю та оптимізації речей.
ffevotte

@JoeS Так, emacs-init-timeце дає цікаву інформацію. Однак це дає лише інклюзивний минулий час, без можливості розбивати окремі частини ініціалізації.
ffevotte

3

На додаток до відповіді @ Malabarba, зауважте, що ви можете виміряти складений час виконання коду benchmark-run-compiled. Цей показник часто набагато релевантніший за інтерпретований час виконання M-x benchmark:

ELISP> (benchmark-run (cl-loop for i below (* 1000 1000) sum i))
(0.79330082 6 0.2081620540000002)

ELISP> (benchmark-run-compiled (cl-loop for i below (* 1000 1000) sum i))
(0.047896284 0 0.0)

Три числа - загальний минулий час, кількість запущених GC та час, проведений у GC.


1

Бенчмаркінг - це не лише отримання чисел, а й прийняття рішень на основі аналізу результатів.

На MELPA є пакет benchstat.el, який можна використовувати для отримання функцій, передбачених програмою benstat .

Він реалізує порівняльний аналіз на основі порівняння, де ви вивчаєте Xвластивості продуктивності проти Y.

Функції Benchstat можна розглядати як benchmark-run-compiledобгортку, яка не тільки збирає інформацію, але і повертає її в легкому для читання форматі інтерпретації. Це включає:

  • Проміжок часу між Xі доY
  • Середній середній час
  • Сума виділень

Дуже простий приклад використання:

(require 'benchstat)

;; Decide how much repetitions is needed.
;; This is the same as `benchmark-run-compiled` REPETITIONS argument.
(defconst repetitions 1000000)

;; Collect old code profile.
(benchstat-run :old repetitions (list 1 2))
;; Collect new code profile.
(benchstat-run :new repetitions (cons 1 2))

;; Display the results.
;; Can be run interactively by `M-x benchstat-compare'.
(benchstat-compare)

Результати benchstat-compareвідображаються у тимчасовому буфері:

name   old time/op    new time/op    delta
Emacs    44.2ms ± 6%    25.0ms ±15%  -43.38%  (p=0.000 n=10+10)

name   old allocs/op  new allocs/op  delta
Emacs      23.0 ± 0%      11.4 ± 5%  -50.43%  (p=0.000 n=10+10)

Вам знадобиться benchstatпрограмний двійковий файл. Якщо ви використовували мову програмування Go, швидше за все, вона вже є у вашій системі. В іншому випадку є можливість скласти його з джерел.

Попередньо складений двійковий файл для Linux / amd64 можна знайти на сторінці випуску github .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.