¿Cómo evito que las líneas extremadamente largas hagan que Emacs sea lento?


72

Veo un rendimiento muy variado dependiendo de cuántas líneas nuevas haya en el archivo que estoy visitando.

Aquí hay un ejemplo. Tengo dos archivos 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

Estos son dos archivos JSON con el mismo contenido. one_line.jsones 18MiB de JSON sin ninguna línea nueva. pretty_printed.jsontiene nuevas líneas y espacios en blanco agregados, lo que lo hace 41MiB.

Sin embargo, el archivo más grande dividido en muchas líneas es mucho más rápido de abrir en Emacs, tanto en modo Javascript como en modo Fundamental.

¿Por qué Emacs tiene un rendimiento tan pobre con líneas largas, ya que en realidad son menos bytes? ¿Hay algo que pueda hacer para mejorar el rendimiento sin reformatear los datos fuera de Emacs?


2
No es realmente una respuesta, pero podría ser útil: View Large Files(vlf) es un modo menor que tiene como objetivo ayudar a editar archivos grandes cargándolos en lotes . Descargo de responsabilidad: nunca lo he usado y no sé si también maneja líneas largas en lotes .
elemakil

3
Conociendo este tipo de comportamiento, y especialmente cuando trato de protegerme de leer un registro que escupe una línea larga, a menudo hago algo como $ tail -f /some/file | fold -sen un búfer de shell. Obviamente, esto no es bueno para editar, pero ayuda mucho con la lectura.
wvxvw

Respuestas:


50

El manejo de Emacs de líneas largas no está muy bien optimizado. Para una serie de operaciones, Emacs tiene que escanear la línea completa repetidamente. Por ejemplo, para mostrar una línea, Emacs tiene que calcular la altura de la línea, lo que requiere escanear toda la línea para encontrar el glifo más alto. Además, el escaneo en busca de visualización bidireccional consume mucho tiempo. Puede obtener información adicional en, por ejemplo, la cadena de documentos de cache-long-line-scans(renombrada cache-long-scansen 24.4).

Puede intentar ver si la configuración bidi-paragraph-directionde left-to-rightmejora la velocidad para usted [la configuración bidi-display-reorderingde nil, hace más o menos lo mismo, pero solo está destinada a fines internos / de depuración]. Esto elimina un contribuyente significativo a los escaneos de línea, pero lamentablemente no es el único.

La mejor opción es agregar nuevas líneas. Puede canalizar un archivo JSON, por ejemplo, python -c 'import json, sys ; json.dump(json.load(sys.stdin), sys.stdout, indent=2)'para agregar nuevas líneas y mejorar la legibilidad en general.


44
Por curiosidad, ¿es esto algo que no se puede mejorar algorítmicamente?
PythonNut

9
Al elegir la estructura de datos subyacente de un editor, debe elegir entre ciertos pros y contras. Emacs utiliza un búfer de espacio , que es una estructura de datos altamente eficiente en cuanto al espacio para la inserción y eliminación, pero hace que las operaciones basadas en líneas sean más lentas, ya que debe buscar secuencialmente una nueva línea. Emacs podría usar una estructura de datos diferente, pero eso haría que otras operaciones fueran más lentas. Emacs ya usa un caché de línea, pero eso realmente no ayuda en todas las situaciones. Por lo tanto, no es fácil mejorar algorítmicamente, pero la elaboración de perfiles y la optimización nunca están de más. :-)
Jorgen Schäfer

44
(setq-default bidi-display-reordering nil)- algunos usuarios pueden no darse cuenta de que esta es una variable local de búfer, que puede necesitar una configuración predeterminada en la medida en que un usuario quiera que esto sea global. Desearía haber agregado eso a mis init.elaños atrás ... pero al menos está ahí ahora. ¡¡¡Muchas gracias!!!
ley

En mi caso, no fue una gran improvisación (líneas json realmente largas con cuerpo de documentos base64), pero ayuda mucho en la congelación de
beign

