Como evito linhas extremamente longas, tornando o Emacs lento?


72

Vejo um desempenho extremamente variado, dependendo de quantas novas linhas existem no arquivo que estou visitando.

Aqui está um exemplo. Eu tenho dois arquivos 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

Estes são dois arquivos JSON com o mesmo conteúdo. one_line.jsontem 18MiB de JSON sem novas linhas. pretty_printed.jsonadicionou novas linhas e espaços em branco, tornando-o 41MiB.

No entanto, o arquivo maior dividido em muitas linhas é muito mais rápido para abrir no Emacs, tanto no modo Javascript quanto no modo Fundamental.

Por que o Emacs tem um desempenho tão ruim com longas filas, já que na verdade são menos bytes? Há algo que eu possa fazer para melhorar o desempenho sem reformatar os dados fora do Emacs?


2
Não é realmente uma resposta, mas pode ser útil: View Large Files(vlf) é um modo secundário que visa ajudar na edição de arquivos grandes, carregando-os em lotes . Isenção de responsabilidade: nunca usei e não sei se também lida com longas filas em lotes .
elemakil

3
Conhecendo esse tipo de comportamento, e especialmente ao tentar me proteger contra a leitura de um log que cospe uma longa fila, geralmente faço algo como $ tail -f /some/file | fold -sem um buffer de shell. Obviamente, isso não é bom para edição, mas ajuda muito na leitura.
Wdxvw

Respostas:


50

O manuseio de linhas longas da Emacs não é muito bem otimizado. Para várias operações, o Emacs precisa varrer toda a linha repetidamente. Por exemplo, para exibir uma linha, o Emacs precisa descobrir a altura da linha, o que requer a varredura de toda a linha para encontrar o glifo mais alto. Além disso, a digitalização para exibição bidirecional consome muito tempo. Você pode obter algumas informações adicionais, por exemplo, na sequência de documentos de cache-long-line-scans(renomeada cache-long-scansem 24.4).

Você pode tentar e ver se a configuração bidi-paragraph-directionpara left-to-rightmelhora a velocidade para você [definindo bidi-display-reorderinga nil, faz mais ou menos o mesmo, mas destina-se apenas para fins / depuração internas]. Isso remove um colaborador significativo das varreduras de linha, mas infelizmente não é o único.

A melhor opção é adicionar novas linhas. Você pode canalizar um arquivo JSON, por exemplo, python -c 'import json, sys ; json.dump(json.load(sys.stdin), sys.stdout, indent=2)'para adicionar novas linhas e melhorar a legibilidade em geral.


4
Por curiosidade, isso é algo que não pode ser aprimorado algoritmicamente?
PythonNut

9
Ao escolher a estrutura de dados subjacente de um editor, você deve escolher entre certos prós e contras. O Emacs usa um buffer de hiato , que é uma estrutura de dados altamente eficiente em termos de espaço para inserção e exclusão, mas torna as operações baseadas em linha mais lentas, pois você precisa procurar sequencialmente uma nova linha. O Emacs poderia usar uma estrutura de dados diferente, mas isso tornaria outras operações mais lentas. O Emacs já usa um cache de linha, mas isso realmente não ajuda em todas as situações. Portanto, não é fácil melhorar algoritmicamente, mas criar perfis e otimizar nunca é demais. :-)
Jorgen Schäfer

4
(setq-default bidi-display-reordering nil)- alguns usuários podem não perceber que essa é uma variável local do buffer, que pode precisar de uma configuração padrão na medida em que um usuário deseja que isso seja global. Eu gostaria de ter acrescentado isso aos meus init.elanos atrás ... mas pelo menos está lá agora. Muito obrigado !!!
LawList #

No meu caso, não era um grande improvent (realmente linhas JSON longos com corpo documentos base64), mas ajuda muito no congelamento beign
anquegi

11
O atual mantenedor do Emacs, Eli, que escreveu o código BIDI, escreve isso sobre como desativar bidi-display-reordering: "Um comentário que tenho é que desabilitar a reordenação de exibição de bidi ... coloca o mecanismo de exibição em um estado que não está sendo testado e pode causar inconsistências. e até erros (porque algumas partes do código foram escritas sob a suposição de que essa variável nunca é nula) ".
Clément

18

Fiz algumas breves experiências com isso usando uma cópia minificada do jquery. font-lock-modee flycheck-modeambos contribuíram para a lentidão, como fizeram js2-mode, e prettify-symbols-mode. line-number-modee column-number-modeteve efeito menor. Uma vez eu desliguei todos os modos diferentes, embora o desempenho fosse relativamente rápido. Use C-h me comece a desativar os diferentes modos que estão habilitados ou tente apenas mudar para fundamental-mode.

Curiosamente, hexl-modeeu poderia voar através do arquivo sem nenhum problema, embora obviamente as colunas fossem bem curtas. Infelizmente, visual-line-moderealmente diminuiu as coisas.

Meu palpite é que a tabela de sintaxe fica feliz em parar o processamento nas terminações de linha e, quando tudo está em uma linha, ele deve revisar tudo a cada atualização.


2
Você pode abrir um relatório de bug no rastreador do Flycheck? Tenho certeza de que não queremos longas filas causando problemas, e o Emacs + Flycheck não deve ser pior que o Emacs (que ainda é muito ruim).
Clément

16

Fiz upload de http://www.emacswiki.org/emacs/OverLongLineMode

Essa biblioteca permite definir limites simples de comprimento de linha além dos quais uma variante de fundamental-modeserá usada para um arquivo em vez de seu modo normal (apenas para modos de programação).

Potencialmente, algo nesse sentido poderia ser adicionado ao Emacs por padrão, mas isso pode ser uma solução provisória para o problema principal do Emacs que fica lento ao rastrear ao encontrar esse arquivo.

