Оптимизация производительности блокировки шрифтов

13

Я хочу выполнить вариант привязки привязанного шрифта. У меня есть определения функций, которые начинаются со списка имен, и я хочу, чтобы эти имена были выделены внутри тела функции.

Я создал функцию, которая делает это, и зарегистрировал ее как функцию jit-lock с помощью jit-lock-register, однако производительность довольно низкая и задержки прокрутки в больших файлах.

  • Как я могу измерить производительность? Если я просто вызываю свою функцию для большого файла (со временем до и после или с elp), я получаю сильно изменяющуюся производительность, это занимает от 0,65 до 12 секунд. Есть ли рекомендуемый способ оценки производительности блокировки шрифтов?
  • Есть ли разница в производительности между привязанным сопоставлением, определенным в font-lock-keyword, и добавлением функции через jit-lock-register?

Редактировать: Кажется, что изменчивость в производительности связана с сборкой мусора, вызовы моей функции jit-lock последовательно замедляются с каждым вызовом до тех пор, пока сборка мусора не будет запущена, после чего они снова начнут работать быстро.

Йоаким Хорсман
источник
Для первого пункта попробуйте профилировщик.
Малабарба
Я могу (и использовал) профилировщик, чтобы увидеть, какие части моего кода требуют времени, но поскольку производительность настолько непоследовательна, трудно сказать, являются ли какие-либо изменения, которые я делаю, улучшением или нет.
Иоаким Хорсман
У вас есть код, который мы можем протестировать? Это может нам очень помочь.
PythonNut
1
Хотя речь идет не о профилировании или микрооптимизации, как таковой: я обнаружил, что пакет font-lock-studio является еще одним полезным инструментом для понимания производительности блокировки шрифтов. Он может помочь так же, как и любой другой интерактивный пошаговый отладчик - вы можете обнаружить, что пути выполнения не соответствуют вашим ожиданиям, и это основная проблема с производительностью.
Грег Хендершотт
Спасибо за совет о font-lock-studio, это здорово! Не помогает с функциями jit-lock, но, конечно, помогает со всем остальным.
Йоаким Хорсман

Ответы:

8

Оказывается, что сильно отличающаяся производительность была связана со сборкой мусора. Каждый вызов функции будет выполняться медленнее, пока не будет запущена сборка мусора. В случае со стандартным emacs gc запускался каждые пару секунд, но у меня в init.el была строка, чтобы улучшить время запуска, которое установило для gc-cons-threshold 20 МБ, и это означало, что gc запускался гораздо реже, вызывая тесты сообщайте все медленнее и медленнее, пока gc не будет запущен через пару минут, тогда времена резко упадут и снова будут быстрыми.

После возврата к стандартному gc-cons-threshhold тестирование стало проще.

Затем я выполнил профилирование памяти с помощью встроенного profiler ( M-x profiler-start) и обнаружил, что вызовы syntax-ppss вызывают наибольшее количество выделений, поэтому после некоторой оптимизации для вызова syntax-ppss реже я достиг приемлемой производительности.

Использование jit-lock-mode (добавление функции через jit-lock-register) кажется наиболее простым способом обеспечить надежную работу многострочной блокировки шрифтов, поэтому я выбрал этот метод.

Изменить: Обнаружив, что производительность все еще недостаточно хороша для очень больших буферов, я потратил много времени на оптимизацию использования и распределения ресурсов процессора, измеряя улучшения производительности с помощью встроенного в Emacs profiler ( M-x profiler-start). Однако Emacs по-прежнему заикается и зависает при быстрой прокрутке очень больших буферов. Удаление функции jit-lock, с которой я зарегистрировался jit-lock-register, устранит заикания и зависания, но профилирование показало, что функция jit-lock завершится примерно за 8 мс, что должно быть достаточно быстро для плавной прокрутки. Удаление вызова jit-lock-registerи использование обычного сопоставления font-lock-Keywords решило проблему.

TLDR: Делать это было медленно и заикаться:

(defun my-font-lock-function (start end)
"Set faces for font-lock between START and END.")

(jit-lock-register 'my-font-lock-function)

Делать это было быстро и не заикалось

(defun my-font-lock-function (start end)
"Set faces for font-lock between START and END.")

(defun my-font-lock-matcher (limit)
    (my-font-lock-function (point) limit)
   nil)

(setq font-lock-defaults
  (list 
     ...
    ;; Note that the face specified here doesn't matter since
    ;; my-font-lock-matcher always returns nil and sets the face on
    ;; its own.
    `(my-font-lock-matcher (1 font-lock-keyword-face nil))))
Йоаким Хорсман
источник
Не могли бы вы поделиться кодом, который вы использовали? Ваше решение может помочь другим, желающим достичь того же.
Мануэль Уберти
Я на самом деле не использовал какой-либо конкретный код, я просто назвал синтаксис-ppss меньше. Вы можете проверить этот код здесь: bitbucket.org/harsman/dyalog-mode/src/… Ищите dyalog-fontify-locals.
Йоаким Хорсман
Я думаю, dyalog-fontify-locals-matcherдолжно быть, my-font-lock-matcherи один из endдолжен быть limit. Во всяком случае, действительно интересное открытие!
Lindydancer
@Lindydancer: Да, спасибо. Исправлена.
Йоаким Хорсман
1
Re:, gc-cons-thresholdесли вы работаете с внутренними ценностями исключительно для того, чтобы улучшить время запуска, я предлагаю вам использовать emacs-startup-hookдля их восстановления впоследствии.
Филс