18

I recorded a simple macro and replayed it on following lines, but it seems replay speed is very slow (handle a very few lines per second). Is this expected and is there a way to improve such speed?

2
  • 6
    Try :set lazyredraw (:h lazyredraw) Commented Aug 13, 2015 at 1:10
  • 1
    Indeed lazyredraw is probably the best solution. You could also provide your macro and a sample file so we can see if it can be optimized. Commented Aug 13, 2015 at 18:42

2 Answers 2

18

Setting the lazyredraw with :set lazyredraw will greatly improve macro execution speed by not redrawing the screen while a macro is executing or a command is run other than those you type, see :help 'lazyredraw' for more information.

Another way to reduce make sure you don't have any time wasting autocmds or mappings. If your macros are still slow after running, check your vimrc file for slow or unnecessary mappings and remove unneeded plugins. In my case I had an autocmd that would run and external program to turn off CapsLock when exiting insert mode, this was convenient and normally unnoticeable but it slowed down insert macros a lot.

If your macro enters insert mode, where you have a lot of mappings, it may help to have a key to set paste mode and disable insert mappings temporarily, see :help 'paste' and :help 'pastetoggle'. Keep in mind this could make insertations more difficult.

Finally, keep the macro as simple as possible by minimizing movement, mode switching, etc.

Good Luck.

3

Reason

Slow macro running is mainly affected by plugins, pasting, not by screen redrawing.

filetype syntax and lazyredraw only have a very, very, very minimal impact. The correct solution should be finding out and disabling those plugins which slows down your editing in insert mode.

Macro is just replaying actions stored in the register onto the selected region. The environment of running the macro is the same as the env you record the macro. Plugin may introduce additional cost during editing. Commonly it's not a problem. But multiplying the cost with 40,000 amplifies the impact dramatically.

Here's the impact factor on macro running I got:

system clipboard >> plugins >> filetype, syntax 

Solution

System Clipboard

Avoid accessing system clipboard in the macro.

Accessing external system clipboard +, * introduces additional cost, when compared with accessing internal registers. It may even freeze the macro replaying forever (run macro on 6000 lines in my test).

imap and event

Disable plugins which affects the editing speed

  • Those do insert mapping, e.g. imap <CR> <Tab> ...
    • jiangmiao/auto-pairs
    • Raimondi/delimitMate
    • ...
  • :noautocmd :norm @q to disable events temporarily during macro running. Or set eventignore=all before macro running, and set it back after.
  • ~~Those register hooks related with editing~~ (Seems not needed)
    • Related hooks
      • CursorMoved(I), CursorHold(I)
      • InsertCharPre, InsertEnter, InsertLeave(Pre)
    • neoclide/coc.nvim
    • brglng/vim-im-select
    • chrisbra/Colorizer
    • ...

Note:

  • Not all plugins registering functions on above hooks make a big differentce on macro running speed. Some of them only have a trivial, negligible impact on the editing speed. You have to find them by replaying macro with and without the plugin and comparing the time cost.
  • Insert mode mapping from auto-pairs couldn't be disabled completely, which is one of reasons I switched to delimitMate.
  • To use :noautocmd on selection. The range should be put before norm: :noa '<,'>norm! @q

Here's a toggle function I made to disable above plugins before running macro.

Use <F3>m to disables plugins before recording a macro. After running the macro, <F3>m again to reset the states of these plugins. (I group all my toggles under <F3>, m stands for macro. You can adjust the mapping for your need.)

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

Ref

  • :h autocmd, :h noautocmd, :h eventignore
  • Thank @ChristianBrabandt for mentioning :noautocmd, eventignore
4
  • I don’t think any of those executes are necessary. You’re just running commands. They can be entered as if they were regular commands (e.g., silent! CocDisable or ImSelectDisable) Commented Jul 22, 2021 at 16:46
  • @D.BenKnoble Thanks. Updated the function. Commented Jul 22, 2021 at 16:50
  • 2
    you should be able to use :noa! :norm! @a to execute the macro without running autocommands (or alternatively, use :set eventignore=all before running the macro. Commented Jul 22, 2021 at 16:58
  • @ChristianBrabandt Thank you for mentioning noautocmd and eventignore. Never heard about them. Commented Jul 23, 2021 at 1:20

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.