Vimのカーソル移動が重い原因をprofileする

カーソル移動が遅かったのでprofileしてみた。

以下のような関数をvimrcに書く。

```vim
function! ProfileCursorMove() abort
  let profile_file = expand('~/log/vim-profile.log')
  if filereadable(profile_file)
    call delete(profile_file)
  endif

  normal! gg
  normal! zR

  execute 'profile start ' . profile_file
  profile func *
  profile file *

  augroup ProfileCursorMove
    autocmd!
    autocmd CursorHold <buffer> profile pause | q
  augroup END

  for i in range(100)
    call feedkeys('j')
  endfor
endfunction
```

`profile_file`はプロファイルの結果がで出るファイル。`~/log`ディレクトリは予め作っておく。

使い方は

```
$ vim +'call ProfileCursorMove()' <カーソルを動かすのが重いファイル>
```

上記コマンドを実行すると`feedkeys('j')`によって、100回jが実行される。`feedkeys()`はキー実行を待たず、非同期で実行されるので、`profile pause | q`をすぐ下に書くと、キー実行が終わる前にvimが終了してしまう。そこで、`CursorHold`を使い、カーソルが動かなくなったら終了するように書いた。

実行後に`profile_file`の下を見ると、各関数毎にかかった時間が書いてある。

```
FUNCTIONS SORTED ON TOTAL TIME
count  total (s)   self (s)  function
  306   0.106875   0.008378  fugitive#head()
  306   0.083811   0.018949  <SNR>37_repo_head()
  306   0.041424   0.033768  <SNR>37_repo_head_ref()
  612   0.027759             <SNR>37_repo()
  101   0.022314             <SNR>62_Highlight_Matching_Pair()
  306   0.010365             <SNR>37_sub()
  612   0.007656             <SNR>37_repo_dir()
```

ステータスラインにブランチ名を表示させるために使っている`fugitive#head()`が重い。

ステータスラインの表示に[itchyny/lightline.vim](https://github.com/itchyny/lightline.vim)を使っているが、そのREADMEを見るとブランチ名表示用に[itchyny/vim-gitbranch](https://github.com/itchyny/vim-gitbranch)というのがあるので試してみた。

```
FUNCTIONS SORTED ON TOTAL TIME
count  total (s)   self (s)  function
  306   0.046956             gitbranch#name()
  101   0.024812             <SNR>58_Highlight_Matching_Pair()
  204   0.005348             GetCurrentColumn()
  102   0.004285             lightline#link()
```

`fugitive#head()`に比べると倍くらい早いがまだ一番重い。`Highlight_Matching_Pair()`も結構重いが、これは[itchyny/vim-parenmatch](https://github.com/itchyny/vim-parenmatch)というプラグインを代わりに使うと、精度は多少落ちるが軽くなる[^1]。

ただし、このプラグインを使うとマッチした括弧のハイライトが下線になってしまうので、元と同じハイライトにするには、vimrcでcolorschemeを呼び出している箇所より後に

```vim
highlight link ParenMatch MatchParen
```

を書き、どこかに

```vim
let g:parenmatch_highlight = 0
```

を書く。

vim-parenmatchにした結果は、

```
FUNCTIONS SORTED ON TOTAL TIME
count  total (s)   self (s)  function
  306   0.045896             gitbranch#name()
  101   0.005845             parenmatch#update()
  204   0.005454             GetCurrentColumn()
  102   0.003950             lightline#link()
  306   0.002518             MyFileencoding()
```

`parenmatch#update()`のほうが大分早い。

しかし、カーソルを動かしてみるとまだまだ遅い。

['cursorline' を必要な時にだけ有効にする - 永遠に未完成](https://thinca.hatenablog.com/entry/20090530/1243615055)の設定をやってみた。

めっちゃ早くなった。

設定前

```
$ time vim +'call ProfileCursorMove()' <カーソルを動かすのが重いファイル>
vim +'call ProfileCursorMove()'   6.03s user 4.06s system 96% cpu 10.496 total
```

設定後[^2]

```
$ time vim +'call ProfileCursorMove()'  <カーソルを動かすのが重いファイル>
vim +'call ProfileCursorMove()'   1.30s user 0.54s system 91% cpu 2.015 total
```

よくよく考えると、関数で一番重いものでもトータルで0.1秒程度しか寄与してないので、関数は関係なかった。

ちなみにカーソル移動以外でかかっている時間は

```
$ time vim +'q'  <カーソルを動かすのが重いファイル>
vim +'q'   0.87s user 0.29s system 90% cpu 1.276 total
```

なので、カーソル移動には0.7秒くらいかかっているようだ。


[^1]: [Vimの標準プラグインmatchparenが遅かったので8倍くらい速いプラグインを作りました - プログラムモグモグ](https://itchyny.hatenablog.com/entry/2016/03/30/210000): 作者による解説記事。
[^2]: ただし、`+command`で起動するとWinEnterのイベントが発生しないらしく、ブログでは`let s:cursorline_lock = 0`となっている箇所を0にした。