Come posso evitare che le linee estremamente lunghe rallentino Emacs?


72

Vedo prestazioni estremamente varie a seconda di quante nuove righe ci sono nel file che sto visitando.

Ecco un esempio Ho due file 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

Questi sono due file JSON con lo stesso contenuto. one_line.jsonè 18 MiB di JSON senza alcuna nuova riga. pretty_printed.jsonha newline e spazi bianchi aggiunti, rendendolo 41 MiB.

Tuttavia, il file più grande suddiviso su molte linee è molto più veloce da aprire in Emacs, sia in modalità Javascript che in modalità Fondamentale.

Perché Emacs ha prestazioni così scarse con linee lunghe, dal momento che in realtà ha un minor numero di byte? Posso fare qualcosa per migliorare le prestazioni senza riformattare i dati al di fuori di Emacs?


2
Non proprio una risposta, ma potrebbe essere utile: View Large Files(vlf) è una modalità minore che ha lo scopo di aiutare con la modifica di file di grandi dimensioni caricandoli in batch . Disclaimer: non l'ho mai usato e non so se gestisce anche le righe lunghe in batch .
elemakil,

3
Conoscendo questo tipo di comportamento, e specialmente quando cerco di proteggermi dalla lettura di un registro che sputa una lunga fila, spesso faccio qualcosa di simile $ tail -f /some/file | fold -sin un buffer di shell. Questo non è buono per l'editing, ovviamente, ma aiuta molto nella lettura.
wvxvw,

Risposte:


50

La gestione delle lunghe file da parte di Emacs non è ottimizzata molto bene. Per una serie di operazioni, Emacs deve scansionare ripetutamente l'intera riga. Ad esempio, per visualizzare una linea, Emacs deve capire l'altezza della linea, che richiede la scansione dell'intera linea per trovare il glifo più alto. Inoltre, la scansione per la visualizzazione bidirezionale richiede molto tempo. È possibile ottenere alcune informazioni aggiuntive, ad esempio, nel docstring di cache-long-line-scans(rinominato cache-long-scansin 24.4).

Puoi provare a vedere se l'impostazione bidi-paragraph-directionper left-to-rightmigliorare la velocità per te [l'impostazione bidi-display-reorderingsu nil, fa più o meno la stessa cosa ma è pensata solo per scopi interni / di debug]. Questo rimuove un contributo significativo alle scansioni di linea, ma purtroppo non l'unico.

L'opzione migliore è aggiungere nuove righe. È possibile reindirizzare un file JSON ad esempio python -c 'import json, sys ; json.dump(json.load(sys.stdin), sys.stdout, indent=2)'per aggiungere nuove righe e migliorare la leggibilità in generale.


4
Per curiosità, è qualcosa che non può essere migliorato algoritmicamente?
PythonNut,

9
Quando si sceglie la struttura dei dati di base di un editor, è necessario scegliere tra alcuni pro e contro. Emacs utilizza un buffer di gap , che è una struttura di dati ad alta efficienza spaziale per l'inserimento e la cancellazione, ma rende le operazioni basate su linea più lente poiché è necessario eseguire la scansione sequenziale di una nuova riga. Emacs potrebbe utilizzare una diversa struttura di dati, ma ciò renderebbe le altre operazioni più lente. Emacs utilizza già una cache di linea, ma ciò non è di grande aiuto in tutte le situazioni. Quindi, non facilmente migliorabile algoritmicamente, ma la profilazione e l'ottimizzazione non fanno mai male. :-)
Jorgen Schäfer,

4
(setq-default bidi-display-reordering nil)- alcuni utenti potrebbero non rendersi conto che si tratta di una variabile buffer-locale, che potrebbe richiedere un'impostazione predefinita nella misura in cui un utente desidera che sia globale. Vorrei che l'avrei aggiunto ai miei init.elanni fa ... ma almeno adesso è lì. Grazie mille!!!
elenco delle leggi

Nel mio caso non è stato un grande improvvisatore (linee json davvero lunghe con corpo di documenti base64) ma aiuta molto sul congelamento di beign
anquegi

