Fork me on GitHub
#lsp
<
2022-02-04
>
emilaasa07:02:15

I'm noticing something a bit weird. I get really high CPU usage (all cores on fire) when I'm writing comments. When I write code nothing odd happens. This started maybe a day or a couple of days ago. EDIT: Let's reword that to I noticed it a day or a couple of days ago, I don't really know if I was writing a bunch of comments! 🙂 Any ideas? Setup:

λ uname -a
Linux wrk 5.16.5-arch1-1 #1 SMP PREEMPT Tue, 01 Feb 2022 21:42:50 +0000 x86_64 GNU/Linux

λ clojure-lsp --version
clojure-lsp 2022.02.01-20.02.32
clj-kondo 2022.01.15

λ vim --version
NVIM v0.6.1
coc.nvim version: 0.0.80-03c9add7cd

emilaasa07:02:23

If nothing immediately jumps to mind I am of course happy to do a minimal repro, file an issue and so on, but I don't really have time until the weekend.

ericdallo12:02:50

good gif, but I never saw this behavior

ericdallo12:02:09

a repro where I could try would help a lot

ericdallo12:02:09

Oh, could you try with 1 or 2 previous versions? We changed something related with zipper parser of comments c/c @U07M2C8TT

jacob.maine18:02:36

Yeah, weird. I’ll research a bit

jacob.maine18:02:15

@UKFSJSM38, 2022.02.01-20.02.32 doesn’t actually have the change to comment parsing, right?

ericdallo18:02:33

hum, maybe related with the z/next* change we did recently?

jacob.maine18:02:08

Yeah, perhaps. Let me build that release and see whether I can reproduce

👍 1
jacob.maine18:02:21

Err… actually, no. z/next* was part of the comment parsing

jacob.maine18:02:17

My changes as of that release are just the addition of move-coll-entry and the removal of resolve-code-action

ericdallo18:02:00

Hum, :thinking_face: @U07M2C8TT can you repro the issue while writing comments for you?

jacob.maine18:02:28

Not really. Although @U6T7M9DBR’s gif shows the completion code offering MANY more suggestions than I get. For example the letter w has at least 20 completions whereas for me, on 2022.02.01-20.02.32 , I only get one

jacob.maine18:02:16

The gif shows the completion code offering functions and other completion types, whereas I only get a “word” type. That is… the editor has seen the word “with” before in the code, so it’s offering that

ericdallo18:02:32

good catch, @U6T7M9DBR could you try disabling vim's completion to confirm that?

jacob.maine18:02:33

But no functions like “with-open” or anything like that

jacob.maine18:02:46

Maybe I have a different completion setting?

ericdallo18:02:00

maybe, I know emacs lsp-mode already tweaked a lot the completion performance as well

jacob.maine18:02:21

mmm… right, I am using emacs

jacob.maine18:02:26

Watching the gif closely, it’s offering completions for single characters. For example c completes to cond and many others. If it’s searching for every symbol that begins with c, and serializing all that data, I can imagine it’d be slow. And especially so when writing comments, where you reach the beginning of a word more often. Emacs doesn’t even seem to try to request completions for single letters

ericdallo18:02:15

actually, company has a setting for that, I think its default is 2 or 3 chars

jacob.maine18:02:29

Actually, there’s a chance I’m not using clojure-lsp’s completion at all. My editor might be configured to use CIDER’s completion. I’m gonna try to figure that out before I offer any more thoughts

👍 1
ericdallo18:02:31

company-minimum-prefix-length

jacob.maine18:02:58

Yeah, company-minimum-prefix-length is 2 for me, which is the length at which I start getting completions

jacob.maine19:02:39

I have so many questions…

jacob.maine19:02:56

1. There are two clojure-lsp processes running. Why is that?

ericdallo19:02:09

maybe 2 opened projects

jacob.maine19:02:30

but both are using a ton of cpu at the same time

1
jacob.maine19:02:38

2. The gif doesn’t show log lines about completion

jacob.maine19:02:39

3. It does show log lines about codeLens, which report a decent number of milliseconds. I don’t get those in my editor. Maybe I’ll make my editor use codeLens more aggressively

👍 1
ericdallo19:02:28

CodeLens are enabled by default on lsp-mode

jacob.maine19:02:17

