Fork me on GitHub
#lsp
<
2022-04-29
>
Stefan T20:04:29

I’ve been trying to debug what appears to be a performance regression with LSP, though I’ve tried rolling back to previous versions of Calva, LSP and Kondo with no improvements. Code snippets and auto complete can be very slow when I have LSP running, it seems like code actions is blocking autocomplete suggestions, and kondo can sometimes be very slow. Here’s an example. I have a snippet pprint which will insert a (clojure.pprint/pprint ) statement, which currently takes ~2 seconds to show in some namespaces. This is what the LSP log shows after I type “ppr” in the editor. Note that clj-kondo is being invoked multiple times, presumably once per keystroke, and each invocation is quite slow.

2022-04-29T20:09:41.041Z  INFO [clojure-lsp.feature.file-management:195] - changes analyzed by clj-kondo took 556ms
2022-04-29T20:09:41.041Z  INFO [clojure-lsp.feature.file-management:?] - :analyze-file 556ms
2022-04-29T20:09:41.272Z  INFO [clojure-lsp.handlers:?] - :document-symbol 0ms
2022-04-29T20:09:41.610Z  INFO [clojure-lsp.feature.file-management:195] - changes analyzed by clj-kondo took 568ms
2022-04-29T20:09:41.611Z  INFO [clojure-lsp.feature.file-management:?] - :analyze-file 569ms
2022-04-29T20:09:42.026Z  INFO [clojure-lsp.handlers:148] - :completion 1326ms - total items: 228
2022-04-29T20:09:42.171Z  INFO [clojure-lsp.feature.file-management:195] - changes analyzed by clj-kondo took 560ms
2022-04-29T20:09:42.172Z  INFO [clojure-lsp.feature.file-management:?] - :analyze-file 561ms
2022-04-29T20:09:42.175Z  INFO [clojure-lsp.server:?] - :refreshing-test-tree 3ms
2022-04-29T20:09:42.189Z  INFO [clojure-lsp.feature.file-management:?] - :reference-files/find 17ms
2022-04-29T20:09:42.190Z  INFO [clojure-lsp.feature.file-management:?] - :notify-references 18ms
2022-04-29T20:09:42.275Z  INFO [clojure-lsp.handlers:427] - :semantic-tokens-full 5ms - waited 1965ms
2022-04-29T20:09:42.281Z  INFO [clojure-lsp.handlers:148] - :completion 234ms - total items: 2
2022-04-29T20:09:42.733Z  INFO [clojure-lsp.handlers:401] - :code-actions 413ms - waited 1149ms
2022-04-29T20:09:42.946Z  INFO [clojure-lsp.handlers:401] - :code-actions 410ms
2022-04-29T20:09:42.949Z  INFO [clojure-lsp.handlers:427] - :semantic-tokens-full 4ms

Stefan T20:04:42

This performance issue does seem to be somewhat namespace dependent. Namespaces with fewer dependencies don’t have quite as severe of a performance issue.

Stefan T20:04:36

TBH right now I’m not sure if the performance correlates more with the number of dependencies or the overall file size. The example above is a relatively small file of 600 lines.

ericdallo20:04:26

Could you share what versions did you test and if you tested with clojure-lsp master? Also, rolling back Calva doesn't affect the used clojure-lsp

ericdallo20:04:47

we had some improvements recently on completion performance both on latest release and master (not released yet)

Stefan T20:04:47

I’m currently testing off the 2022.04.18-00.59.32 release.

Stefan T20:04:12

I can give master a try as well. I’m using the make file do produce a build and configured Calva to use my local build.

ericdallo20:04:45

yeah, it'd be good to test on master, algo, if you have a public project where we could test it as well

Stefan T20:04:10

Unfortunately don’t have a public project. Another thing to note is my company uses a monorepo setup. We have one root “virtual” project that encompasses all of our dependencies across the repo, so the classpath is quite large.

Stefan T20:04:48

Though the reason I bring this up now is it does seem to be a regression as of the past couple days/weeks. I don’t know exactly when I started seeing it.

ericdallo20:04:30

maybe try older releases could help find the release that introduced that regression, I was not aware of any regression, actually we improved lots of performance things on completion recently, it's kind of surprising 😅 maybe we missed something though c/c @U07M2C8TT

Stefan T20:04:04

Building off master branch now 🤞

Stefan T20:04:27

Oh what do you know, much faster 😁

Stefan T20:04:19

It seems like the clj-kondo evaluation time hasn’t improved much, but it no longer blocks other code actions or autocompletes?

ericdallo20:04:37

nice, so probably we fixed on @U0BUV7XSA’s recent commit :)

👍 1
ericdallo20:04:47

no, we didn't change anything regarding block code actions or autocompletes

Stefan T20:04:02

That’s interesting. Previously it looked like code actions were blocked until LSP was completely finished, but now I see my snippet suggestions even while LSP is still computing. Either way it’s much better now.

Stefan T20:04:10

By the way on an unrelated note, I can’t say this enough but LSP is a GAME CHANGER!!

ericdallo20:04:53

Intereting, maybe some change on vscode api that calva's use, I don't know too much about it tho haha, glad you like it 🚀

jacob.maine20:04:40

@UQE9SS4DP, as the logs show, completion waits for kondo analysis. that hasn’t changed, nor have we done anything recently to improve kondo performance. but @U0BUV7XSA recently fixed a performance problem in completion related to aliases, which aligns with what you were seeing. my guess is that you’re seeing better performance because the initial completion request now completes faster. subsequent completion requests will still wait on kondo analysis. also keep in mind that editors tend to do their own filtering of completion items, so often the sequence goes like this: 1. type 1 character 2. editor asks clojure-lsp for completions; it returns a massive list 3. type a few more characters, editor filters the original list. It may look like clojure-lsp is giving you a narrower list, but this is actually all thanks to the editor 4. sometimes the editor asks clojure-lsp for more completions; it returns a smaller list All that is to say that the time for that first completion request is really critical, and subsequent completion requests that wait on kondo analysis are less important

ericdallo20:04:09

I usually disable Emacs lsp-mode's completion cache, so I can always know how much time clojure-lsp takes, a dev thing only tho

🤕 1
🥲 1
Stefan T20:04:39

@U07M2C8TT thanks for the detailed explanation, that makes sense! I haven’t done a lot of work with LSPs, so wasn’t sure which end is responsible for something like debouncing rapid input.

ericdallo20:04:32

Most of the time, LSP servers should handle multiple requests and debounce on their side

jacob.maine20:04:51

I’m still learning about them too—complicated beasts. There’s debouncing on both sides, just one part of many latency/throughput tradeoffs