Как я могу предотвратить очень длинные строки, делающие Emacs медленным?


72

Я вижу сильно различающуюся производительность в зависимости от того, сколько новых строк есть в файле, который я посещаю.

Вот пример. У меня есть два файла 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

Это два файла JSON с одинаковым содержимым. one_line.jsonсоставляет 18 МБ JSON без каких-либо новых строк. pretty_printed.jsonдобавлены символы новой строки и пробелы, что делает его 41MiB.

Однако больший размер файла, разбитый на несколько строк, намного быстрее открывается в Emacs, как в режиме Javascript, так и в основном режиме.

Почему Emacs имеет такую ​​низкую производительность с длинными строками, поскольку на самом деле меньше байтов? Что я могу сделать, чтобы улучшить производительность, не переформатируя данные вне Emacs?


2
Не совсем ответ, но может быть View Large Filesполезным : (vlf) - второстепенный режим, который предназначен для помощи при редактировании больших файлов путем их загрузки в пакетном режиме . Отказ от ответственности: я никогда не использовал это, и я не знаю, обрабатывает ли это также длинные строки партиями .
elemakil

3
Зная такое поведение, особенно когда пытаюсь защитить себя от чтения журнала, который выплевывает длинную строку, я часто делаю что-то вроде $ tail -f /some/file | fold -sбуфера оболочки. Очевидно, что это плохо для редактирования, но очень помогает при чтении.
wvxvw

Ответы:


50

Обработка длинных строк в Emacs не очень хорошо оптимизирована. Для ряда операций Emacs должен многократно сканировать всю строку. Например, чтобы отобразить строку, Emacs должен вычислить высоту строки, что требует сканирования всей строки, чтобы найти самый высокий глиф. Кроме того, сканирование для двунаправленного отображения отнимает много времени. Вы можете получить дополнительную информацию, например, в строке документации cache-long-line-scans(переименованной cache-long-scansв 24.4).

Вы можете попробовать и посмотреть , если установка bidi-paragraph-directionдля left-to-rightулучшает скорость для вас [заходящего bidi-display-reorderingк nil, делает более или менее то же самое , но предназначен только для внутренних целей / отладки]. Это удаляет один значительный вклад в сканирование строк, но, к сожалению, не единственный.

Лучший вариант - добавить новые строки. Вы можете передать JSON-файл через, например, python -c 'import json, sys ; json.dump(json.load(sys.stdin), sys.stdout, indent=2)'добавить новые строки и улучшить читаемость в целом.


4
Из любопытства, это не может быть улучшено алгоритмически?
PythonNut

9
При выборе базовой структуры данных редактора, вы должны выбирать между определенными плюсами и минусами. Emacs использует буфер-пробел , который представляет собой высокоэффективную структуру данных для вставки и удаления, но он замедляет операции на основе строки, так как вам нужно последовательно сканировать новую строку. Emacs может использовать другую структуру данных, но это замедлит выполнение других операций. Emacs уже использует строковый кеш, но это не очень помогает во всех ситуациях. Таким образом, это не так просто улучшить алгоритмически, но профилирование и оптимизация никогда не повредит. :-)
Йорген Шефер

4
(setq-default bidi-display-reordering nil)- некоторые пользователи могут не осознавать, что это локальная переменная буфера, которая может нуждаться в настройке по умолчанию, если пользователь хочет, чтобы она была глобальной. Я бы хотел добавить это к моим init.elгодам назад ... но, по крайней мере, сейчас. Огромное спасибо!!!
законник

В моем случае это не было большой неудачей (действительно длинные строки json с телом документов base64), но очень помогло при замораживании
beign

1
Текущий сопровождающий Emacs, Eli, который написал код BIDI, пишет об отключении bidi-display-reordering: «У меня есть один комментарий: отключение переупорядочивания двунаправленного отображения… переводит механизм отображения в состояние, которое не тестируется, и может вызвать несоответствия и даже ошибки (потому что некоторые части кода были написаны в предположении, что эта переменная никогда не равна нулю). "
Климент

18

Я провел несколько кратких экспериментов с этим, используя уменьшенную копию jquery. font-lock-modeи flycheck-modeоба способствовали медлительности, как и js2-mode, и prettify-symbols-mode. line-number-modeи column-number-modeимел незначительный эффект. Однажды я отключил все разные режимы, хотя производительность была относительно быстрой. Используйте C-h mи начните отключать различные режимы, которые включены, или попробуйте просто переключиться на fundamental-mode.

Интересно, что с помощью hexl-modeя мог пролистать файл без каких-либо проблем, хотя, очевидно, столбцы были довольно короткими. К сожалению, visual-line-modeдействительно все замедлилось.

Я предполагаю, что таблица синтаксиса готова прекратить обработку в конце строки, а когда она находится в одной строке, она должна анализировать все при каждом обновлении.


2
Можете ли вы открыть отчет об ошибке на трекере Flycheck? Я почти уверен, что мы не хотим, чтобы длинные строки вызывали проблемы, и Emacs + Flycheck не должен быть хуже, чем Emacs (что все еще довольно плохо).
Clément

16

Я загрузил http://www.emacswiki.org/emacs/OverLongLineMode

Эта библиотека позволяет вам устанавливать простые пороговые значения длины строки, после которых вариант fundamental-modeбудет использоваться для файла вместо его обычного режима (только для режимов программирования).

Потенциально что-то в этом роде может быть добавлено в Emacs по умолчанию, но это может быть временным обходным решением для основной проблемы замедления Emacs до сканирования при обнаружении такого файла.

nb Это улучшение кода, который я изначально разместил в этом ответе, но все еще в стадии разработки. Тестирование было минимальным. Комментарии приветствуются.

