Quantcast
Channel: Vimタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 5608

Vim scriptを処理系レベルから高速化しようとしている話

$
0
0

TL; DR

Vim scriptをパースしてASTを作り、高速化を図ります。リポジトリはこちら→wholekeik/vim

AST化

Vim script は実行のたびにコマンドをパースしているので非常に遅い言語です。コマンドをパースしておいてASTとし、それを実行すれば高速化が見込めます。しかし、Vim scriptでは引数の解釈が各コマンドによって全く異なるため、共通のパーサーを書くのは不可能です。したがって事前にパースするのではなく実際に実行しながら並行してASTを作っていきます。なおVim scriptの実行はユーザーの入力(コマンドモード)やオートコマンド、関数などがありますが、ASTとなるのは関数内のみです。また、AST化は行単位で行われます。

大まかな流れ

  1. 関数を定義する
  2. 通常通り呼ばれる
  3. 呼ばれたExコマンドのうち、ASTにできるものがあれば実行しつつASTを作る
  4. 2回目に同じ関数が呼ばれたとき、ASTが作られていればそちらを実行する。なお、一度AST化に失敗した行は二度とAST化されない

AST化できるコマンド・式

  • if
  • elseif
  • while
  • throw
  • echo
  • echon
  • return
  • 及びこれらの引数に現れる式(の一部)

AST化されているか確認するには、:function 関数の名前を入力してください。

" AST化されていないfunction! F()1return1endfunction" 1行目がAST化されているfunction! F()1return1endfunction

AST化された行が他の行より深くインデントされます。元々はoptimized: return 1のように表示していたのですが、Vimのテスト(make test)でこの:functionコマンドの出力をパースするため、見づらい意味を変えない表記にしました。

安定性

Vimのソースコードについているテスト(make test)はすべて通ります。しかし、同じ関数を二度以上実行してみないとAST化がうまくいっているかはわかりませんので、あまり信用はできません。

ベンチマーク

TODO // あとで調べて書く

破壊的変更

一点のみ、互換性を破壊する箇所があります。具体的にいうと次のスクリプトが動きません(ソース元はこちら→本当にキモい Vim script - . 演算子編)。

function! s:func(time)let time =a:time
  let suffix ='min'return1000-time.suffix
endfunction

echo s:func(50)" => 950min

echo s:func({'suffix': 'sec'})" => 1000 (これまでの動作)" => エラー (新しい動作)

このように、「関数内でドット演算子を使っているとき、ドット演算子の意味が途中で変化する」とエラーになるようになります。

課題

以下はAST化できません。

  • call
  • execute
  • for
  • let
  • ラムダ式
  • 複雑な関数呼び出し
  • |を含む行
  • echomsg
  • echoerr
  • unlet
  • lockvar
  • unlockvar
  • ブレース展開(var{'iab'}le
  • 関数内関数
  • ある条件を満たすドット演算子

AST化できない式を含むコマンドも連鎖的にAST化できなくなります。

関数についてですが、len('abc')といったビルトイン関数、s:func(123)といったユーザー定義関数はASTにできます。しかし、dict.func(0)や、function('f', [42])(1)というような複雑な関数はASTにできません。詳細はソースコードを参照してください。

もう一つ、ドット演算子の条件について説明します。Vimのドット演算子は左辺の型が決定しないと優先順位すら定まりません。ここで、x ? a.b : c.dのような式を考えてみます。xが真の時はaが評価されるので一つ目のドット演算子が文字列結合か辞書アクセスかが決定します。しかしその時、cは評価されませんので、二つ目のドット演算子はどちらの意味なのかがわかりません。そのためc.dをASTにできず、連鎖的にx ? a.b : c.d全体がASTにできなくなります。xが偽の時も同様のことが起こるため、結局この式は絶対にASTにはできません。「右側に空白が一つ以上あればそのドット演算子を文字列結合とみなす」というルールがあればこの問題を(部分的に)解決できるのではないかと思っています(左側だと行継続の関係で辞書のアクセスでもスペースがある可能性があります)。ただ互換性が壊れる危険があるのでこれについては既存のスクリプトを調査する必要があります。

記事投稿時点での目下の課題は:let:callのAST化です。この二つを実装すればかなりの行がAST化できるようになるのではないでしょうか。

試したい方へ

$ git clone https://github.com/wholekeik/vim
$cd vim
$ git checkout feature/vimscript-optimization
$ ./configure && make && make test# 注: 私は引数なしのconfigure,makeしか試していません

大規模な変更でありどのようなバグがあるかわかりませんので、-u NONE -i NONEのフラグ付きで起動するか、.vimrc等のバックアップをとった状態で起動してください。ファイル消失など、何が起こっても責任は負えません

  • ブランチはfeature/vimscript-optimizationに切り替えてください。

  • FEAT_OPTIMIZATIONを有効にする必要があります。今はsrc/feature.h#define FEAT_OPTIMIZATIONが直書きされているので何もする必要はありません(あとでこれはちゃんとfeatureを定義します)。逆に、その行をコメントアウトすればFEAT_OPTIMIZATIONが有効になっていない状態でのテストが可能になります。

  • 開発中の利便性のためsrc/Makefileが直接変更されており、デバッグ用のフラグがいくつかオンになっています。ベンチマークを取っていただける場合は、それらを切ってコンパイラの最適化オプションを有効にした状態でお願いします。

この量の変更を一人でテストするのは難しいので、バグ報告だけでも非常に助かります。


Viewing all articles
Browse latest Browse all 5608

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>