-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
アイドル時に実行されるGC処理のパフォーマンスが悪く、フリーズすることがある #608
Comments
つまり後に作った dict から順番に開放されるようなケースを作ればいいんですかね? |
今の構造なら、1000個も作れば O(n^2) で100万回オーダーでループが走りそう。 |
👍 お疲れ様です。 unite.vim 特有の問題ではないはずですが、unite.vim を使っている時に起きやすいです。 |
@Shougo たとえば、ループ内でdict作って、そのメンバーとして前回のループで作ったdictを格納する、みたいなことしてますか? let d = {}
let i = 0
while i < 10000
let d = { prev: d }
let i += 1
endwhile
" use d |
unite.vim は前回の candidatesをキャッシュしているので、似たようなコードになっているのではないかと考えています。パフォーマンスを稼ぐために候補のキャッシュが分散しており、どこに何が格納されているかは私でさえ把握できていません。 |
まだ詳細を追っていないのですが、Vimのメモリ解放タイミングはいくつかあるようです。 それらのタイミングで開放できなかったメモリが |
いちおうユーザが呼び出すことも出来るのね |
はい。その通りです。VimのGCはPythonと同じで参照カウント + Mark Sweep になっています。 unite.vim の場合、リストを大量に生成しており、それらの中身も非常に複雑となっています。 |
今試したら一瞬で再現出来ました(;´Д`) let s:hoge = {}
let s:fuga = {}
let s:arr = []
function! Prepare() abort " {{{
let s:hoge.a1 = map(range(1, 100000), '{}')
let s:hoge.a2 = map(range(1, 100000), '{}')
let s:hoge.a3 = map(range(1, 100000), '{}')
let s:hoge.fuga = s:fuga
let s:fuga.hoge = s:hoge
endfunction " }}}
function! Release() abort " {{{
let s:arr = s:hoge.a2
unlet s:hoge
unlet s:fuga
endfunction " }}}
|
おお、素晴らしい。 @todesking こちらでも .vimrc なし、上記の手順で再現しました。これはいけそうです。 |
list も同じ問題ありです |
パッチ(改) diff --git a/src/eval.c b/src/eval.c
index ae8331d..8fd3d99 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -6900,7 +6900,9 @@ free_unref_items(copyID)
int copyID;
{
dict_T *dd;
+ dict_T *dd_next;
list_T *ll;
+ list_T *ll_next;
int did_free = FALSE;
/*
@@ -6912,11 +6914,10 @@ free_unref_items(copyID)
/* Free the Dictionary and ordinary items it contains, but don't
* recurse into Lists and Dictionaries, they will be in the list
* of dicts or list of lists. */
+ dd_next = dd->dv_used_next;
dict_free(dd, FALSE);
did_free = TRUE;
-
- /* restart, next dict may also have been freed */
- dd = first_dict;
+ dd = dd_next;
}
else
dd = dd->dv_used_next;
@@ -6933,11 +6934,10 @@ free_unref_items(copyID)
/* Free the List and ordinary items it contains, but don't recurse
* into Lists and Dictionaries, they will be in the list of dicts
* or list of lists. */
+ ll_next = ll->lv_used_next;
list_free(ll, FALSE);
did_free = TRUE;
-
- /* restart, next list may also have been freed */
- ll = first_list;
+ ll = ll_next;
}
else
ll = ll->lv_used_next; |
"next dict/list may also have been freed" というコメントがどうにも不安なんですが、そういうケースないように見える…… |
ありがとうございます |
お忙しそうな感じなので最新でパッチ作成 |
良さそうに思います。 @todesking パッチ提出お願い出来ますか? |
了解しました(vim_devにですよね?) |
「このscriptがxx秒掛かっていたけどこのpatchをあてたらyy秒に改善されたぜ!」みたいな数値的があればgood. |
@todesking お忙しい様なら送信代行(お名前は存じております)出来ますがどうしましょうか? |
let s:hoge = {}
let s:fuga = {}
let s:arr = []
function! Prepare() abort " {{{
let s:hoge.a1 = map(range(1, 100000), '{}')
let s:hoge.a2 = map(range(1, 100000), '{}')
let s:hoge.a3 = map(range(1, 100000), '{}')
let s:hoge.fuga = s:fuga
let s:fuga.hoge = s:hoge
endfunction " }}}
function! Release() abort " {{{
let s:arr = s:hoge.a2
unlet s:hoge
unlet s:fuga
endfunction " }}}
let start = reltime()
call Prepare()
call Release()
call garbagecollect()
echomsg string(reltimestr(reltime(start))) 上記スクリプトで測定したかったのですが、私の環境ではパッチなし状態ではスクリプト実行後にフリーズするので測定が不能です。パッチを当てると0.2598sで処理が終わります。 |
@Shougo 👍 |
👍 |
let s:hoge = {}
let s:fuga = {}
let s:arr = []
function! Prepare() abort " {{{
let s:hoge.a1 = map(range(1, 50000), '{}')
let s:hoge.a2 = map(range(1, 50000), '{}')
let s:hoge.a3 = map(range(1, 50000), '{}')
let s:hoge.fuga = s:fuga
let s:fuga.hoge = s:hoge
endfunction " }}}
function! Release() abort " {{{
let s:arr = s:hoge.a2
unlet s:hoge
unlet s:fuga
endfunction " }}}
let start = reltime()
call Prepare()
call Release()
call garbagecollect()
echomsg string(reltimestr(reltime(start))) 上記スクリプトで、 備考:なぜか GC はスクリプトの実行後に発動するのでパッチ無し状態はストップウォッチにて測定しています。 |
👍 |
申し訳ございません、お願いします |
👍 |
こう言われてますが、何か反論のある方はいますか? |
現在の1つ解放するたびに先頭からやりなおすのは明らかにやり過ぎ。 解決策なんですが、こんな風に折衷案にするのはどうでしょう。
|
「コメントには"next dict may also have been freed"と書いてあるのでこのパッチだと不十分ではないか。しかしちょっと上のコメントには矛盾するようなこと(再帰的な開放は行わない)が書いてあってどっちが正しいんだろう。」という意見ですよね。 これはたぶん上のコメントが正しくて、現在はdictやlistの中身を再帰的に開放はしないのでこのままの処理でいいんじゃないかと思ってます。ソースを見る限りはそうなっているように見える。 |
このパッチですが、現在どういうステータスなのでしょうか。 |
mattn さん対応有難うございます。 https://groups.google.com/d/msg/vim_dev/DBYOdHQWvqY/asNnl1fVBQgJ |
おおおおおおおおおおおおおおおおおおおおおおおおおおおおお |
🎉 |
👍 |
👏 |
皆さんありがとうございました!!! |
👍
|
感極まってきました |
㊗️ |
特定のメモリ確保パターンにおいて、メモリ解放処理(
eval.c:garbage_collect()
)のパフォーマンスが劣化して長時間フリーズする問題があるようです。小さな再現パターンをまだ発見できていませんが、
Unite.vim
で大量(数万件)のcandidateを表示させるのを何度か繰り返した後で10秒ほど放置することで再現します(放置時に裏でgarbage_collect
が実行される)。パッチは以下。
todesking/vim@eed1844
The text was updated successfully, but these errors were encountered: