Jak mogę zapobiec bardzo długim liniom spowalniającym Emacsa?

72

Widzę bardzo zróżnicowaną wydajność w zależności od liczby nowych linii w pliku, który odwiedzam.

Oto przykład. Mam dwa pliki 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

Są to dwa pliki JSON o tej samej zawartości. one_line.jsonwynosi 18 MB JSON bez żadnych nowych linii. pretty_printed.jsondodano nowe znaki i białe znaki, dzięki czemu wynosi 41 MB.

Jednak większy plik podzielony na wiele wierszy jest znacznie szybszy do otwarcia w Emacsie, zarówno w trybie Javascript, jak i trybie podstawowym.

Dlaczego Emacs ma tak niską wydajność z długimi liniami, skoro w rzeczywistości ma mniej bajtów? Czy mogę coś zrobić, aby poprawić wydajność bez formatowania danych poza Emacsem?

Wilfred Hughes
źródło
2
Naprawdę nie jest to odpowiedź, ale może się przydać: View Large Files(vlf) to niewielki tryb, którego celem jest pomoc w edycji dużych plików poprzez ładowanie ich w partiach . Oświadczenie: Nigdy go nie używałem i nie wiem, czy obsługuje on również długie linie w partiach .
elemakil
3
Znając tego rodzaju zachowanie, a zwłaszcza gdy próbuję się wystrzegać przed czytaniem dziennika, który wyrzuca długą linię, często robię coś $ tail -f /some/file | fold -sw buforze powłoki. To oczywiście nie jest dobre do edycji, ale bardzo pomaga w czytaniu.
wvxvw

Odpowiedzi:

50

Obsługa długich linii przez Emacsa nie jest zbyt dobrze zoptymalizowana. W przypadku wielu operacji Emacs musi wielokrotnie skanować całą linię. Na przykład, aby wyświetlić linię, Emacs musi obliczyć wysokość linii, co wymaga zeskanowania całej linii w celu znalezienia najwyższego glifu. Ponadto skanowanie w celu wyświetlenia dwukierunkowego zużywa dużo czasu. Możesz uzyskać dodatkowe informacje, na przykład w dokumentacji cache-long-line-scans(zmienionej cache-long-scansw 24.4).

Można spróbować i sprawdzić, czy ustawienie bidi-paragraph-directionsię left-to-rightpoprawia szybkość dla ciebie [Setting bidi-display-reorderingdo nil, robi mniej więcej to samo, ale jest przeznaczona tylko do użytku wewnętrznego / debugowania]. To usuwa jeden znaczący wkład w skanowanie linii, ale niestety nie jedyny.

Najlepszą opcją jest dodanie nowych linii. Możesz potokować plik JSON, np. W python -c 'import json, sys ; json.dump(json.load(sys.stdin), sys.stdout, indent=2)'celu dodania nowych linii i ogólnej poprawy czytelności.

Jorgen Schäfer
źródło
4
Z ciekawości, czy nie można tego poprawić algorytmicznie?
PythonNut
9
Wybierając bazową strukturę danych edytora, musisz wybierać między pewnymi zaletami i wadami. Emacs używa bufora przerw , który jest wysoce efektywną przestrzennie strukturą danych do wstawiania i usuwania, ale spowalnia operacje liniowe, ponieważ musisz sekwencyjnie skanować w poszukiwaniu nowej linii. Emacs mógłby użyć innej struktury danych, ale spowolniłoby to inne operacje. Emacs już korzysta z pamięci podręcznej linii, ale tak naprawdę nie pomaga to we wszystkich sytuacjach. Tak więc niełatwo poprawić algorytm, ale profilowanie i optymalizacja nigdy nie boli. :-)
Jorgen Schäfer
4
(setq-default bidi-display-reordering nil)- niektórzy użytkownicy mogą nie zdawać sobie sprawy, że jest to zmienna lokalna buforująca, która może wymagać ustawienia domyślnego w zakresie, w jakim użytkownik chce, aby była globalna. Chciałbym dodać to do moich init.ellat temu ... ale przynajmniej jest teraz. Dziękuję bardzo!!!
prawnik
W moim przypadku nie był to duży ulepszenie (naprawdę długie linie JSON z dokumentem base64), ale bardzo pomaga w beign zamrażaniu
anquegi
1
Obecny opiekun Emacsa, Eli, który napisał kod BIDI, pisze o wyłączeniu bidi-display-reordering: „Jeden komentarz, który mam, to to, że wyłączenie ponownego zamawiania wyświetlania BiDi… powoduje, że silnik wyświetlania jest w stanie, który nie jest testowany i może powodować niespójności a nawet błędy (ponieważ niektóre części kodu zostały napisane przy założeniu, że ta zmienna nigdy nie ma wartości zero). ”
Clément
18

