Acontece que o desempenho bastante variado estava relacionado à coleta de lixo. Cada chamada para a função ficaria mais lenta até que uma coleta de lixo fosse executada. Com o estoque emacs, o gc era executado a cada dois segundos, mas eu tinha uma linha no init.el para melhorar o tempo de inicialização que definia o limite de gc-cons-20 para 20 MB, e isso significava que o gc era executado com muito menos frequência, fazendo com que os benchmarks relatar um tempo cada vez mais lento até que um gc fosse executado depois de alguns minutos; os tempos despencariam e seriam rápidos novamente.
Após reverter para o padrão gc-cons-threshhold, o benchmarking ficou mais fácil.
Em seguida, criei um perfil de memória com o profiler interno ( M-x profiler-start
) e descobri que as chamadas para syntax-ppss causavam mais alocações; portanto, após alguma otimização para chamar syntax-ppss com menos frequência, obtive um desempenho aceitável.
Usar o modo jit-lock (adicionar uma função via jit-lock-register) parece ser a maneira mais fácil de fazer com que o bloqueio de fontes de várias linhas funcione de maneira confiável, e esse foi o método que eu escolhi.
Edit: Depois de descobrir que o desempenho ainda não era bom o suficiente em buffers muito grandes, passei muito tempo otimizando o uso e a alocação da CPU, medindo as melhorias de desempenho com o criador de perfil Emacs ( M-x profiler-start
). No entanto, o Emacs ainda gagueja e trava ao rolar rapidamente por buffers muito grandes. A remoção da função jit-lock com a qual eu me registrei jit-lock-register
removeria a gagueira e o travamento, mas o perfil mostrou que a função jit-lock é concluída em cerca de 8 ms, o que deve ser rápido o suficiente para uma rolagem suave. A remoção da chamada jit-lock-register
e, em vez disso, o uso de um correspondente regular de bloqueio de fonte-palavras-chave resolveu o problema.
TLDR: Fazer isso era lento e gaguejava:
(defun my-font-lock-function (start end)
"Set faces for font-lock between START and END.")
(jit-lock-register 'my-font-lock-function)
Fazer isso era rápido e não gaguejava:
(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))))