Speedup neovim

I maintain same neovim+tmux development environment in all my machines which also includes extremely slow free-tier cloud servers. If you ever have experienced performance issues with neovim(or vim) like me, here are some tips and tricks you might find useful. Some of them are absolutely unnecessary if you are fine with your current configuration. But yes, overengineering workflow is always fun.

Here’s my vimrc.

Profile

First thing you should do to tackle performance issues is profiling. Vim includes a native profiling system. Check :help profile, :help --startuptime. But most of the performance issue is related to plugin’s startuptime and you might want to check vim-startuptime for this.

$ vim-startuptime -vimpath nvim 
Extra options: []
Measured: 10 times

Total Average: 32.805900 msec
Total Max:     33.308000 msec
Total Min:     32.470000 msec

  AVERAGE       MAX       MIN
------------------------------
13.683300 13.860000 13.560000: /home/rok/.config/nvim/init.vim
 4.965100  5.058000  4.902000: /usr/share/nvim/runtime/filetype.vim
 3.421500  3.514000  3.363000: reading ShaDa
 1.750200  1.808000  1.724000: loading plugins
 1.682500  1.755000  1.624000: loading packages

Find functions, plugins that slow down startup. Running tmux in background, I run vim per projects. As I frequently start/exit whole vim instance(which is really bad habit that I just don’t fix), I try to maintain startuptime under 50 msec.

Disable standard plugins

Did you ever use netrw, archive view in vim, matchparen(which is based on regex)? If not, you should disable all of it. There are better alternatives which is better in most ways and can be lazyloaded.

let g:loaded_matchparen        = 1
let g:loaded_matchit           = 1
let g:loaded_logiPat           = 1
let g:loaded_rrhelper          = 1
let g:loaded_tarPlugin         = 1
" let g:loaded_man               = 1
let g:loaded_gzip              = 1
let g:loaded_zipPlugin         = 1
let g:loaded_2html_plugin      = 1
let g:loaded_shada_plugin      = 1
let g:loaded_spellfile_plugin  = 1
let g:loaded_netrw             = 1
let g:loaded_netrwPlugin       = 1
let g:loaded_tutor_mode_plugin = 1
let g:loaded_remote_plugins    = 1

Visual settings

These kinds of visual helpers cause too much screen redraw and significant lag on large files. Turn it on when you need it.

set nonumber
set norelativenumber
set nocursorcolumn
set nocursorline

Syntax highlighting of vim uses regex match. I believe it is one of the slowest operations in vim but there’s not much we can do with it. But these options might help a bit.

And, if syntax file is slow, foldmethod=syntax might also cause a lot of issues for large files like megabytes of json or minimized javascript codes.

set synmaxcol=200
set lazyredraw

" Change foldmethod for specific filetype
au! BufNewFile,BufRead *.json set foldmethod=indent

Hope treesitter fix this in the future.


Many colorscheme plugins cause extreme slow startuptime. Check u/-romainl- ’s comment on this. Color scheme based on these frameworks is recommended.

If you like monokai, this is my own version of it. vim-monokai-pro. Here’s startuptime.

AVERAGE   MAX       MIN 
0.474000  0.724000  0.305000

Plugins

Vim supports lazy loading with “autoload”. Plugins should not slow down your startup time if it’s written in proper way. But many of them, even famous ones, doesn’t consider startuptime issue seriously.

For example, here’s startuptime comparison with vim-fugitive and gina.vim.

1.257800  1.283000  1.242000: /home/rok/.local/share/nvim/site/pack/paqs/start/vim-fugitive/plugin/fugitive.vim
0.022200  0.023000  0.022000: /home/rok/.local/share/nvim/site/pack/paqs/start/gina.vim/plugin/gina.vim

And vim-easymotion also have huge plugin/ files that would have been much better if is autoloaded.

3.766800  3.910000  3.686000: /home/rok/.local/share/nvim/site/pack/paqs/start/vim-easymotion/plugin/EasyMotion.vim

I tried to maintain fork of the few plugins, but as now vim has :packadd which is vim’s native package loading system, we can circumvent this issues without fixing plugin itself.

But first of all, you should use plugin manager that works well with :packadd instead of famous vim-plug. I use savq/paq-nvim written in lua. With only much much readable 150 LOC, it supports all the features I need :PaqInstall,:PaqClean, :PaqUpdate. The most amazing part I realized later on is that you can even make paq itself optional. And with packadd you can do pure lazyloading without any plugin management support like this.

" fzf/fzf.vim
nnoremap <silent><leader>rg :packadd fzf \| :packadd fzf.vim \| :Rg<cr>

" markdown-preview.nvim
command! MarkdownPreview packadd markdown-preview.nvim | call mkdp#util#open_preview_page()"

" phaazon/hop.nvim
nmap <silent><Leader>w :packadd hop.nvim \| :lua require'hop'.jump_words()<cr>

" lazy loading by filetypes
autocmd! FileType go packadd vim-goaddtags | packadd nvim-go
autocmd! FileType yaml packadd vim-yaml-folds
autocmd! FileType markdown packadd vim-markdown | packadd vim-markdown-folding | packadd md-img-paste.vim
autocmd! FileType qf packadd quickfix-reflector.vim

With these tricks, I could reduce 20ms of startuptime compared to the vim-plug setup. This kind of setup was not was easy to configure with vim-plug’s lazyloading features. And I felt that plug itself get quite slow with huge, complex configs.

In terms of the plugin itself, many of them are newly developed or rewritten from scratch by awesome developers everyday. Many of them feel much better as they use latest vim/neovim’s core features or developed in lua. Especially neovim is adopting many features of plugins into the core, like lsp and treesitter. There’s also telescope.nvim which replaces fzf. You might want to check r/neovim, gitter/neovim for latest updates.