Zrobiłem z tym kilka krótkich eksperymentów, używając zminimalizowanej kopii jquery. font-lock-modei flycheck-modeoba przyczyniły się do spowolnienia, podobnie jak js2-modei prettify-symbols-mode. line-number-modei column-number-modemiał niewielki efekt. Kiedyś wyłączyłem wszystkie różne tryby, chociaż wydajność była stosunkowo szybka. Użyj C-h mi zacznij wyłączać różne tryby, które są włączone, lub po prostu przełącz się na fundamental-mode.

Co ciekawe hexl-mode, mogłem bez problemu latać po pliku, choć oczywiście kolumny były dość krótkie. Niestety visual-line-modenaprawdę spowolniło sytuację.

Domyślam się, że tabela składniowa chętnie przestaje przetwarzać na końcach linii, a kiedy wszystko jest w jednym wierszu, musi ponownie wszystko analizować przy każdej aktualizacji.

zdeterminowany
źródło
2
Czy możesz otworzyć raport o błędzie w narzędziu do śledzenia Flycheck? Jestem prawie pewien, że nie chcemy, aby długie linie powodowały problemy, a Emacs + Flycheck nie powinien być gorszy niż Emacs (co wciąż jest dość złe).
Clément
16

Przesłałem http://www.emacswiki.org/emacs/OverLongLineMode

Ta biblioteka pozwala ustawić proste progi długości linii, powyżej których wariant fundamental-modebędzie używany dla pliku zamiast jego normalnego trybu (tylko dla trybów programowania).

Potencjalnie coś podobnego można by domyślnie dodać do Emacsa, ale może to być tymczasowe obejście podstawowego problemu spowolnienia Emacsa podczas indeksowania po napotkaniu takiego pliku.

nb Jest to ulepszenie w stosunku do kodu, który pierwotnie opublikowałem w tej odpowiedzi, ale nadal jest w toku. Testowanie było minimalne. Komentarze są mile widziane.

Mile widziane są również sugestie dotyczące innych (poza css-mode) prog-modenieobsługiwanych głównych trybów domyślnej obsługi.

phils
źródło
1
Teraz jeszcze ulepszony i haniebnie przemianowany na so-long.el :) (powyższy link przekieruje). Można z tym zrobić więcej, ale jest w 100% funkcjonalny i użyteczny w obecnej postaci.
phils
To naprawdę fajne rozwiązanie (chciałbym zobaczyć to na MELPA), ale moja instancja Emacsa jest nadal bardzo wolna podczas otwierania pliku one_line.json. Myślę, że byłoby znacznie szybciej, gdyby najpierw nie aktywował oryginalnego trybu głównego.
Wilfred Hughes
3
Ponownie czytając to i używając pliku one_line.json z pytania, zrezygnowałem z oczekiwania na domyślną konfigurację Emacsa 25.3 i 26.0.91, aby odpowiedzieć po poproszeniu ich o otwarcie tego pliku (po odczekaniu ponad minuty), podczas gdy mój własny config z so-long.elaktywnym otworzył plik w niecałe 2 sekundy. W rzeczywistości edycja pliku jest nadal bardzo problematyczna (np. Próba przejścia do „następnej linii” zajmie bardzo dużo czasu), ale przywraca to moją wiarę w przydatność napisanej przeze mnie biblioteki, więc powinienem wznowić swoje plany dodaj go do GNU ELPA ...
phils
1
Czy jest już w (M) ELPA?
binki
3
Raport o stanie: wersja 1.0 so-long.el(z licznymi ulepszeniami) jest zawarta w aktualnych wersjach rozwojowych Emacsa 27 i będzie dostępna (dla wcześniejszych wersji Emacsa) za pośrednictwem GNU ELPA w najbliższej przyszłości.
phils
7

Oczekuję, że zauważysz, że różnica wynika z font-lock. Kiedy czcionkowanie ma zostać wykonane na podzbiorze pliku widocznym w oknie, następuje najpierw rozszerzenie obszaru czcionkowania, aby zawierał pełne jednostki semantyczne. Zobacz font-lock-extend-region-functionskod tego. Często obejmuje to rozszerzenie regionu o pełne linie. Gdy linie są bardzo długie, może to prowadzić do wykonania czcionek na znacznie większej części zawartości, niż jest to w rzeczywistości widoczne.