1
El actual mantenedor de Emacs, Eli, quien escribió el código BIDI, escribe esto acerca de la desconexión bidi-display-reordering: "Un comentario que tengo es que deshabilitar la reordenación de visualización bidireccional ... pone el motor de visualización en un estado que no se está probando y puede causar inconsistencias e incluso errores (porque algunas partes del código se escribieron bajo el supuesto de que esta variable nunca es nula) ".
Clément

18

Hice algunos experimentos breves con esto usando una copia minificada de jquery. font-lock-modey flycheck-modeambos contribuyeron a la lentitud, como lo hizo js2-mode, y prettify-symbols-mode. line-number-modey column-number-modetuvo un efecto menor. Una vez que apagué todos los modos diferentes, aunque el rendimiento fue relativamente rápido. Use C-h my comience a deshabilitar los diferentes modos que están habilitados, o intente simplemente cambiar a fundamental-mode.

Curiosamente hexl-mode, podría volar a través del archivo sin ningún problema, aunque obviamente las columnas eran bastante cortas. Desafortunadamente, visual-line-moderealmente ralentizó las cosas.

Supongo que la tabla de sintaxis se complace en detener el procesamiento en los finales de línea, y cuando todo está en una línea, tiene que volver a analizar todo en cada actualización.


2
¿Puedes abrir un informe de error en el rastreador de Flycheck? Estoy bastante seguro de que no queremos largas colas que causen problemas, y Emacs + Flycheck no debería ser peor que Emacs (que todavía es bastante malo).
Clément

16

He subido http://www.emacswiki.org/emacs/OverLongLineMode

Esta biblioteca le permite establecer umbrales simples de longitud de línea más allá de los cuales fundamental-modese utilizará una variante de para un archivo en lugar de su modo normal (solo para modos de programación).

Potencialmente, algo por el estilo podría agregarse a Emacs de forma predeterminada, pero esto puede ser una solución provisional para el problema principal de Emacs que se ralentiza al encontrar un archivo de este tipo.

nb Esta es una mejora sobre el código que publiqué inicialmente en esta respuesta, pero sigue siendo un trabajo en progreso. Las pruebas han sido mínimas. Los comentarios son bienvenidos.

También se aceptan sugerencias para otros (además css-mode) prog-modemodos principales no derivados para admitir por defecto.


1
Ahora mejoró aún más, y vergonzosamente renombrado a so-long.el :) (el enlace anterior redirigirá). Se puede hacer más con esto, pero es 100% funcional y útil como está.
phils

Esta es una solución realmente agradable (me encantaría verla en MELPA), pero mi instancia de Emacs sigue siendo extremadamente lenta al abrir one_line.json. Creo que sería significativamente más rápido si no activara primero el modo principal original.
Wilfred Hughes

3
Al volver a leer esto y usar su archivo one_line.json de la pregunta, dejé de esperar a que Emacs 25.3 y 26.0.91 de configuración predeterminada respondan después de pedirles que abran ese archivo (después de esperar más de un minuto), mientras que el mío config con so-long.elactive abrió el archivo en menos de 2 segundos. En realidad, editar el archivo sigue siendo muy problemático (por ejemplo, tratar de pasar a la 'siguiente línea' tomará un tiempo extremadamente largo), pero sin embargo, esto restaura mi fe en la utilidad de la biblioteca que escribí, por lo que debería reanudar mis planes para agréguelo a GNU ELPA ...
phils

1
¿Ya está en (M) ELPA?
binki

3
Informe de estado: la versión 1.0 de so-long.el(con numerosas mejoras) se incluye en las versiones de desarrollo actuales de Emacs 27, y estará disponible (para versiones anteriores de Emacs) a través de GNU ELPA en algún momento cercano.
Phil

7

