Optimisation des performances de verrouillage des polices

13

Je souhaite effectuer une variante de correspondance de verrouillage de police ancrée. J'ai des définitions de fonctions qui commencent par une liste de noms, et je veux que ces noms soient mis en évidence dans le corps de la fonction.

J'ai créé une fonction qui fait cela et je l'ai enregistrée en tant que fonction jit-lock avec jit-lock-register, cependant, les performances sont assez médiocres et le défilement traîne dans les fichiers plus gros.

  • Comment puis-je mesurer les performances? Si je viens d'appeler ma fonction sur un gros fichier (avec float-time avant et après ou avec elp), j'obtiens des performances très variables, cela prend de 0,65 à 12 secondes. Existe-t-il un moyen recommandé de comparer les performances de verrouillage des polices?
  • Existe-t-il une différence de performances entre un matcher ancré défini dans font-lock-keywords et l'ajout d'une fonction via jit-lock-register?

Edit: Il semble que la variabilité des performances soit liée au garbage collection, les invocations de ma fonction jit-lock deviennent successivement plus lentes à chaque appel jusqu'à ce que le garbage collection soit exécuté, moment auquel elles redeviennent rapides.

Joakim Hårsman
la source
Pour le premier élément, essayez le profileur.
Malabarba
Je peux (et j'ai déjà) utilisé le profileur pour voir quelles parties de mon code prennent du temps, mais comme les performances sont tellement incohérentes, il est difficile de dire si les modifications que j'apporte sont une amélioration ou non.
Joakim Hårsman
Avez-vous du code que nous pouvons tester? Cela pourrait nous aider beaucoup.
PythonNut
1
Bien qu'il ne s'agisse pas de profilage ou de micro-optimisations, en soi: j'ai trouvé que le package font-lock-studio était un autre outil utile pour comprendre les performances du verrouillage de police. Cela peut aider de la même manière que n'importe quel autre débogueur pas à pas interactif - vous pourriez découvrir que les chemins d'exécution ne sont pas ceux que vous attendez, et c'est le principal problème de performances.
Greg Hendershott du
Merci pour l'astuce sur font-lock-studio, c'est génial! N'aide pas avec les fonctions jit-lock, mais bien sûr avec tout le reste.
Joakim Hårsman

Réponses:

8

Il s'avère que les performances extrêmement variables étaient liées à la collecte des ordures. Chaque appel à la fonction deviendrait plus lent jusqu'à ce qu'une récupération de place soit exécutée. Avec emacs stock, gc était exécuté toutes les deux secondes, mais j'avais une ligne dans mon init.el pour améliorer le temps de démarrage qui définissait gc-cons-threshold à 20 Mo, et cela signifiait que gc était exécuté beaucoup plus rarement, ce qui provoquait signaler un chronométrage de plus en plus lent jusqu'à ce qu'un GC soit exécuté après quelques minutes, puis les temps chuteraient et seraient à nouveau rapides.

Après être revenu à gc-cons-threshhold par défaut, l'analyse comparative est devenue plus facile.

J'ai ensuite profilé la mémoire avec le profiler ( M-x profiler-start) intégré et découvert que les appels à syntax-ppss causaient le plus d'allocations, donc après une optimisation pour appeler syntax-ppss moins souvent, j'ai atteint des performances acceptables.

Utiliser le mode jit-lock (ajouter une fonction via jit-lock-register) semble être le moyen le plus simple pour que le verrouillage de police multi-lignes fonctionne de manière fiable, c'est donc la méthode que j'ai choisie.

Edit: Après avoir découvert que les performances n'étaient toujours pas assez bonnes dans les très gros tampons, j'ai passé beaucoup de temps à optimiser l'utilisation et l'allocation des processeurs, à mesurer les améliorations des performances avec le profileur Emacs intégré ( M-x profiler-start). Cependant, Emacs bégayait toujours et se bloquait lors du défilement rapide à travers de très grands tampons. La suppression de la fonction jit-lock avec laquelle je me suis inscrit jit-lock-registersupprimerait le bégaiement et les blocages, mais le profilage a montré que la fonction jit-lock se terminait en environ 8 ms, ce qui devrait être assez rapide pour un défilement fluide. La suppression de l'appel à jit-lock-registeret à la place à l'aide d'un correcteur de police-verrouillage-mots-clés standard a résolu le problème.

TLDR: Faire cela était lent et bégayerait:

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

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

Faire cela était rapide et ne bégayerait pas:

(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))))
Joakim Hårsman
la source
Pourriez-vous partager le code que vous avez utilisé? Votre solution pourrait aider ceux qui cherchent à réaliser la même chose.
Manuel Uberti
Je n'ai pas vraiment utilisé de code spécifique, j'ai juste appelé moins syntax-ppss. Vous pouvez consulter le code en question ici: bitbucket.org/harsman/dyalog-mode/src/… Recherchez dyalog-fontify-locals.
Joakim Hårsman
Je suppose que ça dyalog-fontify-locals-matcherdevrait être my-font-lock-matcheret l'un des enddevrait être limit. Bref, découverte vraiment intéressante!
Lindydancer
@ Lindindancer: Oui merci. Fixé.
Joakim Hårsman
1
Re gc-cons-threshold:, si vous jouez avec des valeurs internes uniquement pour améliorer le temps de démarrage, je vous suggère de les utiliser emacs-startup-hookpour les restaurer ensuite.
phils