Comment empêcher les lignes extrêmement longues de ralentir Emacs?

72

Je vois des performances extrêmement variées en fonction du nombre de nouvelles lignes figurant dans le fichier que je visite.

Voici un exemple. J'ai deux fichiers JSON:

$ wget https://github.com/Wilfred/ReVo-utilities/blob/a4bdc40dd2656c496defc461fc19c403c8306d9f/revo-export/dictionary.json?raw=true -O one_line.json
$ python -m json.tool <one_line.json >pretty_printed.json

Ce sont deux fichiers JSON avec le même contenu. one_line.jsonest 18MiB de JSON sans nouvelles lignes. pretty_printed.jsona des nouvelles lignes et des espaces ajoutés, ce qui en fait 41 Mo.

Cependant, le fichier plus volumineux divisé sur plusieurs lignes est beaucoup plus rapide à ouvrir dans Emacs, aussi bien en mode Javascript qu'en mode fondamental.

Pourquoi Emacs a-t-il des performances si médiocres avec les longues lignes, vu que c'est en fait moins d'octets? Puis-je faire quelque chose pour améliorer les performances sans reformater les données en dehors d'Emacs?

Wilfred Hughes
la source
2
Ce n'est pas vraiment une réponse, mais cela pourrait être utile: View Large Files(vlf) est un mode mineur destiné à aider à l'édition de gros fichiers en les chargeant par lots . Déni de responsabilité: je ne l'ai jamais utilisé et je ne sais pas s'il gère également les longues lignes par lots .
Elemakil
3
Connaissant ce type de comportement, et plus particulièrement lorsque j'essaie de me garder de lire un journal qui crache une longue ligne, je fais souvent quelque chose comme $ tail -f /some/file | fold -sdans un tampon. Ce n'est évidemment pas bon pour l'édition, mais aide beaucoup à la lecture.
wvxvw

Réponses:

50

Le traitement des longues lignes par Emacs n’est pas très optimisé. Pour un certain nombre d'opérations, Emacs doit balayer la ligne entière de manière répétée. Par exemple, pour afficher une ligne, Emacs doit déterminer la hauteur de la ligne, ce qui nécessite de balayer toute la ligne pour trouver le glyphe le plus haut. De plus, la recherche d’affichage bidirectionnel prend beaucoup de temps. Vous pouvez obtenir des informations supplémentaires dans, par exemple, la docstring de cache-long-line-scans(renommée cache-long-scansen 24.4).

Vous pouvez essayer de voir si régler bidi-paragraph-directionpour left-to-rightaméliorer votre vitesse [régler bidi-display-reorderingsur nil, fait plus ou moins la même chose mais est uniquement destiné à des fins internes / de débogage]. Cela supprime un contributeur important aux analyses de ligne, mais malheureusement pas le seul.

La meilleure option consiste à ajouter des nouvelles lignes. Vous pouvez diriger un fichier JSON, par exemple, python -c 'import json, sys ; json.dump(json.load(sys.stdin), sys.stdout, indent=2)'pour ajouter des nouvelles lignes et améliorer la lisibilité en général.