Также приветствуются предложения для других (помимо css-mode) prog-modeосновных типов, не являющихся производными, для поддержки по умолчанию.


1
Теперь дополнительно улучшено и позорно переименовано в so-long.el :) (указанная выше ссылка будет перенаправлена). С этим можно многое сделать, но он на 100% функциональный и полезный как есть.
Филс

Это действительно хорошее решение (хотелось бы увидеть его на MELPA), но мой экземпляр Emacs все еще очень медленный при открытии one_line.json. Я думаю, что было бы значительно быстрее, если бы он сначала не активировал оригинальный основной режим.
Уилфред Хьюз

3
Перечитав это и используя ваш файл one_line.json из вопроса, я перестал ждать ответа от Emacs 25.3 и 26.0.91 по умолчанию, попросив их открыть этот файл (после ожидания более минуты), в то время как мой собственный Конфиг с so-long.elактивным открыл файл менее чем за 2 секунды. На самом деле редактирование файла по-прежнему чрезвычайно проблематично (например, попытка перейти к «следующей строке» займет очень много времени), но, тем не менее, это восстанавливает мою веру в полезность библиотеки, которую я написал, поэтому я должен возобновить свои планы по добавить его в GNU ELPA ...
phils

1
Это в (M) ELPA еще?
binki

3
Отчет о состоянии: версия 1.0 so-long.el(с многочисленными улучшениями) включена в текущие версии разработки Emacs 27 и будет доступна (для более ранних версий Emacs) через GNU ELPA когда-нибудь в ближайшем будущем.
Фил

7

Я ожидаю, что вы обнаружите, что разница связана с font-lock. Когда фонификация должна быть выполнена для подмножества файла, который виден в окне, она сначала расширяет область фонификации так, чтобы она включала полные семантические единицы. Смотрите font-lock-extend-region-functionsкод для этого. Обычно это включает в себя расширение области для включения полных строк. Когда строки очень длинные, это может привести к тому, что фонификация будет выполняться по гораздо большему фрагменту контента, чем на самом деле видно.

Кроме того, когда сами новые строки имеют семантическую информацию, их отсутствие может иногда означать, что шаблоны регулярных выражений для блокировки шрифта должны сканироваться дальше, чтобы определить, соответствуют ли они или нет.


7

Я обычно развертываю длинные строки и делаю отступ по тегам (например, HTML, XML, JSON).

Чтобы сделать такую ​​операцию возможной, я добавляю:

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

Я разделил линию регулярных выражений для XML этого: C-M-% >< RET >NL< RET !.

После того, как Emacs разделит длинные строки - можно включить множество *-modesи заново сделать отступ кода.

Для заметки: как предотвратить замедление, когда низшие процессы генерируют длинные строки?


4

Я создал собственное решение этой проблемы здесь: https://github.com/rakete/too-long-lines-mode

Я не был удовлетворен решением phils, которое переключает буфер с очень длинными строками в основной режим, я хотел решение, которое позволит мне сохранять подсветку синтаксиса и другие функции основного режима. Поэтому я создал вспомогательный режим, который использует наложения, чтобы скрыть большинство символов слишком длинных строк.

Это решает проблему и делает emacs пригодным для использования даже в буферах с очень длинными строками, без необходимости возврата к основному режиму.


2

В моей установке Emacs У меня есть режим с пользовательскими, то есть подсветка не делается , где я поставил font-lock-defaults. Одна страница вниз будет использовать 30 секунд для отображения части строки 30000 символов. Это замедление было исправлено путем уменьшения обратного отслеживания регулярных выражений. Вместо:

  (". * закончилось неполной командой *" 0 font-lock-comment-face)

сделай это

  («^. \ {1,80 \} завершился неполной командой *» 0 font-lock-comment-face)

Это не ответ на вопрос, который не касается font-lock-defaultsили соответствия регулярному выражению.
Дрю

1
@Drew Менее идеальное регулярное выражение будет делать шрифт блокировку медленно на длинных линиях , хотя ...
wasamasa

1
@wasamasa: Да. Сам вопрос слишком широкий, ИМО. Есть много вещей, которые могут замедлить Emacs (и для каких действий?), Когда задействованы длинные очереди.
Дрю

3
Я не думаю, что вопрос к широким («почему длинные строки делают Emacs медленным»)? Я также не думаю, что ответ не касается вопроса (« одна из возможных причин - неоптимальные регулярные выражения»). Другие ответы могут касаться других причин. Открытие файла с длинными строками не означает широкую тему только потому, что это может быть проблематично по разным причинам, иногда у вас есть такие файлы, и вам нужно просмотреть их, желательно с помощью Emacs.
tarsius

1

В моих буферах режима оболочки (оболочка Mx) я обнаруживаю, что стараюсь sed -r 's/(.{2000}).*/\1/' -uизбегать длинных строк.


Это отвечает на вторую часть вопроса: как улучшить производительность. Это не относится к первой части (которая в порядке): « Почему Emacs имеет такую ​​низкую производительность с длинными строками
Дрю

0

Я использую следующую функцию для открытия dired-modeбольших файлов с длинными строками:

(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

Вот обходной путь, взятый из 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)))))

В Emacs по состоянию на 24.4 longlines-modeпомечены как устаревшие visual-line-mode.
Александр Иванович Графов

Однако эти две функции делают совершенно разные вещи за кулисами, и visual-line-modeне помогают с рассматриваемой проблемой, в то время как longlines-modeделает. По этой причине я ожидаю, что longlines.el будет восстановлен в статусе, не являющемся устаревшим.
Фил
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.