Closed xenos1984 closed 4 years ago
Thanks. Can you paste all of your g:ycm_ settings?
Also can you attach gdb to vim and get a back trace? Or use pstack on the pid?
Sure. First, my settings:
let g:ycm_warning_symbol = '▶'
let g:ycm_error_symbol = '▶'
let g:ycm_collect_identifiers_from_comments_and_strings = 1
let g:ycm_collect_identifiers_from_tags_files = 1
let g:ycm_seed_identifiers_with_syntax = 1
let g:ycm_complete_in_comments = 1
let g:ycm_complete_in_strings = 1
let g:ycm_semantic_triggers = {'tex' : ['\ref{','\vref{','\eqref{','\pageref{','\cite{'], }
let g:ycm_server_python_interpreter = '/usr/bin/python3'
let g:ycm_disable_for_files_larger_than_kb = 0
"let g:ycm_log_level = 'debug'
Backtrace:
(gdb) bt
#0 0x00007fc4fd672bf9 in __GI___poll (fds=0x7ffea75f91a0, nfds=1, timeout=7) at ../sysdeps/unix/sysv/linux/poll.c:29
#1 0x00007fc500a57e7a in _XtWaitForSomething () from /usr/lib/x86_64-linux-gnu/libXt.so.6
#2 0x00007fc500a59632 in XtAppProcessEvent () from /usr/lib/x86_64-linux-gnu/libXt.so.6
#3 0x0000564301b487b5 in gui_mch_wait_for_chars ()
#4 0x0000564301b15786 in ui_wait_for_chars_or_timer ()
#5 0x0000564301b17e52 in inchar_loop ()
#6 0x0000564301b3c695 in ?? ()
#7 0x0000564301b17905 in ui_inchar ()
#8 0x0000564301a02412 in ?? ()
#9 0x0000564301a02da6 in ?? ()
#10 0x0000564301a044ff in vgetc ()
#11 0x0000564301a04a19 in safe_vgetc ()
#12 0x0000564301a572d4 in normal_cmd ()
#13 0x0000564301b8d2f7 in main_loop ()
#14 0x0000564301b8e67f in vim_main2 ()
#15 0x0000564301969a27 in main ()
Another comment and clarification of the test results above: These are actually taken from gvim with the Athena GUI.
I forgot to mention, since I had the problem with plain command line vim a few days ago as well. I'm currently trying to reproduce the issue (I'm on a different computer at the moment), but now command line vim does not cause issues, CPU usage stays low.
Backtrace from command line vim (in urxvt terminal) after CPU usage rising:
(gdb) bt
#0 0x00007fa4dc2d303f in __GI___select (nfds=5, readfds=0x5641c26f18e0, writefds=0x5641c26f1860, exceptfds=0x5641c26f17e0, timeout=0x7fff1d7caef0)
at ../sysdeps/unix/sysv/linux/select.c:41
#1 0x00005641c23220a0 in ()
#2 0x00005641c232254b in ()
#3 0x00005641c23be656 in ui_wait_for_chars_or_timer ()
#4 0x00005641c231b545 in ()
#5 0x00005641c23c0d62 in inchar_loop ()
#6 0x00005641c23c07da in ui_inchar ()
#7 0x00005641c22aac52 in ()
#8 0x00005641c22ab5e6 in ()
#9 0x00005641c22ace24 in vgetc ()
#10 0x00005641c22ad289 in safe_vgetc ()
#11 0x00005641c22fff8e in normal_cmd ()
#12 0x00005641c24377cf in main_loop ()
#13 0x00005641c2438b0f in vim_main2 ()
#14 0x00005641c221179f in main ()
Which process is hogging the CPU? Is it vim or python?
It's vim, so not Python.
Does it only happen for html files?
As far as I can say, it happens for all file types for which syntax completion is enabled - at least for all which I have tested. So far I have observed it for tex, remind, php and html - the latter was just convenient to hack up a quick minimal working example.
Does it happen for any filetypes with native YCM support, like python?
In any case, would you be able to capture a profile, using these instructions :
:profile start profile.log
:profile func *
:profile file *
" At this point do slow actions
:profile pause
:noautocmd qall!
profile.log
will contain the relevant info.
It seems that Python is not affected. I typed for quite a while, including both identifier and semantic completion, but CPU usage stayed low.
In the meantime I also tried C++. There CPU usage rises again.
Here's the requested profile:
https://gist.github.com/xenos1984/c79836e6677bac9fdb7ab9ef4b72d7b5
Could it be related to signature help? That seems to be called quite often, and as far as I could see from the commit history, there were some recent changes.
So, python does support signature help, but PHP and html don't (by default). What's the output of :YcmDebugInfo
in a c++ file?
Here I picked a file for which semantic completion is enabled by passing compile flags via .ycm_extra_conf.py:
Printing YouCompleteMe debug information...
-- Client logfile: /tmp/ycm_0ajfctk2.log
-- Server Python interpreter: /usr/bin/python3
-- Server Python version: 3.6.9
-- Server has Clang support compiled in: True
-- Clang version: clang version 7.0.0-3~ubuntu0.18.04.1 (tags/RELEASE_700/final)
-- Extra configuration file found and loaded
-- Extra configuration path: /data/epi/C/osdev/nos/.ycm_extra_conf.py
-- C-family completer debug information:
-- Compilation database path: None
-- Flags: ['-target', 'i686-pc-elf', '-nostdinc', '-nostdlib', '-fdiagnostics-color=always', '-mgeneral-regs-only', '-O3', '-ffreestanding', '-fno-leading-underscore', '-Wall', '-Wextra', '-Wpedantic', '-fno-rtti', '-fno-exceptions', '-std=c++17', '-isystem', '/opt/cross/i686-elf/lib/gcc/i686-elf/9.2.0/../../../../i
686-elf/include/c++/9.2.0', '-isystem', '/opt/cross/i686-elf/lib/gcc/i686-elf/9.2.0/../../../../i686-elf/include/c++/9.2.0/i686-elf', '-isystem', '/opt/cross/i686-elf/lib/gcc/i686-elf/9.2.0/../../../../i686-elf/include/c++/9.2.0/backward', '-isystem', '/opt/cross/i686-elf/lib/gcc/i686-elf/9.2.0/include', '-isystem', '
/opt/cross/i686-elf/lib/gcc/i686-elf/9.2.0/include-fixed', '-isystem', '/opt/cross/i686-elf/lib/gcc/i686-elf/9.2.0/../../../../i686-elf/include', '-DHAVE_CONFIG_H', '-imacros', '/data/epi/C/osdev/nos/./build/system/config.h', '-I', '/data/epi/C/osdev/nos/./source/system/include', '-I', '/data/epi/C/osdev/nos/./source/
system/kernel', '-resource-dir=/home/mhohmann/.vim/bundle/YouCompleteMe/third_party/ycmd/third_party/clang/lib/clang/9.0.0', '-fspell-checking']
-- Translation unit: /data/epi/C/osdev/nos/source/system/kernel/Process.cpp
-- Server running at: http://127.0.0.1:36615
-- Server process ID: 29793
-- Server logfiles:
-- /tmp/ycmd_36615_stdout_3btfa3du.log
-- /tmp/ycmd_36615_stderr_i81ek7bm.log
That's what I expected. It's the libclang based completer which doesn't support signature help, just like PHP, but unlike Python.
-- Clang version: clang version 7.0.0-3~ubuntu0.18.04.1 (tags/RELEASE_700/final)
For the record, you should be on version 9. Also make sure you are on the latest YCM commit and have updated submodules (git submodule update --init --recursive
).
Open a python file and execute :py3 print(ycm_state._signature_help_available_requests['python'].Done())
. Then reproduce the issue in another filetype and do the same, but change the 'python'
part.
Expected output for a filetype that supports sig help is True
and for a filetype that doesn't is False
.
You can also try setting g:ycm_disable_signature_help
to 1
in your vimrc, to confirm if signature help is the problem.
Oh, indeed, it seems that I haven't properly updated in a while. So now I did and indeed I have clang version 9. But results are still not quite as expected:
g:ycm_disable_signature_help
did not make any difference. CPU usage still goes up.The python command you asked for returns True on python, tex, php... I would have expected that it is false on at least one of them.
That's because it should have been .Response()
, not .Done()
at the end. The expected values are YES
and NO
. .Done()
only checks if the /signature_help_available
request completed.
Oh, did you remember to restart vim after setting ycm_disable_signature_help
?
Yes, I did restart. Still no luck.
Now indeed I get NO
for tex, php, html... And for python I get YES
if signature help is enabled and None
if it is disabled.
Thanks for the investigation. It does look like a performance issue with the new signature help feature particularly where not available for a filetype. Will have to look into it.
Thanks for looking into this issue!
This might be a stupid question, since I'm not familiar with the YCM code, but where is the value of ycm_disable_signature_help
actually used / taken into account? I ran grep -rn ycm_disable_signature_help *
on the YCM directory and it only showed me the files where it is initially set and where it is documented, but no other occurrence in the code. The same for a search on GitHub. So I wonder how setting this will have an effect.
Also I looked up the code for those functions which are called a few thousand times during my test, according to the profile. This made me wonder:
https://github.com/ycm-core/YouCompleteMe/blob/master/autoload/youcompleteme.vim#L933
function! s:ShouldUseSignatureHelp()
return s:Pyeval( 'vimsupport.VimSupportsPopupWindows()' )
endfunction
function! s:RequestSignatureHelp()
if !s:ShouldUseSignatureHelp()
return
endif
exec s:python_command "ycm_state.SendSignatureHelpRequest()"
call s:PollSignatureHelp()
endfunction
function! s:PollSignatureHelp( ... )
if !s:ShouldUseSignatureHelp()
return
endif
if !s:Pyeval( 'ycm_state.SignatureHelpRequestReady()' )
let s:pollers.signature_help.id = timer_start(
\ s:pollers.signature_help.wait_milliseconds,
\ function( 's:PollSignatureHelp' ) )
return
endif
let s:signature_help = s:Pyeval( 'ycm_state.GetSignatureHelpResponse()' )
call s:UpdateSignatureHelp()
endfunction
So do I understand correctly that the first one only checks whether vim supports popup windows, and then the last one starts polling? The same here:
https://github.com/ycm-core/YouCompleteMe/blob/master/python/ycm/signature_help.py#L87
def ShouldUseSignatureHelp():
return ( vimsupport.VimHasFunctions( 'screenpos', 'pum_getpos' ) and
vimsupport.VimSupportsPopupWindows() )
Only here I found more checks:
https://github.com/ycm-core/YouCompleteMe/blob/master/python/ycm/youcompleteme.py#L324
But again, this might be an irrelevant observation, since I'm not familiar with the code.
where is the value of ycm_disable_signature_help actually used / taken into account?
The ycm_
prefix is just that, a prefix. What you should have looked for is disable_signature_help
, which is used on the server side:
ycmd/completers/completer.py:199:29: if not user_options[ 'disable_signature_help' ] else None )
ycmd/completers/completer.py:242:28: if self.user_options[ 'disable_signature_help' ]:
ycmd/completers/language_server/language_server_completer.py:789:52: self._signature_help_disabled = user_options[ 'disable_signature_help' ]
So do I understand correctly that the first one only checks whether vim supports popup windows, and then the last one starts polling?
Yes, s:ShouldUseSignatureHelp()
only checks if vim supports a list of necessary functions. The SendSignatureHelpRequest()
is supposed to return early if there's no signature help supported. Try reverting https://github.com/ycm-core/YouCompleteMe/pull/3541 locally and see if that helps. Also, for some filetype that has a problem, check the output of echo &ft
.
Ah, I see, thanks for the clarification.
I locally reverted #3541, but again it had no effect, CPU usage stays high. Also I ran echo &ft
on two of my test cases (both have the issue), and I got html
and tex
(as I expected).
As a test and temporary workaround, I disabled s:ShouldUseSignatureHelp()
by changing it such that it always returns 0, and indeed, CPU usage keeps low while typing in insert mode with completion enabled, and drops back to near 0 when not typing / leaving insert mode. That apparently kills signature help already at the earliest stage.
Thanks for confirming. Unfortunately, I can't repro, but I've compiled a list of functions that are called to make signature help work. Feel free to poke around and/or ask questions.
In autoload/youcompleteme.vim
:
s:signature_help
- current values:pollers.signature_help
- timers:UpdateSignatureHelp()
- calls ycm_state.UpdateSignatureHelp( s:signature_help )
- called on CompleteDone/CompleteChanged/TextChangedIs:RequestSignatureHelp()
- calls ycm_state.SendSignatureRequest();
s:PollSignatureHelp()
- called on TextChangedI, after s:UpdateSignatureHelp()
s:ClearSignatureHelp()
- stops timer, resets s:signature_help; calls ycm_state.ClearSignatureHelp()
- called on InsertLeave, s:StopCompletion
, restart and reparses:PollSignatureHelp()
- stores ycm_state.GetSignatureHelpRequest()
in s:signature_help; calls s:UpdateSignatureHelp()
- called from s:RequestSignatureHelp()
s:ShouldUseSignatureHelp()
- only checks vim support - called all the timeIn python/ycm/youcompleteme.py
ycm_state.UpdateSignatureHelp()
- A lot of things when there is something in s:signature_help
, when s:signature_help
is empty it does:ShouldUseSignatureHelp()
from python/ycm/signature_help.py
and exits early.s:signature_help.get( 'signatures' ) or []
is evaluates to False
and if so returns SignatureHelp( None, INACTIVE )
.ycm_state.SendSignatureHelpRequest()
- A bunch of preconditions before making a /signature_help
request, including the management of /signature_help_available
request.ycm_state.GetSignatureHelpRequest()
- Returns the last stored signature help response.ycm_state.ClearSignatureHelp()
- calls ycm_state.UpdateSignatureHelp({})
and calls Reset()
on the last stored response.I think the exists calls are expensive. And we should cache the result.
Here's a gist with the patch that caches that: https://gist.github.com/bstaletic/2ff17d054041b0da8816501112835e7f
@xenos1984 Can you test the patch?
@bstaletic I tried the patch, but it does not make a notable difference - vim is still at around 30% CPU usage. It seems that caching on the python side is not enough. Maybe one should cache inside the vim script part already?
Also thanks for the suggestions, I'll play around with these functions and see whether I can figure something out. Also I'm wondering what might be different in my setup which makes me run into this issue, and which you cannot reproduce. I'll try a fresh install of YCM (even though I did this already, but apparently with a stale libclang - now I tried also clangd) and check whether that fixes the issue.
Would you mind capturing a profile as well, just for comparison? I wonder whether these functions are called just as often and take the same amount of time as in my case. What I did in the profile was enter insert mode in an empty html file and typing something like <html><head><title>
- that was enough to trigger it.
Maybe one should cache inside the vim script part already?
Maybe. You can add something in autoload/youcompleteme.vim
along the lines of what my patch does.
Would you mind capturing a profile as well, just for comparison?
https://gist.github.com/bstaletic/d18eb1dba8ee28def401874d5184cc3a
I opened YouCompleteMe/plugin/youcompleteme.vim
wrote an empty function and deleted it.
I'm not an expert in vim scripting, but I can give it a try.
Thanks also for the profile log. That looks indeed very similar to mine. Some questions come to my mind looking at these numbers:
s:ShouldUseSignatureHelp()
and s:PollSignatureHelp( ... )
being called at the same rate, while vim was just waiting in normal mode.As another test / workaround, I increased the poll timer interval to 100ms. CPU usage still increases notably, but significantly less (a few % instead of 30-50%; without signature help, it's less than 1%).
Is it really necessary / useful to poll the signature help functions that often, i.e., every 10ms, or could / should one increase the interval?
It should be fast enough to not notice the delay while typing. Completion also uses such a fast timer. Though that doesn't mean 10ms is right, just that it's 10ms because completion is 10ms and the use case is similar.
Does it make sense to poll these functions at all in a buffer / for a file type which does not support signature help? Or should one rather disable the timer completely in that case?
It was just easier to implement that logic in python.
Shouldn't signature help / the poll timer be switched off when leaving insert mode?
It is, or should be, stopped whenever s:ClearSignatureHelp()
is called. See here. You can put the following function in the autoload/youcompleteme.vim
:
function! youcompleteme#PollTest()
return s:pollers.signature_help.id
endfunction
Then do :echo youcompleteme#PollTest()
and see what's the ID returned. If the timer is stopped, it should be -1.
As another test / workaround, I increased the poll timer interval to 100ms. CPU usage still increases notably, but significantly less (a few % instead of 30-50%; without signature help, it's less than 1%).
For me the CPU usage is around 3% to 5% in insert mode. I'm on a i7-2620M. Somehow I doubt that CPU is 10x faster than whatever you have.
It should be fast enough to not notice the delay while typing. Completion also uses such a fast timer.
That does make sense. Still I wonder why the signature help functions are called more than 100 times more often than the completion ones (according to the profile, s:RequestCompletion()
was called 25 times, while the two functions from above were called nearly 4000 times).
It was just easier to implement that logic in python.
I see, that also sounds reasonable to me
It is, or should be, stopped whenever
s:ClearSignatureHelp()
is called.
Indeed, that's what I was thinking, but...
Then do
:echo youcompleteme#PollTest()
and see what's the ID returned. If the timer is stopped, it should be -1.
...that gives me some positive number (23082) instead, so it seems that the timer keeps running after leaving insert mode.
For me the CPU usage is around 3% to 5% in insert mode. I'm on a i7-2620M. Somehow I doubt that CPU is 10x faster than whatever you have.
That's what's bugging me about this issue. I don't see a reason how my setup differs to make my CPU usage that much higher. Certainly not the CPU (i7-8550U here). Maybe a different vim version, so that mine could actually be buggy and the bug is triggered by the poll timer?
Then do :echo youcompleteme#PollTest() and see what's the ID returned. If the timer is stopped, it should be -1.
...that gives me some positive number (23082) instead, so it seems that the timer keeps running after leaving insert mode.
My bad. I also get a positive number. timer_stop()
indeed doesn't reset the ID. However, there's :call timer_stopall()
that you can try and see if it brings down the CPU usage.
As for thte vim version, I'm on 8.1.2268.
However, there's
:call timer_stopall()
that you can try and see if it brings down the CPU usage.
Indeed, that brings down CPU usage immediately. Typing in insert mode raises it again as before, and :call timer_stopall()
brings it down again. So I guess there remains a stale poll timer...
Try sprinkling echom 'whatever'
throughout s:OnInsertLeave()
and s:ClearSignatureHelp()
and see if the call to the relevant timer_stop
is even executed. Also helpful would be :echo timer_info()
.
EDIT: Use :messages
to see what the echom
printed.
Now that is interesting. First, my changes:
function! s:OnInsertLeave()
echom "OnInsertLeave begin"
if !s:AllowedToCompleteInCurrentBuffer()
return
endif
call timer_stop( s:pollers.completion.id )
let s:force_semantic = 0
let s:completion = s:default_completion
call s:OnFileReadyToParse()
exec s:python_command "ycm_state.OnInsertLeave()"
if g:ycm_autoclose_preview_window_after_completion ||
\ g:ycm_autoclose_preview_window_after_insertion
call s:ClosePreviewWindowIfNeeded()
endif
echom "OnInsertLeave near end"
call s:ClearSignatureHelp()
echom "OnInsertLeave end"
endfunction
function! s:ClearSignatureHelp()
echom "ClearSignatureHelp begin"
if !s:ShouldUseSignatureHelp()
return
endif
echom "ClearSignatureHelp call timer stop"
call timer_stop( s:pollers.signature_help.id )
echom "ClearSignatureHelp timer stop done"
let s:signature_help = s:default_signature_help
call s:Pyeval( 'ycm_state.ClearSignatureHelp()' )
echom "ClearSignatureHelp end"
endfunction
And this is the output:
ClearSignatureHelp begin
ClearSignatureHelp call timer stop
ClearSignatureHelp timer stop done
ClearSignatureHelp end
OnInsertLeave begin
ClearSignatureHelp begin
ClearSignatureHelp call timer stop
ClearSignatureHelp timer stop done
ClearSignatureHelp end
OnInsertLeave near end
ClearSignatureHelp begin
ClearSignatureHelp call timer stop
ClearSignatureHelp timer stop done
ClearSignatureHelp end
OnInsertLeave end
So apparently the timer_stop()
does get executed. But timer_info()
tells me:
[{'id': 19233, 'repeat': 1, 'remaining': 9, 'time': 10, 'paused': 0, 'callback': function('<SNR>73_PollSignatureHelp')}]
If I call the same function repeatedly, the timer id changes every time. Looks like the timer keeps on restarting.
Huh, now that's even worse. If I just keep working and typing for a while, vim CPU usage rises to 100%. timer_info()
shows some 20-30 timers, all of them calling <SNR>73_PollSignatureHelp
.
function! s:PollSignatureHelp( ... )
if !s:ShouldUseSignatureHelp()
return
endif
if !py3eval( 'ycm_state.SignatureHelpRequestReady()' )
let s:pollers.signature_help.id = timer_start(
\ s:pollers.signature_help.wait_milliseconds,
\ function( 's:PollSignatureHelp' ) )
return
endif
let s:signature_help = py3eval( 'ycm_state.GetSignatureHelpResponse()' )
call s:UpdateSignatureHelp()
endfunction
The idea is that the timer should self-restart until ycm_state.SignatureHelpRequestReady()
becomes true. Each timer_start()
call does change the ID. However, s:ClearSignatureHelp()
should stop that loop by calling timer_stop
.
Indeed, so then I understood correctly. I'm puzzled about the 'repeat': 1
in the timer info. Looking at the YCM and vim sources, I would have expected that this should be 0 instead...
Ah, never mind about the repeat. The timer_info
adds 1 to the actual value, so that's correct.
Anyway, I analysed the profile I captured and I have kind of a suspicion what might be going on, comparing completion and signature help. So that is what happens on completion:
s:RequestCompletion()
is called and sends a completion request.s:PollCompletion()
is called to poll for the response.s:RequestCompletion()
and 50 calls to s:PollCompletion()
.Now for signature help this happens:
s:RequestSignatureHelp()
is called and sends a signature help request.s:PollSignatureHelp()
is called to poll for the response.s:Pyeval( 'ycm_state.SignatureHelpRequestReady()' )
never succeeds, and so the timer keeps looping indefinitely.s:RequestSignatureHelp()
may be called again (25 times in my profile log) and pile up another timer instance.s:pollers.signature_help.id
and overwrites the id of other, still running timers.s:ClearSignatureHelp()
, only the last started timer, whose id is in s:pollers.signature_help.id
, is killed.That's just a suspicion, as much as I understand the code by now. Maybe there is some function I have not yet found that prevents timers from piling up.
In the meantime,
s:RequestSignatureHelp()
may be called again (25 times in my profile log) and pile up another timer instance.
Remember, s:RequestSignatureHelp()
happens only on TextChangedI
event, i.e. when you type in insert mode without the completion menu open. There's another event, called InsertCharPre
that is triggered before TextChangedI
. The relelvant functions are:
As you can see, those functions treat the completion and signature help timer identically.
EDIT: But you are right that if there's no signature help support, at least in insert mode, the timer will keep looping, as these lines never get executed, so self._latest_signature_help_response
remains None
and these lines never return True
.
yeah I think we should have SendSignatureHelpRequest
return False if no request was generated, in which case, we should not call s:PollSignatureHelp
.
So we need at least these changes:
I'll look at these changes today.
Thanks again for helping look into this, @bstaletic and @xenos1984
Issue Prelude
Please complete these steps and check these boxes (by putting an
x
inside the brackets) before filing your issue:vim --version
.:YcmDebugInfo
.:YcmToggleLogs
command.install.py
(orcmake
/make
/ninja
) including its invocationThank you for adhering to this process! It ensures your issue is resolved quickly and that neither your nor our time is needlessly wasted.
Issue Details
After entering insert mode, starting typing and doing completion in a document which uses completion via YouCompleteMe, VIM CPU usage rises to around 30-50% and stays there. Even after leaving insert mode, changing to a different buffer etc. CPU usage does not drop, but stays at 30-50%, occasionally even higher, up to 100%.
I suspect it to be related to YCM, since 1. CPU usage does not rise if I disable YCM, 2. even with all other plugins except YCM disabled, CPU usage still rises.
Start VIM, open a document with completion enabled, enter insert mode, start typing.
Minimal test case:
Start VIM.
:e /tmp/test.html
i (Enter insert mode.)
Type: <html><head><title>
What did you expect to happen?
CPU usage stays low / returns to low when leaving insert mode.
See report; VIM CPU usage rises and does not return to normal.
Diagnostic data
https://gist.github.com/xenos1984/2353bbdb48fd6ef63b1ece72760567ac
OS version, distribution, etc.
Ubuntu 18.04 VIM installed from https://launchpad.net/~jonathonf/+archive/ubuntu/vim