1
L'attuale manutentore di Emacs, Eli, che ha scritto il codice BIDI, scrive questo sullo spegnimento bidi-display-reordering: "Un commento che ho è che la disabilitazione del riordino del display bidi ... mette il motore di visualizzazione in uno stato che non è stato testato e può causare incoerenze e persino bug (perché alcune parti del codice sono state scritte partendo dal presupposto che questa variabile non è mai nulla). "
Clément,

18

Ho fatto alcuni brevi esperimenti con questo usando una copia minimizzata di jquery. font-lock-modeed flycheck-modeentrambi hanno contribuito alla lentezza, come ha fatto js2-mode, e prettify-symbols-mode. line-number-modee ha column-number-modeavuto un effetto minore. Una volta avevo disattivato tutte le diverse modalità sebbene le prestazioni fossero relativamente scattanti. Usa C-h me inizia a disabilitare le diverse modalità abilitate, oppure prova a passare a fundamental-mode.

È interessante notare che usando hexl-modeho potuto volare attraverso il file senza problemi, anche se ovviamente le colonne erano piuttosto brevi. Purtroppo ha visual-line-modedavvero rallentato le cose.

La mia ipotesi è che la tabella di sintassi sia felice di interrompere l'elaborazione alle terminazioni di riga e quando è tutto su una riga deve replicare tutto su ogni aggiornamento.


2
Puoi aprire una segnalazione di bug sul tracker di Flycheck? Sono abbastanza sicuro che non vogliamo che le lunghe file causino problemi e Emacs + Flycheck non dovrebbe essere peggio di Emacs (che è ancora piuttosto male).
Clément,

16

Ho caricato http://www.emacswiki.org/emacs/OverLongLineMode

Questa libreria consente di impostare soglie di lunghezza della linea oltre le quali fundamental-modeverrà utilizzata una variante di un file anziché la sua modalità normale (solo per le modalità di programmazione).

Potenzialmente, qualcosa del genere potrebbe essere aggiunto a Emacs per impostazione predefinita, ma questa può essere una soluzione temporanea per il problema principale di Emacs che rallenta a una scansione quando si incontra un file del genere.

nb Si tratta di un miglioramento rispetto al codice inizialmente pubblicato in questa risposta, ma ancora in fase di elaborazione. I test sono stati minimi. I commenti sono benvenuti.

Sono anche ben accetti i suggerimenti per altre modalità principali ( css-modenon specializzate) non prog-modesupportate da supportare di default.


1
Ora ulteriormente migliorato e rinominato vergognosamente in così-long.el :) (il link sopra verrà reindirizzato). C'è di più che si potrebbe fare con questo, ma è funzionale al 100% e utile così com'è.
phils,

Questa è davvero una bella soluzione (mi piacerebbe vederla su MELPA), ma la mia istanza di Emacs è ancora estremamente lenta quando apro one_line.json. Penso che sarebbe significativamente più veloce se non attivasse prima la modalità principale originale.
Wilfred Hughes,

3
Rileggendo questo e usando il tuo file one_line.json dalla domanda, ho rinunciato ad aspettare che Emacs 25.3 e 26.0.91 di default-config rispondessero dopo aver chiesto loro di aprire quel file (dopo aver atteso più di un minuto), mentre il mio la configurazione con so-long.elattivo ha aperto il file in meno di 2 secondi. In realtà la modifica del file è ancora estremamente problematica (ad esempio, il tentativo di passare alla "riga successiva" richiederà molto tempo), ma ciò tuttavia ripristina la mia fiducia nell'utilità della libreria che ho scritto, quindi dovrei riprendere i miei piani per aggiungilo a GNU ELPA ...
phils

1
È già in (M) ELPA?
binki,

3
Rapporto sullo stato: la versione 1.0 di so-long.el(con numerosi miglioramenti) è inclusa nelle attuali versioni di sviluppo di Emacs 27 e sarà disponibile (per le versioni precedenti di Emacs) tramite GNU ELPA nel prossimo futuro.
phils,

7

