Resulta que el rendimiento muy variable estaba relacionado con la recolección de basura. Cada llamada a la función sería más lenta hasta que se ejecutara una recolección de basura. Con el stock emacs, gc se ejecutaba cada dos segundos, pero tenía una línea en mi init.el para mejorar el tiempo de inicio que establecía gc-cons-umbral en 20 MB, y eso significaba que gc se ejecutaba con mucha menos frecuencia, lo que causaba puntos de referencia para informe un tiempo cada vez más lento hasta que se ejecute un gc después de un par de minutos, luego los tiempos caerían en picado y volverían a ser rápidos.
Después de volver al valor predeterminado gc-cons-threshhold, la evaluación comparativa se hizo más fácil.
Luego perfilé la memoria con el generador de perfiles incorporado ( M-x profiler-start
), y descubrí que las llamadas a syntax-ppss causaban la mayoría de las asignaciones, por lo que después de alguna optimización para llamar a syntax-ppss con menos frecuencia logré un rendimiento aceptable.
El uso de jit-lock-mode (agregar una función a través de jit-lock-register) parece ser la forma más fácil de lograr que el bloqueo de fuentes de varias líneas funcione de manera confiable, por lo que ese fue el método que elegí.
Editar: después de descubrir que el rendimiento aún no era lo suficientemente bueno en buffers muy grandes, pasé mucho tiempo optimizando el uso y la asignación de la CPU, midiendo las mejoras de rendimiento con el generador de perfiles Emacs incorporado ( M-x profiler-start
). Sin embargo, Emacs todavía tartamudearía y se colgaría al desplazarse rápidamente a través de buffers muy grandes. La eliminación de la función de bloqueo de jit con la que me registré jit-lock-register
eliminaría la tartamudez y los bloqueos, pero la creación de perfiles mostró que la función de bloqueo de jit se completa en alrededor de 8 ms, lo que debería ser lo suficientemente rápido para un desplazamiento suave. La eliminación de la llamada jit-lock-register
y, en su lugar, el uso de un emparejador de palabras clave de bloqueo de fuente regular resolvió el problema.
TLDR: Hacer esto fue lento y tartamudearía:
(defun my-font-lock-function (start end)
"Set faces for font-lock between START and END.")
(jit-lock-register 'my-font-lock-function)
Hacer esto fue rápido y no tartamudearía:
(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))))