Jorgen Schäfer
la source
4
Par curiosité, est-ce quelque chose qui ne peut pas être amélioré algorithmiquement?
PythonNut
9
Lorsque vous choisissez la structure de données sous-jacente d'un éditeur, vous devez choisir entre certains avantages et inconvénients. Emacs utilise un gap , une structure de données très efficace en termes d'espace pour l'insertion et la suppression, mais elle ralentit les opérations basées sur les lignes car vous devez rechercher séquentiellement une nouvelle ligne. Emacs pourrait utiliser une structure de données différente, mais cela ralentirait les autres opérations. Emacs utilise déjà un cache de lignes, mais cela n’aide pas vraiment dans toutes les situations. Donc, il n’est pas facile d’améliorer l’algorithme, mais le profilage et l’optimisation ne font jamais de mal. :-)
Jorgen Schäfer
4
(setq-default bidi-display-reordering nil)- Certains utilisateurs peuvent ne pas se rendre compte qu'il s'agit d'une variable locale du tampon, ce qui peut nécessiter un paramètre par défaut si un utilisateur souhaite que cela soit global. J'aurais aimé ajouter cela à mes init.elannées précédentes ... mais au moins, c'est là maintenant. Merci beaucoup!!!
lawlist
Dans mon cas, ce n’était pas une grosse avancée (très longues lignes json avec corps de documents base64) mais une aide précieuse sur la congélation
anquegi
1
Le responsable actuel d’Emacs, Eli, qui a écrit le code BIDI, écrit ceci à propos de l’extinction bidi-display-reordering: «L’un de mes commentaires est que la désactivation de bidi-display-reordering… met le moteur d’affichage dans un état qui n’est pas testé et peut provoquer des incohérences. et même des bugs (parce que certaines parties du code ont été écrites en supposant que cette variable n'est jamais nulle). "
Clément
18

J'ai fait quelques brèves expériences avec cela en utilisant une copie réduite de jQuery. font-lock-modeet les flycheck-modedeux ont contribué à la lenteur, comme l'a fait js2-mode, et prettify-symbols-mode. line-number-modeet a column-number-modeeu un effet mineur. Une fois que j'avais désactivé tous les modes, la performance était relativement vive. Utilisez C-h met commencez à désactiver les différents modes activés ou essayez simplement de basculer vers fundamental-mode.

De manière intéressante, hexl-modeje pouvais parcourir le fichier sans problème, bien que les colonnes soient évidemment assez courtes. Malheureusement, les visual-line-modechoses ont vraiment ralenti.

Mon hypothèse est que la table de syntaxe est heureuse d’arrêter le traitement en fin de ligne et que, sur une seule ligne, elle doit tout réparer à chaque mise à jour.

dédoublé
la source
2
Pouvez-vous ouvrir un rapport de bogue sur le suivi de Flycheck? Je suis sûr que nous ne voulons pas de longues files d'attente, et Emacs + Flycheck ne devrait pas être pire qu'Emacs (qui est quand même assez mauvais).
Clément
16

J'ai téléchargé http://www.emacswiki.org/emacs/OverLongLineMode

Cette bibliothèque vous permet de définir des seuils simples de longueur de ligne au-delà desquels une variante de fundamental-modesera utilisée pour un fichier au lieu de son mode normal (pour les modes de programmation uniquement).

Il se peut que quelque chose dans ce sens puisse être ajouté à Emacs par défaut, mais il peut s'agir d'une solution provisoire au problème principal d'Emacs qui ralentit son analyse lorsqu'il rencontre un tel fichier.

nb Il s'agit d'une amélioration par rapport au code que j'ai initialement publié dans cette réponse, mais qui reste un travail en cours. Les tests ont été minimes. Les commentaires sont les bienvenus.

Les suggestions concernant d'autres modes principaux (en plus css-mode) non prog-modedérivés à prendre en charge par défaut sont également les bienvenues.

phils
la source
1
Maintenant encore amélioré, et honteusement renommé so-long.el :) (le lien ci-dessus sera redirigé). Cela pourrait faire plus, mais c'est 100% fonctionnel et utile en l'état.
phils
C'est une très bonne solution (j'adorerais la voir sur MELPA), mais mon instance Emacs est encore extrêmement lente lors de l'ouverture de one_line.json. Je pense que ce serait beaucoup plus rapide s'il n'activait pas d'abord le mode majeur.
Wilfred Hughes
3
Relisant ceci et en utilisant votre fichier one_line.json de la question, j’ai abandonné l’attente de la configuration par défaut d’Emacs 25.3 et 26.0.91 pour répondre après leur avoir demandé d’ouvrir ce fichier (après plus d’une minute), alors que le mien config avec so-long.elactif a ouvert le fichier en moins de 2 secondes. En fait, éditer le fichier pose toujours énormément de problèmes (par exemple essayer de passer à la "ligne suivante" prendra très longtemps), mais néanmoins, cela me permet de croire de nouveau à l'utilité de la bibliothèque que j'ai écrite. Je devrais donc reprendre mes plans ajoutez-le à GNU ELPA ...
phils
1
Est-ce déjà dans (M) ELPA?
binki
3
Rapport de statut: la version 1.0 de so-long.el(avec de nombreuses améliorations) est incluse dans les versions de développement actuelles d’Emacs 27 et sera disponible (pour les versions antérieures d’Emacs) via GNU ELPA dans un avenir proche.
phils
7

Je suppose que vous constaterez que la différence est due à font-lock. Lorsque la fontification doit être effectuée sur le sous-ensemble du fichier visible dans la fenêtre, elle commence par étendre la région de fontification de manière à inclure des unités sémantiques complètes. Voir le font-lock-extend-region-functionscode pour cela. Il est courant que cela inclue l'extension de la région pour inclure des lignes complètes. Lorsque les lignes sont extrêmement longues, la fontification peut être effectuée sur un bloc de contenu beaucoup plus volumineux que ce qui est réellement visible.

De plus, lorsque les nouvelles lignes contiennent elles-mêmes des informations sémantiques, leur absence peut parfois signifier que les modèles d'expressions rationnelles pour le verrouillage de police doivent analyser davantage afin de déterminer s'ils correspondent ou non.

santé mentale
la source
7

Je déroule généralement de longues lignes et indente par balises (comme HTML, XML, JSON).

Afin de rendre cette opération possible, j'ajoute:

(setq line-number-display-limit large-file-warning-threshold)
(setq line-number-display-limit-width 200)

(defun my--is-file-large ()
  "If buffer too large and my cause performance issue."
  (< large-file-warning-threshold (buffer-size)))