Espero que encuentres que la diferencia se debe a font-lock. Cuando se va a realizar la fuente en el subconjunto del archivo que está visible en la ventana, se procede primero extendiendo la región de fuente de manera que incluya unidades semánticas completas. Vea el font-lock-extend-region-functionscódigo para esto. Es común que esto incluya extender la región para incluir líneas completas. Cuando las líneas son extremadamente largas, esto puede llevar a que la fuente se realice en una porción de contenido mucho más grande de lo que es realmente visible.

Además, cuando las nuevas líneas tienen información semántica, su ausencia a veces puede significar que los patrones regexp para el bloqueo de fuentes tienen que escanear más para determinar si coinciden o no.


7

Por lo general, desenrollo líneas largas y sangría por etiquetas (como HTML, XML, JSON).

Para hacer posible tal operación, agrego:

(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))

Me dividir la línea de expresiones regulares, para XML que: C-M-% >< RET >NL< RET !.

Después de que Emacs separe las líneas largas, es posible habilitar muchos *-modesy volver a sangrar el código.

Para la nota: ¿Cómo prevenir la desaceleración cuando un proceso inferior genera largas filas?


4

Creé mi propia solución para este problema aquí: https://github.com/rakete/too-long-lines-mode

No estaba satisfecho con la solución phils que cambia un búfer con líneas muy largas al modo fundamental, quería una solución que me permitiera mantener el resaltado de sintaxis y otras características del modo principal. Así que creé un modo menor que usa superposiciones para ocultar la mayoría de los caracteres de líneas demasiado largas.

Eso soluciona el problema y hace que emacs sea utilizable incluso en buffers con líneas muy largas, sin tener que volver al modo fundamental.


2

En mi configuración de Emacs tengo un modo con fuente personalizada, es decir, donde configuro font-lock-defaults. Una sola página hacia abajo usaría 30 segundos para mostrar parte de la línea de 30000 caracteres. Esta ralentización se solucionó reduciendo el retroceso regexp. En lugar de:

  (". * terminó con un comando incompleto *" 0 font-lock-comment-face)

hacer esto

  ("^. \ {1,80 \} terminó con un comando incompleto *" 0 font-lock-comment-face)

Esta no es una respuesta a la pregunta, que no se refiere específicamente a la font-lock-defaultscoincidencia de expresiones regulares.
Drew

1
@Drew Regex menos que ideal hace que el bloqueo de fuente sea lento en líneas largas ...
wasamasa

1
@wasamasa: Sí. La pregunta en sí es demasiado amplia, OMI. Hay muchas cosas que pueden ralentizar Emacs (¿y para qué acciones?) Cuando se trata de largas colas.
Drew

3
No creo que la pregunta sea demasiado amplia ("¿por qué las largas colas hacen que Emacs sea lento")? Tampoco creo que la respuesta no aborde la pregunta (" una posible razón son expresiones regulares subóptimas"). Otras respuestas pueden abordar otras razones. Abrir un archivo con líneas largas no es un tema amplio solo porque eso podría ser problemático por una variedad de razones, a veces tienes esos archivos y tienes que mirarlos, preferiblemente usando Emacs.
tarsius

1

En mis búferes de modo shell (shell Mx), me encuentro con tuberías sed -r 's/(.{2000}).*/\1/' -upara evitar largas colas.


Esto responde a la segunda parte de la pregunta: cómo mejorar el rendimiento. No aborda la primera parte (que está bien): " ¿Por qué Emacs tiene un rendimiento tan pobre con líneas largas ?"
Dibujó el

0

Utilizo la siguiente función para abrir dired-modearchivos grandes con líneas largas:

(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)

0

Aquí hay una solución, tomada de 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)))))

En Emacs a partir de 24.4 longlines-modese marcó como obsoleto visual-line-mode.
Alexander I.Grafov

Sin embargo, las dos características hacen cosas muy diferentes detrás de escena y visual-line-modeno ayudan con el problema en cuestión, mientras que lo longlines-modehacen. Por esta razón, espero que longlines.el se restablezca a un estado no obsoleto.
phils