cafegale(LeafCage備忘録)

LeafCage備忘録(はてなダイアリー)と統一しました。

vim処理速度調査(オートロード関数読み込み編)

以下の内容が書かれたファイルをautoload/speed1.vimとして置く。

function! speed1#nop()
endfunction

同様に、autoload/speed1.vim、autoload/speed2.vim、...としてautoload/speed10.vimまで同様に置く。

そして、以下のスクリプトを:sourceする。
速度計測にはLeafCage/laptime.vimを使用する。

let lt = laptime#new()
call speed1#nop()
call lt.lap()
call speed2#nop()
call lt.lap()
call speed3#nop()
call lt.lap()
call speed4#nop()
call lt.lap()
call speed5#nop()
call lt.lap()
call speed6#nop()
call lt.lap()
call speed7#nop()
call lt.lap()
call speed8#nop()
call lt.lap()
call speed9#nop()
call lt.lap()
call speed10#nop()
call lt.end()

結果は以下のとおり

       TOTAL       LAP
  1:   0.003770    0.003770
  2:   0.007049    0.003279
  3:   0.010308    0.003259
  4:   0.013501    0.003194
  5:   0.017289    0.003788
  6:   0.021382    0.004093
  7:   0.024573    0.003191
  8:   0.027727    0.003153
  9:   0.030940    0.003213
 10:   0.034118    0.003179  

内容の(ほとんどない)ない10のファイルを読み込むだけで計約0.034秒かかっている。

いったい何が原因なのか?
なぜ(ほとんど)空っぽのファイルをsourceするのにここまで時間がかかっているのか?
Vimはautoload関数が呼ばれるとruntimepathから該当ファイルを探してsourceするが、もしやここで時間がかかっているのかもしれない。
私はneobundle.vimプラグインを管理しているが、その代償としてruntimepathに複数のパスが設定されている。
私のVim起動直後のruntimepathは以下のとおりであった。

runtimepath=~/vimfiles,~/box/vimfiles/neobundle/vim-fugitive,~/box/vimfiles/neobundle/vimproc,~/box/vimfiles/neobundle/tlib_vim,~/box/vim
files/neobundle/vital.vim,~/box/vimfiles/neobundle/vim-openbuf,~/box/vimfiles/neobundle/curses-vim,~/box/vimfiles/neobundle/vim-submode,~/b
ox/vimfiles/neobundle/vim-altr,~/box/vimfiles/neobundle/mdv,~/box/vimfiles/neobundle/vimhelpgenerator,~/box/vimfiles/neobundle/laptime.vim,
~/box/vimfiles/neobundle/neocomplcache,~/box/vimfiles/neobundle/neosnippet,~/box/vimfiles/neobundle/vim-operator-user,~/box/vimfiles/neobun
dle/vim-textobj-user,~/box/vimfiles/neobundle/textobj-wiw,~/box/vimfiles/neobundle/vim-textobj-indent,~/box/vimfiles/neobundle/vim-textobj-
plugins,~/box/vimfiles/neobundle/vim-textobj-xbrackets,~/box/vimfiles/neobundle/vim-textobj-parameter,~/box/vimfiles/neobundle/nerdcommente
r,~/box/vimfiles/neobundle/ToggleCase-vim,~/box/vimfiles/neobundle/jasegment.vim,~/box/vimfiles/neobundle/clever-f.vim,~/box/vimfiles/neobu
ndle/vim-ambicmd,~/box/vimfiles/neobundle/vim-altercmd,~/box/vim

そこで今度は:set rtp=~/vimfilesとして、runtimepathのパスを一つだけにしてから同じことをやってみた。

       TOTAL       LAP
  1:   0.001546    0.001546
  2:   0.002974    0.001428
  3:   0.004392    0.001418
  4:   0.005837    0.001445
  5:   0.007338    0.001501
  6:   0.008639    0.001301
  7:   0.009950    0.001311
  8:   0.011222    0.001271
  9:   0.012507    0.001286
 10:   0.014164    0.001656

処理時間が目に見えて短くなった(半分以下に)。すなわち時間がかかっている原因は、一つは探索場所が広大だったせいである。
だが、ちょっと待ってほしい。
確かに0.0015秒と短くなったとはいえ、これでもVimの処理の中では時間がかかってるほうである。拙作のプラグインにlaptime.vimというのがあるが、その関数の一つ、s:lt.end()のリストの要素が10個のときと同じくらいの時間がかかっている(これの実行時間は約0.0016秒)。

これはそれなりにリスト処理や文字列処理を含んでいる。つまりruntimepathを1つにして、探索場所を減らした状態でも、それなりの処理を含む1つの関数くらいの時間はかかっているわけである。

ちなみに、call speed1#nop()などの関数はほとんど何の仕事もしていないのであるが、これ自体の処理時間はおよそ0.0001秒である。二度目の関数呼び出しからはsourceの時間がなくなるので、この関数の時間だけしかかからず処理が高速になる。


参考

let s:test = range(101)
call filter(s:test, 'v:val != "0"')

↑の処理時間は約0.0019秒前後

let s:test = range(101)
call map(s:test, 'v:val. "yen"')
call match(s:test, '25yen')

約0.002秒

let s:test = range(101)
call map(s:test, 'v:val. "yen"')
call filter(s:test, 'v:val != "0yen"')

約0.0039秒前後