Windows の Vim で system() を高速化してみた
Windows の Vim では system() が遅いと言われている(?)んですがそれを vimproc を使用して高速化してみました。
まだ簡易版なんですが system() と比べてだいたい 3〜15倍ほど早くなりました。
[ソース]
let s:cmd = {} function! s:cmd.open() let cmd = "cmd.exe" let self.vimproc = vimproc#popen3(cmd) while 1 let output = self.vimproc.stdout.read() if output =~ '.\+>$' break endif endwhile endfunction function! s:cmd.close() call self.vimproc.stdout.close() call self.vimproc.stderr.close() call self.vimproc.waitpid() endfunction function! s:cmd.system(cmd) let result = "" call self.vimproc.stdin.write('(cd '. shellescape(getcwd()) .' & ' . a:cmd.")\n") while 1 let output = self.vimproc.stdout.read() if empty(result) && empty(output) continue endif if output =~ '.\+>$' let result .= output break endif let result .= output endwhile let result = substitute(result, "\r\n", "\n", "g") return join(split(result, "\n")[1:-2], "\n") endfunction call s:cmd.open() echo s:cmd.system("ls") echo s:cmd.system("echo %date%") call s:cmd.close()
[出力]
foo.txt hoge.vim main.vim test.vim 2013/06/11
これ、何をやっているのかというと cmd.exe を外部プロセスとして常駐させて置いてそのプロセスに対して ls や echo といったコマンドを投げつけて実行させていま。
こうすることで cmd.exe を起動させることなくコマンドを実行することが出来るので高速化を図ることが出来ます。
(これに関する詳しい説明は VAC で ujihisa さんが解説してくれるはず…!
で、実際に実行時間を比較するとこんな感じです。
[比較コード]
command! -bar TimerStart let start_time = reltime() command! -bar TimerEnd echom reltimestr(reltime(start_time)) | unlet start_time function! s:test1(num, cmd) echo "cmd.exe" call s:cmd.open() TimerStart for i in range(a:num) call s:cmd.system(a:cmd) endfor TimerEnd call s:cmd.close() endfunction function! s:test2(num, cmd) echo "system()" TimerStart for i in range(a:num) call system(a:cmd) endfor TimerEnd endfunction function! s:test3(num, cmd) echo "vimproc#system()" TimerStart for i in range(a:num) call vimproc#system(a:cmd) endfor TimerEnd endfunction function! Test(...) let cmd = get(a:, 1, "ls") let num = get(a:, 2, 10) echo "------------------" echo "== '" . cmd . "' x " . num . " ==" call s:test1(num, cmd) call s:test2(num, cmd) call s:test3(num, cmd) echo "" endfunction call Test("dir", 5) call Test("path", 5) call Test("echo %date%", 5) call Test("attrib main.vim", 5)
[出力]
------------------ == 'dir' x 5 == cmd.exe 0.046523 system() 0.613175 vimproc#system() 0.390333 ------------------ == 'path' x 5 == cmd.exe 0.039159 system() 0.490228 vimproc#system() 0.357809 ------------------ == 'echo %date%' x 5 == cmd.exe 0.030154 system() 0.502048 vimproc#system() 0.349304 ------------------ == 'attrib main.vim' x 5 == cmd.exe 0.252042 system() 0.697622 vimproc#system() 1.131706
こんな感じ。
コード自体はまだ簡易版なんですがだいぶ早くなっていますね。
実際に使用する場合はもうちょっと工夫(タイムアウトとか)する必要がありそう。
あとコマンドの実行が終了した時の判定はどうするのがいいのだろうか。