4. rewrite-clj parses an entire comment, not a single word within a comment. How is it even getting single words?

👍 1
jacob.maine19:02:46

I get more CPU usage if I turn on codeLens, but not as much as the gifs

jacob.maine19:02:27

5. (related to 3) the gif about comments reports many milliseconds running codeLens and didChange. But the gif about regular code show all the same stuff, plus completion, resolveCodeLens, and resolveCompletion. And yet, the regular code uses less CPU. I’m really confused

ericdallo19:02:37

one thing that is tricky, is that didChange most of the time report 0ms because it runs async, but usually takes way more time than other features :(, we should metrify didChange better

👍 1
jacob.maine19:02:47

Is it up to the client to decide how often to call didChange?

ericdallo19:02:37

yes, we already do a debounce on server side, client can and should call didChange as many times it wants

jacob.maine20:02:56

OK... I don’t know how to debug further. I’ve turned on codeLens and completion. And I’ve set company mode to attempt completion after 1 character. Together, those increase CPU usage, but by similar amounts in comments and in code. If anything, it uses less CPU in comments because completion isn’t triggered.

jacob.maine20:02:00

The obvious difference between my setup and the gifs is that I’m running Emacs. So, I wouldn’t be surprised if the problem is somewhere within vim. I’m not familiar with that infrastructure, so can’t really help debug there.

jacob.maine20:02:03

I initially thought completion was the problem, but at least according to the log in the gifs, vim isn’t asking clojure-lsp to run completion in comments. Vim is, however, offering completions while in comments. (This happens in Emacs too. I think company-mode, the thing that manages completions uses several different completion backends simultaneously. So while in a comment it’s getting completions from somewhere besides clojure-lsp. I suspect vim is doing something similar.). Anyway, perhaps completion isn’t where we should focus.

jacob.maine20:02:06

The log isn’t definitive about what’s using so much CPU. That said, top shows two clojure-lsp processes, but the gif has only one log. It would be helpful @U6T7M9DBR to tail the other log. It may be tricky to figure out which file it is, but usually you can look in the log directory for the most recently changed files. Or you could try adding :log-path “/tmp/clojure-lsp.out” to .lsp/config.edn, so that both processes log to the same path. Edit: log path may be configured differently with coc.nvim

👍 1
jacob.maine20:02:12

One thing we forgot to ask @U6T7M9DBR... are you building clojure-lsp locally, or using the official release?

ericdallo00:02:30

Thanks for the help @U07M2C8TT, let's see what @U6T7M9DBR thinks about those questions

jacob.maine01:02:14

Yeah, sounds good. I’ve done a bit more research but don’t have much to report. I got nvim running with coc. I haven’t figured out how to configure it to show the code lenses. Any pointers there would be helpful. It does request completion from within comments. And clojure-lsp returns valid completions. It takes over a second, probably because it invokes completion for individual characters, which means a lot of data is returned. @UKFSJSM38 do you know how that’s even possible? AFAIK, clj-rewrite parses entire comment blocks, not individual words within comments. Is there something special about the completion code that lets it handle words within comments? Anyway, it uses a lot of CPU just from the completion. And I suspect if I could turn on code lenses, it would use more, since that’s invoked a lot in the gifs. But I still have no idea why it would use more CPU in comments for @U6T7M9DBR. That doesn’t really happen for me. All the completion stuff as well as did-change happens just as often in real code as in comments. Maybe it’s all from codelens, although I don’t know why that’d be different while in comments. Anyway, I’m still stumped there. @U6T7M9DBR, I don’t know how, but perhaps you can configure coc and/or nvim to invoke completion only after 2 characters are available? Or maybe increase the delay before completion is requested?

jacob.maine02:02:01

I know that a fix in clojure-lsp would be preferable, but that would at least let us confirm what’s taking so much CPU

ericdallo02:02:23

@U07M2C8TT the completion is kind of special, the client send the literal text that is being completed, so it "skips" the zipper on clojure-lsp side (but we also check the zipper to know what to complete and etc). So it's sending the comment chars indeed, maybe automatically completing a word of 1 length is a little too much? I'd love to hear options to improve performance if you think of anything, I already spent a lot of time improving that code, but maybe I'm missing something

jacob.maine02:02:13

> the client send the literal text I believe you, but I can’t see in https://github.com/clojure-lsp/clojure-lsp/blob/0efef5c1ea266ac4af0c44f4c802185191a81580/lib/src/clojure_lsp/feature/completion.clj#L352-L430 where it uses the literal text, which makes me think I’m looking at the wrong thing. I see where resolveCompletionItem , uses the literal text, but not completion > options to improve performance if you think of anything, I already spent a lot of time improving that code I’m not surprised that’s been heavily optimized already! It’s just a hard problem… If clients can ask for “anything in this project or its libraries or clojure.core that begins with the letter C”, that’s going to be a lot of data. Perhaps there are faster algorithms for finding those results, but just serializing them could be slow (I’ve seen that be the case when there’s a lot of data, especially for JSON which is pretty verbose). I suppose there could be some server-side restrictions, like the input text has to be 2 characters or more. Or if serialization is the problem perhaps if the result set is too large the server could truncate it or return nothing. Otherwise, I’d have to look at it a lot more closely to come up with any suggestions

👍 1
jacob.maine03:02:10

Here’s a thought… Perhaps in comments clojure-lsp is returning ALL the completions, as https://github.com/clojure-lsp/clojure-lsp/blob/master/lib/test/clojure_lsp/features/completion_test.clj#L97-L98 suggests it does. And then the editors re-filter that list to only things that match the literal text. So that giant list (almost 1000 items in a nearly empty project) gets calculated and serialized every time completion is invoked from within a comment. And in regular code, because a zloc can be found, clojure-lsp finds and returns a much smaller result set, making it use less CPU. Does that sound possible @UKFSJSM38?

ericdallo13:02:55

Yes, totally possible @U07M2C8TT, I always tried to avoid limiting the results or length as this is something clients should do probably as default (like don't request at all if length < 2), but maybe we should have a setting on completion regarding the minimum length or try to not return anything on comments

jacob.maine17:02:22

Yeah, inside comments, I see two options. The server could return nothing. Or it could use the word that the client provides (which I still don’t think clojure-lsp does) and filter on that. In any case, I don’t think clojure-lsp should ever return every symbol in clojure.core, java.lang and java.util, beginning with any character, which is what it seems to do now. I also think that whether in a comment or not, it’d be reasonable for the server to require a minimum request length. Providing completions for a single character provides too many options to be helpful, IMO.

ericdallo17:02:08

Agree, I think we can probably implement all of those, first don't return anything on comments, I don't see why one would want completion on comments, second, don't compute core symbols if length is 1 and we can think if those would not be enough we can think about having a setting to not complete X length

ericdallo17:02:00

Feel free to complement that issue and LMK if interested on working on that, otherwise I will find sometime until next release, thanks for the help!

jacob.maine18:02:55

I have a lot of context so I can take that Issue. I’m pretty busy this weekend, so won’t get to work on it until next week.

jacob.maine18:02:49

There was some work on that part of the code in the parse-comments branch, which will mean merge conflicts. What are your thoughts on getting that branch into master for this release? It’s a big change, so I’d prefer to have it merged earlier in the release cycle, so it has time to be tested.

jacob.maine18:02:31

Also, we should remember that we aren’t 100% sure this explains the original problem. @U6T7M9DBR, in addition to the above questions, it’d be helpful to know whether the problem gets better or goes away if you change the settings to A) not invoke completion until 2 characters have been entered, B) not invoke completion until after a slight delay (a few hundred milliseconds) and C) not invoke completion at all. (The last one is to know whether the culprit is completion, or something else like code-lens or did-change.)

ericdallo18:02:12

@U07M2C8TT I intend to review again and merge this weekend!

jacob.maine18:02:02

OK, great. Thanks! If it’s merged before I work on the Issue, that’ll avoid merge conflicts.

👍 1
emilaasa20:02:32

Great digging so far, I feel bad for leaving you with only a gif and some whining to work off of now! The chance that I have something messed up in my setup is a lot higher than 0%. I mean look at these great suggestions from my autocomplete:

emilaasa20:02:58

tian_hieroglyphs?

😂 1
emilaasa20:02:32

What the hell is going on

emilaasa20:02:08

I mean I wouldn't sweat this too much until I have a minimal repro (sorry if that ship has sailed), what the heck can be going on here...

emilaasa20:02:58

I use the official release (I think, installed via dharrigans AUR build).

emilaasa20:02:29

turning off autocomplete suggestions via let b:coc_suggest_disable = 1 in the vim config fixes the problem immediately

ericdallo20:02:08

yeah, I'm no vim user, but it seems your completion backend is using lsp and something else

ericdallo20:02:21

which is possible on emacs as well, we call it company-backends

emilaasa20:02:32

I wonder how it messes with the LSP tho

ericdallo20:02:08

yeah, I guess is not related with clojure-lsp topping cpu

ericdallo20:02:19

but it'd be good to test without those weird completions enabled just to make sure

emilaasa20:02:28

Haha what do you mean weird

😂 1
emilaasa20:02:50

I need those for stuff

emilaasa20:02:01

trakana trokes thipic

emilaasa20:02:12

And toneletters

ericdallo20:02:25

😂 I never saw something like that

emilaasa20:02:27

whattt is going ooon 🙂

ericdallo20:02:41

vim is for pros indeed 😂

emilaasa20:02:48

I wonder if they are in the dict or something 😄

emilaasa20:02:42

Yeah I'll figure out wtf is going on with those:

emilaasa20:02:57

I think that little tag in brackets points to the source, and CLJ here I think is Conjure's clojure client

1
ericdallo20:02:05

it seems to be something built-in on clojure "mode" for vim

emilaasa20:02:12

Yeah maybe, I'll dig around. This is a hilarious mess.

emilaasa20:02:42

I got rid of the cool completions with CocUninstall coc-conjure Now I only have LSP suggestions, the CPU hogging remains tho.

ericdallo20:02:22

good, one less thing to think about while debugging 😅

emilaasa20:02:18

I'll try setting the coc config to as default as possible.

ericdallo20:02:39

I think it's worth to add the details on that issue we created

emilaasa20:02:22

If your editor is suggesting hieroglyphs you got bigger problems than clojure lsp?

ericdallo20:02:55

😂 I think so

emilaasa20:02:15

🙂 I'll mess around a bit more and see if I can get some sort of hypothesis formed at least

emilaasa20:02:01

I did try to make a from zero reproduction, and everything worked fine when I only had clojure core and a few random libraries on the path.

emilaasa20:02:17

This is a large ish (for a clojure project) codebase

emilaasa21:02:36

it "feels" like the whole classpath is getting analyzed every keystroke

ericdallo21:02:24

Sounds unlikely from LSP side

emilaasa21:02:14

I'll add the few details I've found so far, but I'll try to do an open source repro too somehow.

emilaasa21:02:54

Great effort tho, thanks so much for looking into it.

👍 1
emilaasa21:02:21

2022-02-06T20:59:13.689Z DEBUG [clojure-lsp.server:?] - :completion 12836ms

emilaasa21:02:36

Something is happening atleast when I'm just writing in the comments, but god knows what

emilaasa21:02:37

Is it normal that I have two LSP processes running?

ericdallo21:02:48

if you have 2 protjects opened, yes

emilaasa21:02:49

Well I need to go to sleep 🙂 I updated the issue, I think I might have some cleanup left to do in my vimrc / coc config, or that coc nvim might be misbehaving a bit maybe?

emilaasa21:02:06

I'll try the other nvim lsp client tomorrow and see if the issue persists.

emilaasa21:02:02

This doesn't seem right then:

ericdallo21:02:56

Please, make sure you have your project root correctly configured

ericdallo21:02:25

it's client responsibility (coc/neovim side)

emilaasa21:02:06

"languageserver": {
        "clojure-lsp": {
            "command": "clojure-lsp",
            "filetypes": ["clojure"],
            "disableDiagnostics": false,
            "rootPatterns": ["deps.edn", "project.clj"],
            "additionalSchemes": ["jar", "zipfile"],
            "trace.server": "verbose"
        },
Anyway thanks guys - I really appreciate all your work with this project. It mostly works great and if noone else is reporting it's probably my bad config. But I'll try to repro with nvim-lsp too in a couple of days.

👍 1
ericdallo12:02:54

@U6T7M9DBR just FYI, we merged a pr from @U07M2C8TT which increase performance a lot on completion, maybe you should try a nightly build from #clojure-lsp-builds

emilaasa13:02:06

Thanks - I will!