Оказывается, что сильно отличающаяся производительность была связана со сборкой мусора. Каждый вызов функции будет выполняться медленнее, пока не будет запущена сборка мусора. В случае со стандартным 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))))