Immagino che scoprirai che la differenza è dovuta font-lock. Quando la fontificazione deve essere eseguita sul sottoinsieme del file che è visibile nella finestra, procede estendendo prima la regione di fontificazione in modo tale da includere unità semantiche complete. Vedi il font-lock-extend-region-functionscodice per questo. È comune per questo includere l'estensione della regione per includere le linee complete. Quando le linee sono estremamente lunghe, ciò può portare alla fontificazione che viene eseguita su una porzione di contenuto molto più ampia di quanto sia effettivamente visibile.

Inoltre, quando le newline stesse hanno informazioni semantiche, la loro assenza a volte può significare che i pattern regexp per il blocco dei caratteri devono eseguire ulteriori scansioni per determinare se corrispondono o meno.


7

Solitamente srotolo lunghe righe e rientro per tag (come HTML, XML, JSON).

Per rendere possibile tale operazione, aggiungo:

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

Ho diviso riga per regex, per XML esso: C-M-% >< RET >NL< RET !.

Dopo che Emacs ha diviso le righe lunghe, è possibile abilitare molti *-modese rientrare il codice.

Per la nota: come prevenire il rallentamento quando un processo inferiore genera linee lunghe?


4

Ho creato la mia soluzione per questo problema qui: https://github.com/rakete/too-long-lines-mode

Non ero soddisfatto della soluzione phils che commuta un buffer con linee molto lunghe in modalità fondamentale, volevo una soluzione che mi permettesse di mantenere l'evidenziazione della sintassi e altre funzionalità della modalità principale. Così ho creato una modalità minore che utilizza le sovrapposizioni per nascondere la maggior parte dei personaggi con linee troppo lunghe.

Ciò risolve il problema e rende emacs utilizzabili anche in buffer con linee molto lunghe, senza dover ricorrere alla modalità fondamentale.


2

Nella mia configurazione di Emacs ho una modalità con fontificazione personalizzata, ovvero dove ho impostato font-lock-defaults. Una sola pagina in basso impiegherebbe 30 secondi per visualizzare parte di una riga di 30000 caratteri. Questo rallentamento è stato risolto riducendo il backtracking di regexp. Invece di:

  (". * è terminato con un comando incompleto *" 0 font-lock-comment-face)

Fai questo

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

Questa non è una risposta alla domanda, che non è specificamente font-lock-defaultso corrispondenza espressione regolare.
Estratto il

1
@Drew Meno di ideale regex sta rallentando il blocco dei caratteri su linee lunghe ...
wasamasa

1
@wasamasa: Sì. La domanda in sé è troppo ampia, IMO. Ci sono molte cose che possono rallentare Emacs (e per quali azioni?) Quando sono coinvolte lunghe file.
Drew

3
Non credo che la domanda sia troppo ampia ("perché le linee lunghe rallentano Emacs")? Né penso che la risposta non affronti la domanda (" una possibile ragione sono regexps non ottimali"). Altre risposte possono affrontare altri motivi. L'apertura di un file con linee lunghe non significa estendere un argomento solo perché potrebbe essere problematico per una serie di motivi, a volte hai tali file e devi guardarli, preferibilmente usando Emacs.
Tarsius,

1

Nei miei buffer in modalità shell (shell Mx), mi ritrovo a sed -r 's/(.{2000}).*/\1/' -ueseguire il piping per evitare le lunghe file.


Questo risponde alla seconda parte della domanda: come migliorare le prestazioni. Non affronta la prima parte (che è OK): " Perché Emacs ha prestazioni così scarse con le linee lunghe ?"
Disegnò il

0

Uso la seguente funzione per aprire dired-modefile di grandi dimensioni con linee lunghe:

(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

Ecco una soluzione alternativa, presa da 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)))))

In Emacs a partire dal 24.4 è longlines-modestato contrassegnato come obsoleto da visual-line-mode.
Alexander I.Grafov

Tuttavia le due caratteristiche fanno cose molto diverse dietro le quinte, e visual-line-modenon aiutano con il problema in questione, mentre lo longlines-modefa. Per questo motivo, mi aspetto che longlines.el venga ripristinato a uno stato non deprecato.
phils,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.