This is an archive of the discontinued LLVM Phabricator instance.

[clangd] use a compatible preamble for the first AST built
AbandonedPublic

Authored by qchateau on Feb 24 2021, 2:32 PM.

Details

Reviewers
sammccall
Summary

Keep a store of the preambles of all opened filed, plus up to
5 previously used preambles. When building the AST for a TU,
if the preamble for this TU is not yet built, look through
the stored premables to find the best compatible preamble
and use this preamble to speed-up the AST build.


This patch is starting to look like something acceptable, so
I'm sending it for review. At this point I'd like to get some
feedback on this before going further.

Diff Detail

Event Timeline

qchateau created this revision.Feb 24 2021, 2:32 PM
qchateau requested review of this revision.Feb 24 2021, 2:32 PM

@kadircet who was doing (or planning!) some measurements around this idea. Maybe it's possible to use this patch for them?

My guess about risks:

  • currently, preamble patching covers relatively few extra headers in practice. Here we're likely to end up with larger divergences which might lead to really terrible AST build times (e.g. what if we're missing 30% of a 500MB preamble...)
  • maybe extra resource consumption from the +5 cached preambles (e.g. in preamble-in-ram case), but I guess this number can just be tuned

The hardcoded 5 premables can indeed be changed, I did not want to waste time on coding a configuration logic at such an early stage.

Well indeed do some extra work if we elect a compatible but almost useless preamble. We'll basically do the work twice (but at least we do it concurrently \o/). I can look into adding a heuristic layer to detect almost useless preambles and reject them. Though I'm not sure how I could do this as a single header can contain 99% of the preamble through include chains

Anyway that's the current state of this patch: it's already pretty cool (it does speed things up noticeably) but there are probably tons of flaws or potential improvements and I'd like to receive comments from another POV

Well indeed do some extra work if we elect a compatible but almost useless preamble. We'll basically do the work twice (but at least we do it concurrently \o/).

I am afraid this can happen more than twice, e.g. for signature help, the client will issue a request after each keystroke, clangd will see the request try to serve it by patching the AST, while building it we'll receive more requests, and each of those will be served with a huge patching penalty. (as we don't cancel preamble tasks, maybe we should but even then, we'll keep building responses for out-dated requests).

I can look into adding a heuristic layer to detect almost useless preambles and reject them. Though I'm not sure how I could do this as a single header can contain 99% of the preamble through include chains

That's actually an interesting trade-off we can make. getBestPreamble can choose the preamble that has most similar line coverage (this was something i was testing internally already) but in addition to that as you mentioned we can have a threshold and say that we won't consider a preamble as a match, if it requires patching more than X lines(or bytes) of code (note that this heuristic would require doing some IO to calculate number of new lines/bytes, until first preamble is built). Though we would need to ensure that we can still utilize the preamble cache even after that mechanism, and find the sweet spot for X.

Anyway that's the current state of this patch: it's already pretty cool (it does speed things up noticeably) but there are probably tons of flaws or potential improvements and I'd like to receive comments from another POV

yes it indeed looks great! could you give a bit more information on average preamble build latency in the files you are testing this with, and how all of this works when you open files from different parts of the codebase? (I'll also try to do some analysis for this one myself, especially for the heuristic I mentioned)

qchateau updated this revision to Diff 326816.Feb 26 2021, 3:05 PM
  • [clangd] make more compile commands compatible
  • [clangd] ignore incompatible preamble

    I improved command line compatibility detection (faster & matches more files) and I had to blacklist some preambles. Namely, when the file we're building the AST for appears in the include tree of a preamble, we can't use it, otherwise symbols are missing from the AST. I feel like I understand why, but I have no idea how to fix it ...
nridge added a subscriber: nridge.Feb 28 2021, 12:01 AM

I am much more afraid of providing bad results than I am afraid of degrading performance. I mean, eventually the "real" preamble is built, and the results are just as correct as before, but it may yield incorrect results until we invalidate the AST.
That is especially the case as I'm not very experienced in compiler design and I don't have a deep understanding of what happens behind the curtains when using a PCH.
There are probably cases where we degrade perf, but overall my experience with this patch is overwhelmingly positive, especially for "long coding sessions".

I am afraid this can happen more than twice

I'm not sure I follow, I see these different situations (in TUScheduler):

  • runWithPreamble
    • PreambleConsistency::Stale -> will wait for a "real" preamble, no change in behavior
    • PreambleConsistency::StaleOrAbsent
      • a preamble is available, we'll use it, no change in behavior
      • the preamble is not yet built, we'll use a compatible preamble instead -> this means instant partial completion !
  • runWithAST
    • a preamble is available, we'll use it, no change in behavior
    • the preamble is not yet built, we'll use a compatible preamble to start building the AST straight away -> here there is a risk of double work, if the compatible preamble is useless, we'll start building the AST *while* we're also building the preamble

But I don't see how we can do worse than building the AST and the preamble at the same time ?

could you give a bit more information on average preamble build latency in the files you are testing this with

Extracts from VSCode logs, between the file being opened and the semantic token request completion.
Also worth noting that we have partial completion with 0 delay (with the compatible preamble)

Without the patch:

I[22:40:57.317] ASTWorker building file /home/quentin/dev/llvm-project/clang-tools-extra/clangd/ParsedAST.h version 1 with command inferred from /home/quentin/dev/llvm-project/clang-tools-extra/clangd/ParsedAST.cpp
I[22:41:07.893] --> reply:textDocument/semanticTokens/full(1) 9921 ms

I[22:41:16.526] ASTWorker building file /home/quentin/dev/llvm-project/clang-tools-extra/clangd/ParsedAST.cpp version 1 with command
I[22:41:38.401] --> reply:textDocument/semanticTokens/full(12) 21877 ms

I[22:41:53.861] ASTWorker building file /home/quentin/dev/llvm-project/clang-tools-extra/clangd/IncludeFixer.h version 1 with command inferred from /home/quentin/dev/llvm-project/clang-tools-extra/clangd/IncludeFixer.cpp
I[22:42:06.006] --> reply:textDocument/semanticTokens/full(23) 12144 ms

I[22:42:08.619] ASTWorker building file /home/quentin/dev/llvm-project/clang-tools-extra/clangd/IncludeFixer.cpp version 1 with command
I[22:42:21.920] --> reply:textDocument/semanticTokens/full(28) 13278 ms

With the patch

I[22:36:32.828] ASTWorker building file /home/quentin/dev/llvm-project/clang-tools-extra/clangd/ParsedAST.h version 1 with command inferred from /home/quentin/dev/llvm-project/clang-tools-extra/clangd/ParsedAST.cpp
I[22:36:43.211] --> reply:textDocument/semanticTokens/full(3) 9675 ms

I[22:36:52.331] ASTWorker building file /home/quentin/dev/llvm-project/clang-tools-extra/clangd/ParsedAST.cpp version 1 with command
I[22:36:58.206] --> reply:textDocument/semanticTokens/full(9) 5881 ms

I[22:37:13.474] ASTWorker building file /home/quentin/dev/llvm-project/clang-tools-extra/clangd/IncludeFixer.h version 1 with command inferred from /home/quentin/dev/llvm-project/clang-tools-extra/clangd/IncludeFixer.cpp
I[22:37:15.840] --> reply:textDocument/semanticTokens/full(15) 2351 ms

I[22:37:23.939] ASTWorker building file /home/quentin/dev/llvm-project/clang-tools-extra/clangd/IncludeFixer.cpp version 1 with command
I[22:37:25.030] --> reply:textDocument/semanticTokens/full(20) 1089 ms
qchateau updated this revision to Diff 327922.Mar 3 2021, 2:29 PM

rebase on main, fix formatting

qchateau updated this revision to Diff 327923.Mar 3 2021, 2:31 PM

fix bad arc diff

Have you guys been giving some thoughts to that patch ? I've been using it in my daily work since I submitted the patch, and I'd not go back

Herald added a project: Restricted Project. · View Herald TranscriptApr 15 2021, 1:16 PM
qchateau updated this revision to Diff 374654.Sep 23 2021, 1:16 PM
  • Rebase
  • Add cli argument to set the cache size
  • Reduce default cache size to 1 (to allow fast open/close/reopen)
qchateau abandoned this revision.Oct 27 2021, 1:33 PM