Vim宏非常缓慢

17

我将一个非常简单的vim宏保存到键上:qa$pjq,然后使用40000@a执行它40000次。这非常缓慢。我一定做错了什么,因为虽然它确实能够工作,但大约需要60-90秒时间。vim的速度就是这样快吗?有哪些设置可以加速它?有哪些插件组合会使宏执行变慢吗?

我正在使用Mac和MacVim。这是一个纯文本文件,真的没有比这更简单的了。

5个回答

28

把 40,000 行代码粘贴进去并不需要很长时间,但是如果你的屏幕一直更新,就像宏常常做的那样,这会拖慢速度。

首先,关于你的宏应该做什么的问题。如果只是要粘贴默认寄存器的内容,那么正确的宏定义只是 qapjq。没有必要在上一行的特定位置放置光标。[编辑:抱歉,我假设你要进行行复制,如果你要复制字符寄存器到行尾,则需要定位.. ..]

第二,你可以通过设置 lazyredraw (:set lazyredraw) 来加速当前的宏,这样屏幕在执行过程中就不会更新了。这可能不会大幅提高速度,像这样的键盘宏不像直接处理缓冲区。

第三,这里有另一种方法,大约需要一秒钟:.,+40000g/.*/normal! $p


他不进入插入模式,只是将宏存储在寄存器a中。不过屏幕更新做得很好。 - Don Reba
@Don:哎呀,谢谢。那么我的观点就是没有必要让$符号到达行尾。如果他正在逐个字符地粘贴到行尾,那么这可能是错误的... - Herbert Sitz
哇,这对于运行在大文件上的长宏来说真是救命稻草!时间差异快了一个数量级。 - bigtunacan

10

我建议使用“vim -u NONE”启动空白的vim。 通常插件会拖慢速度。 你会发现这样可以更快地工作。 然后,你需要找出是哪个插件导致了这种减速。 参见如何查看哪些插件使Vim变慢?


闪电般的快速! - Yann VR
Vim是一款绝对的利器。今天在运行一个复杂的宏命令处理几GB的文本文件时,感觉有点缓慢,但实际上是插件的问题。再次运行之前使用“vim -u NONE”命令,只用了一秒钟就完成了。 - jones

9

这是正常现象。如Herbert Sitz所述,更新屏幕的速度较慢。

您可能只想运行替换命令::.,+40000s/.*/&<c-r>"

  • .,+40000 是一个范围,包括当前行和接下来的40000行
  • 模式中的.*匹配整行
  • 字符串中的&是被匹配的行
  • <c-r>"粘贴未命名寄存器的内容

3

原因

我使用40000@a的方式执行宏,但速度非常慢,大约需要60-90秒。我肯定做错了什么,因为虽然它可以工作,但速度太慢了。

其他答案都没有针对主要原因进行解释。宏运行缓慢主要是由插件和粘贴板引起的,与屏幕重绘无关。

filetype语法和lazyredraw功能只会带来非常小的影响。正确的解决方法是找出并禁用那些在插入模式下导致编辑缓慢的插件。

宏只是将记录在寄存器中的操作重放到所选区域。宏的运行环境与录制宏的环境相同。插件可能会在编辑时增加额外的开销。通常这不是问题。但将代价乘以40000倍会极大地增加影响。

下面是我得到的宏运行影响因素:

system clipboard >> plugins >> filetype, syntax

解决方案

系统剪贴板

避免在宏中访问系统剪贴板

与访问内部寄存器相比,访问外部系统剪贴板+*会增加额外的成本。在测试中,运行6000行的宏可能会永远冻结宏重放。

imap和事件

禁用影响编辑速度的插件

  • 那些插入映射,例如 imap <CR> <Tab> ...
    • jiangmiao/auto-pairs
    • Raimondi/delimitMate
    • ...
  • :noautocmd :norm @q 临时禁用事件运行宏。或者在运行宏之前设置set eventignore=all,并在运行后将其设置回来。
  • ~~与编辑相关的那些寄存器钩子~~(似乎不需要)
    • 相关的钩子
      • CursorMoved(I)CursorHold(I)
      • InsertCharPreInsertEnterInsertLeave(Pre)
    • neoclide/coc.nvim
    • brglng/vim-im-select
    • chrisbra/Colorizer
    • ...

注意

  • 并非所有插件在上述钩子上注册函数都会对宏运行速度产生很大影响。其中一些只会对编辑速度产生微不足道的影响。您必须通过比较有插件和没有插件时重放宏所需的时间成本来找到它们。
  • auto-pairs中的插入模式映射无法完全禁用,这是我切换到delimitMate的原因之一。
  • 要在选择上使用:noautocmd命令,范围应该放在norm之前::noa '<,'>norm! @q

这是一个我制作的切换函数,在运行宏之前禁用以上插件。

使用<F3>m 在录制宏之前禁用插件。运行宏后,<F3>m 再次重置这些插件的状态。(我将所有切换都分组在<F3>下,m代表宏。您可以根据需要调整映射。)

function! <SID>ToggleMacro(...)
  " Optimize macro running by disable some plugins.
  if get(g:, '_macro', {}) ==# {}
    let g:_macro = {'state': 1}

    let g:_macro.auto_pairs = get(b:, 'autopairs_enabled')
    if g:_macro.auto_pairs
      let b:autopairs_enabled = 0
    endif

    let g:_macro.delimit_mate = get(b:, 'delimitMate_enabled')
    if g:_macro.delimit_mate
      DelimitMateOff
    endif

    let g:_macro.eventignore = &eventignore
    set eventignore=all

  else
    let g:_macro.state = 0

    let b:autopairs_enabled = g:_macro.auto_pairs

    if g:_macro.delimit_mate
      DelimitMateOn
    endif

    let &eventignore = g:_macro.eventignore

  endif

  if g:_macro.state
    echo 'Macro boost: On'
  else
    echo 'Macro boost: Off'
    unlet g:_macro
  endif
endfunction

nnoremap <F3>m  :call <SID>ToggleMacro()<CR>
command! -nargs=? ToggleMacro call <SID>ToggleMacro(<f-args>)

参考资料

  • :h autocmd:h noautocmd:h eventignore
  • 感谢Christian Brabandt提到:noautocmdeventignore

1
我在使用VIM宏操作少量行时遇到了速度非常慢的问题(尽管更改很复杂)。使用se synmaxcol=1se ft=txtse foldmethod=manual可以很大程度上帮助解决这个问题。VIM似乎不够智能,无法等待宏完成后再更新语法高亮和折叠等其他更改。说实话,这是VIM的缺点。

真的,语法高亮确实会影响宏重放时间。在我的情况下,"set syntax=" 命令帮了我很大忙。 - Oleg Andriyanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接