(define-derived-mode my-large-file-mode fundamental-mode "LargeFile"
  "Fixes performance issues in Emacs for large files."
  ;; (setq buffer-read-only t)
  (setq bidi-display-reordering nil)
  (jit-lock-mode nil)
  (buffer-disable-undo)
  (set (make-variable-buffer-local 'global-hl-line-mode) nil)
  (set (make-variable-buffer-local 'line-number-mode) nil)
  (set (make-variable-buffer-local 'column-number-mode) nil) )

(add-to-list 'magic-mode-alist (cons #'my--is-file-large #'my-large-file-mode))

Je coupais ligne par regex, pour XML il: C-M-% >< RET >NL< RET !.

Après qu'Emacs ait divisé de longues lignes, il est possible d'activer de nombreux *-modescodes et des codes de réindentation.

Pour la note: Comment empêcher le ralentissement lorsqu'un processus inférieur génère de longues lignes?

Gavenkoa
la source
4

J'ai créé ma propre solution à ce problème ici: https://github.com/rakete/too-long-lines-mode

Je n'étais pas satisfait de la solution phils qui bascule un tampon avec de très longues lignes en mode fondamental. Je recherchais une solution me permettant de conserver la coloration syntaxique et d'autres fonctionnalités en mode majeur. J'ai donc créé un mode mineur qui utilise des incrustations pour masquer la plupart des caractères de lignes trop longues.

Cela contourne le problème et rend emacs utilisable même dans les tampons avec de très longues lignes, sans avoir à revenir en mode fondamental.

Andreas Raster
la source
2

Dans ma configuration Emacs, j'ai un mode avec une fontification personnalisée, c'est-à-dire où je le fixe font-lock-defaults. Une seule page vers le bas prendrait 30 secondes pour afficher une partie de la ligne 30000 caractères. Ce ralentissement a été corrigé en réduisant le retour arrière des expressions rationnelles. Au lieu de:

  (". * se termine par une commande incomplète *" 0 font-lock-comment-face)

fais ça

  ("^. \ {1,80 \} s'est terminé par une commande incomplète *" 0 font-lock-comment-face)
Axel Bregnsbo
la source
Ce n'est pas une réponse à la question, qui ne concerne pas spécifiquement font-lock-defaultsou ne correspond pas à l' expression rationnelle.
Drew
1
@Drew Moins de regex idéal est faire lentement verrouiller la police sur les longues lignes si ...
wasamasa
1
@wasamasa: Oui. La question elle-même est trop large, OMI. Beaucoup de choses peuvent ralentir Emacs (et pour quelles actions?) Lorsque de longues lignes sont impliquées.
Drew
3
Je ne pense pas que la question soit trop large ("Pourquoi les longues lignes font-elles ralentir Emacs")? Je ne pense pas non plus que la réponse ne règle pas la question (" une des raisons possibles est une expression rationnelle sous-optimale"). D'autres réponses peuvent aborder d'autres raisons. Ouvrir un fichier avec de longues lignes n’est pas trop vaste pour un sujet simplement parce que cela peut poser problème pour diverses raisons. Parfois, vous avez de tels fichiers et vous devez les consulter, de préférence avec Emacs.
Tarsius
1

Dans mes mémoires tampons en mode shell (shell Mx), je me retrouve sed -r 's/(.{2000}).*/\1/' -uà contourner pour éviter les longues lignes.

David Chandler
la source
Cela répond à la deuxième partie de la question: comment améliorer les performances. Cela ne concerne pas la première partie (ce qui est OK): " Pourquoi Emacs a-t-il des performances aussi médiocres avec de longues lignes ?"
Drew
0

J'utilise la fonction suivante pour ouvrir de dired-modegros fichiers avec de longues lignes:

(defun dired-find-file-conservatively ()
   (interactive)
   (let ((auto-mode-alist nil))
     (dired-find-file)
     ;; disable costly modes
     (fundamental-mode)
     (setq-local bidi-display-reordering nil)
     (when (boundp 'smartparens-mode)
       (smartparens-mode -1))))

(define-key dired-mode-map (kbd "S-<return>") 'dired-find-file-conservatively)
Dodgie
la source
0

Voici une solution de contournement, tirée d’ emacs-devel :

(add-hook 'find-file-hook
          (defun my-find-file-care-about-long-lines ()
            (save-excursion
              (goto-char (point-min))
              (when (and (not (eq major-mode 'image-mode))
                         (search-forward-regexp ".\\{2000\\}" 50000 t)
                         (y-or-n-p "Very long lines detected - enable 
longlines-mode? "))
                (require 'longlines)
                (longlines-mode +1)))))
Clemera
la source
Dans Emacs à partir de 24.4, il a longlines-modeété marqué comme obsolète par visual-line-mode.
Alexander I.Grafov
Cependant, les deux fonctionnalités font des choses très différentes dans les coulisses, et n'aident visual-line-modepas à résoudre le problème en question, alors longlines-mode. Pour cette raison, je m'attends à ce que longlines.el soit restauré à un statut non obsolète.
phils