Nota: isso é uma melhoria no código que eu postei inicialmente nesta resposta, mas ainda é um trabalho em andamento. Os testes foram mínimos. Comentários são bem-vindos.

Sugestões para outros (além css-mode) prog-modemodos principais não derivados para suporte por padrão também são bem-vindas.


11
Agora melhorado e vergonhosamente renomeado para so-long.el :) (o link acima será redirecionado). Pode-se fazer mais com isso, mas é 100% funcional e útil como está.
Phill #

Esta é uma solução muito boa (adoraria vê-la no MELPA), mas minha instância do Emacs ainda é extremamente lenta ao abrir o one_line.json. Eu acho que seria significativamente mais rápido se não ativasse primeiro o modo principal original.
Wilfred Hughes

3
Relendo isso e usando o arquivo one_line.json da pergunta, desisti de esperar que o Emacs 25.3 e 26.0.91 de configuração padrão respondesse depois de pedir para abrir o arquivo (depois de esperar mais de um minuto), enquanto o meu config with so-long.elactive abriu o arquivo em menos de 2 segundos. A edição do arquivo ainda é extremamente problemática (por exemplo, tentar passar para a 'próxima linha' levará um tempo extremamente longo), mas, no entanto, isso restaura minha fé na utilidade da biblioteca que escrevi, por isso devo retomar meus planos para adicione-o ao GNU ELPA ...
phils

11
Já está em (M) ELPA?
binki

3
Relatório de status: a versão 1.0 do so-long.el(com inúmeras melhorias) está incluída nas versões atuais de desenvolvimento do Emacs 27 e estará disponível (para versões anteriores do Emacs) via GNU ELPA em algum momento no futuro próximo.
phils 13/07

7

Espero que você descubra que a diferença se deve a isso font-lock. Quando a fonte é executada no subconjunto do arquivo visível na janela, ela prossegue estendendo primeiro a região da fonte, de forma a incluir unidades semânticas completas. Veja o font-lock-extend-region-functionscódigo para isso. É comum que isso inclua a extensão da região para incluir linhas completas. Quando as linhas são extremamente longas, isso pode fazer com que a fonte seja executada em um pedaço de conteúdo muito maior do que é realmente visível.

Além disso, quando as novas linhas possuem informações semânticas, sua ausência às vezes pode significar que os padrões de expressão regular para bloqueio de fonte precisam ser varridos ainda mais para determinar se são correspondentes ou não.


7

Normalmente, desenrolo linhas longas e recuo por tags (como HTML, XML, JSON).

Para possibilitar essa operação, adiciono:

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

Eu dividir linha de regex, para XML-lo: C-M-% >< RET >NL< RET !.

Depois que o Emacs divide as linhas longas - é possível ativar muitos *-modese re-recuar o código.

Nota: Como evitar a desaceleração quando um processo inferior gera longas filas?


4

Criei minha própria solução para esse problema aqui: https://github.com/rakete/too-long-lines-mode

Eu não estava satisfeito com a solução phils, que alterna um buffer com linhas muito longas para o modo fundamental; queria uma solução que me permitisse manter o destaque da sintaxe e outros recursos do modo principal. Então, criei um modo menor que usa sobreposições para ocultar a maioria dos caracteres de linhas muito longas.

Isso soluciona o problema e torna o emacs utilizável mesmo em buffers com linhas muito longas, sem precisar voltar ao modo fundamental.


2

Na minha configuração do Emacs, eu tenho um modo com fonte personalizada, ou seja, onde eu configuro font-lock-defaults. Uma única página para baixo usaria 30 segundos para exibir parte da linha de 30000 caracteres. Esse lentidão foi corrigido através da redução do retorno do regexp. Ao invés de:

  (". * terminou com um comando incompleto *" 0 font-lock-comment-face)

fazem isto

  ("^. \ {1,80 \} terminou com um comando incompleto *" 0 font-lock-comment-face)

Esta não é uma resposta para a pergunta, que não é especificamente sobre font-lock-defaultscorrespondência ou regexp.
Tirou

11
@ Drew Regex abaixo do ideal está tornando o bloqueio de fonte lento em linhas longas ...
wasamasa 27/03

11
@wasamasa: Sim. A questão em si é muito ampla, IMO. Há muitas coisas que podem retardar o Emacs (e para quais ações?) Quando longas filas estão envolvidas.
Tirou

3
Não acho que a questão seja ampla ("por que as longas filas tornam o Emacs lento")? Também não acho que a resposta não atenda à pergunta (" uma razão possível é regexps abaixo do ideal"). Outras respostas podem abordar outros motivos. Abrir um arquivo com longas filas não é um tópico amplo, apenas porque isso pode ser problemático por vários motivos; às vezes, você possui esses arquivos e precisa vê-los, de preferência usando o Emacs.
tarsius 27/03

1

Nos meus buffers de modo shell (shell Mx), eu me pego tentando sed -r 's/(.{2000}).*/\1/' -uevitar longas filas.


Isso responde à segunda parte da pergunta: como melhorar o desempenho. Ele não aborda a primeira parte (o que é aceitável): " Por que o Emacs tem desempenho tão ruim em longas filas ?"
Drew

0

Eu uso a seguinte função para abrir dired-modearquivos grandes com linhas longas:

(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

Aqui está uma solução alternativa, retirada do 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)))))

No Emacs, a partir de 24.4, longlines-modefoi marcado como obsoleto por visual-line-mode.
Alexander I.Grafov

No entanto, os dois recursos fazem coisas muito diferentes nos bastidores, e visual-line-modenão ajudam com o problema em questão, enquanto o longlines-modefazem. Por esse motivo, espero que longlines.el seja restaurado para um status não obsoleto.
phils 16/07
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.