Ponadto, gdy same znaki nowej linii mają informacje semantyczne, ich brak może czasami oznaczać, że wzory wyrażeń regularnych dla blokowania czcionek muszą skanować dalej, aby ustalić, czy pasują do siebie, czy nie.

sanityinc
źródło
7

Zazwyczaj rozwijam długie linie i wciskam według znaczników (takich jak HTML, XML, JSON).

Aby umożliwić taką operację, dodaję:

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

Podzielić przez linię regex, XML IT: C-M-% >< RET >NL< RET !.

Po tym jak Emacs podzieli długie linie - możliwe jest włączenie wielu *-modesi ponowne wcięcie kodu.

Uwaga: jak zapobiegać spowolnieniu, gdy gorsze procesy generują długie linie?

gavenkoa
źródło
4

Stworzyłem własne rozwiązanie tego problemu tutaj: https://github.com/rakete/too-long-lines-mode

Nie byłem zadowolony z rozwiązania phils, które przełącza bufor z bardzo długimi liniami na tryb podstawowy, chciałem rozwiązania, które pozwoli mi zachować podświetlanie składni i inne funkcje trybu głównego. Więc stworzyłem tryb pomocniczy, który używa nakładek, aby ukryć większość znaków zbyt długich linii.

To rozwiązuje problem i sprawia, że ​​emacs jest użyteczny nawet w buforach z bardzo długimi liniami, bez konieczności powrotu do trybu podstawowego.

Andreas Raster
źródło
2

W mojej konfiguracji Emacsa mam tryb z niestandardową czcionką, tj. Gdzie ustawiłem font-lock-defaults. Pojedyncza strona w dół zajęłaby 30 sekund, aby wyświetlić część 30000 linii znaków. To spowolnienie zostało naprawione przez ograniczenie cofania śledzenia wyrażeń regularnych. Zamiast:

  („. * zakończyło się niepełnym poleceniem *” 0 font-lock-comment-face)

Zrób to

  („^. \ {1,80 \} zakończyło się niepełnym poleceniem *” 0 font-lock-comment-face)
Axel Bregnsbo
źródło
To nie jest odpowiedź na pytanie, które nie dotyczy konkretnie font-lock-defaultsani wyrażenia regularnego.
Drew
1
@Drew dalekie od ideału regex jest co font-lock powolny na długich liniach chociaż ...
wasamasa
1
@wasamasa: Tak. Samo pytanie jest zbyt ogólne, IMO. Istnieje wiele rzeczy, które mogą spowolnić Emacsa (i dla jakich działań?), Gdy zaangażowane są długie kolejki.
Drew
3
Nie sądzę, aby pytanie było zbyt szerokie („dlaczego długie linie spowalniają Emacsa”)? Nie sądzę też, aby odpowiedź nie dotyczyła pytania („ jednym z możliwych powodów są nieoptymalne wyrażenia regularne”). Inne odpowiedzi mogą dotyczyć innych powodów. Otwieranie pliku z długimi liniami nie jest szerokim tematem tylko dlatego, że może to być problematyczne z różnych powodów, czasami masz takie pliki i musisz na nie spojrzeć, najlepiej za pomocą Emacsa.
tarsjusz
1

W moich buforach w trybie powłoki (powłoka Mx), próbuję sed -r 's/(.{2000}).*/\1/' -uunikać długich linii.

David Chandler
źródło
To odpowiada na drugą część pytania: jak poprawić wydajność. Nie dotyczy pierwszej części (co jest OK): „ Dlaczego Emacs ma tak słabą wydajność przy długich liniach ?”
Drew
0

Używam następującej funkcji do otwierania w dired-modedużych plikach z długimi liniami:

(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
źródło
0

Oto obejście, zaczerpnięte z 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
źródło
W Emacsie od 24.4 longlines-modeoznaczono jako przestarzałe visual-line-mode.
Alexander I.Grafov
Jednak te dwie funkcje robią bardzo różne rzeczy za kulisami i visual-line-modenie pomaga w omawianym problemie, podczas gdy longlines-moderobi. Z tego powodu oczekuję, że longlines.el zostanie przywrócony do stanu nieaktualnego.
phils