Vim - 文字列連結で再代入する場合はjoinを使う方が早そう
概要
自作プラグインのコードに以下のようなTODOが残っていました。
" TODO リストじゃなくて普通に文字列連結にする(テスト書く) for line in s:do_external_parse(a:lines) let escaped = 適当な処理 call add(converted_lines, escaped) endfor return join(converted_lines, "\\n")
ここでは文字列の配列をぐるぐる回して処理を行い、最終的に改行区切りの文字列としています。
join
で書いたものの文字列連結の方が早いのではと思って上記のようなTODOコメントを残していました。
今日このTODOを消化しようと思い、本当に文字列連結の方が早いのか計測しました。
先に結論
- 10回程度の連結なら大差ない
- 3000回ともなると圧倒的にjoinが早い
- joinの方が実装もシンプル
環境
- Mac OS X 10.9.5
- MacVim Custom Version 7.4 (KaoriYa 20150707)
ベンチマーク
h1mesuke/vim-benchmarkを使用します。
テストデータは1行85文字が3000行です。(3000回連結される)
let s:lines = [] for s:n in range(3000) call add(s:lines, " * 注意:拡張子が`.md`の場合は`markdown`ではなく`modula2`として認識されてしまいます。その場合は以下の設定を.vimrcに記述してください") endfor let s:bm = benchmark#new("concat string") function! s:bm.operator_for() let result = "" let delim = "" for line in s:lines let result = result . delim . line let delim = "\\n" endfor endfunction function! s:bm.operator_while() let result = "" let delim = "" let n = 0 let length = len(s:lines) while n < length let result = result . delim . s:lines[n] let delim = "\\n" let n += 1 endwhile endfunction function! s:bm.join() let tmp = [] for line in s:lines call add(tmp, line) endfor let result = join(tmp, "\\n") endfunction call s:bm.run(3)
結果は以下の通り。
Benchmark: concat string Trial #1 join : 0.008425 operator_for : 0.348224 operator_while : 0.356583 Trial #2 join : 0.010402 operator_for : 0.353079 operator_while : 0.359111 Trial #3 join : 0.008463 operator_while : 0.347276 operator_for : 0.359592
joinが圧倒的に早かった。
Javaで+
連結すると遅いように、Vim Scriptでも気をつけた方がいいのかな。
ちなみに10回程度の連結なら大差ない。
Benchmark: concat string Trial #1 join : 0.000046 operator_for : 0.000063 operator_while : 0.000076 Trial #2 join : 0.000051 operator_for : 0.000080 operator_while : 0.000083 Trial #3 join : 0.000048 operator_for : 0.000066 operator_while : 0.000082
おわりに
「推測するな、計測せよ」の大事さ。