rust-analyzer/crates
Aleksey Kladov 099b63e7c0 feature: massively improve performance for large files
This story begins in #8384, where we added a smart test for our syntax
highting, which run the algorithm on synthetic files of varying length
in order to guesstimate if the complexity is O(N^2) or O(N)-ish.

The test turned out to be pretty effective, and flagged #9031 as a
change that makes syntax highlighting accidentally quadratic. There was
much rejoicing, for the time being.

Then, lnicola asked an ominous question[1]: "Are we sure that the time
is linear right now?"

Of course it turned out that our sophisticated non-linearity detector
*was* broken, and that our syntax highlighting *was* quadratic.

Investigating that, many brave hearts dug deeper and deeper into the
guts of rust-analyzer, only to get lost in a maze of traits delegating
to traits delegating to macros.

Eventually, matklad managed to peel off all layers of abstraction one by
one, until almost nothing was left. In fact, the issue was discovered in
the very foundation of the rust-analyzer -- in the syntax trees.

Worse, it was not a new problem, but rather a well-know, well-understood
and event (almost) well-fixed (!) performance bug.

The problem lies within `SyntaxNodePtr` type -- a light-weight "address"
of a node in a syntax tree [3]. Such pointers are used by rust-analyzer all
other the place to record relationships between IR nodes and the
original syntax.

Internally, the pointer to a syntax node is represented by node's range.
To "dereference" the pointer, you traverse the syntax tree from the
root, looking for the node with the right range. The inner loop of this
search is finding a node's child whose range contains the specified
range. This inner loop was implemented by naive linear search over all
the children. For wide trees, dereferencing a single `SyntaxNodePtr` was
linear. The problem with wide trees though is that they contain a lot of
nodes! And dereferencing pointers to all the nodes is quadratic in the
size of the file!

The solution to this problem is to speed up the children search --
rather than doing a linear lookup, we can use binary search to locate
the child with the desired interval.

Doing this optimization was one of the motivations (or rather, side
effects) of #6857. That's why `rowan` grew the useful
`child_or_token_at_range` method which does exactly this binary search.

But looks like we've never actually switch to this method? Oups.

Lesson learned: do not leave broken windows in the fundamental infra.
Otherwise, you'll have to repeatedly re-investigate the issue, by
digging from the top of the Everest down to the foundation!

[1]: https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/.60syntax_highlighting_not_quadratic.60.20failure/near/240811501
[2]: https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Syntax.20highlighting.20is.20quadratic
[3]: https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Syntax.20highlighting.20is.20quadratic/near/243412392
2021-06-21 20:14:38 +03:00
..
base_db Cleanup insert_use tests 2021-06-19 22:33:29 +02:00
cfg Implement a config override for the default #[cfg(test)] in cargo crates 2021-06-19 01:09:19 -07:00
flycheck Use package root as cargo check working directory 2021-05-12 19:50:52 -07:00
hir Apply some clippy suggestions 2021-06-21 16:40:21 +02:00
hir_def Apply some clippy suggestions 2021-06-21 16:40:21 +02:00
hir_expand Apply some clippy suggestions 2021-06-21 16:40:21 +02:00
hir_ty Fix benchmark_include_macro 2021-06-20 19:37:45 +02:00
ide Merge #9264 2021-06-21 14:15:49 +00:00
ide_assists Fix var name 2021-06-21 22:47:39 +09:00
ide_completion Merge #9356 2021-06-21 13:15:12 +00:00
ide_db Cleanup insert_use tests 2021-06-19 22:33:29 +02:00
ide_diagnostics minor: use minicore 2021-06-18 23:48:18 +03:00
ide_ssr clippy::redundant_field_names 2021-06-13 09:40:22 +05:30
mbe Apply some clippy suggestions 2021-06-21 16:40:21 +02:00
parser Complete repr attribute parameters 2021-06-17 21:15:49 +02:00
paths tree-wide: make rustdoc links spiky so they are clickable 2021-06-13 21:58:05 -07:00
proc_macro_api Minor clippy perf fixes 2021-06-18 14:40:51 +03:00
proc_macro_srv Apply some clippy suggestions 2021-06-21 16:40:21 +02:00
proc_macro_test Build test-macros in a build script 2021-06-09 17:16:52 +02:00
profile Merge #9260 2021-06-14 07:16:48 +00:00
project_model Merge #9227 2021-06-21 13:41:27 +00:00
rust-analyzer Merge #9264 2021-06-21 14:15:49 +00:00
stdx tree-wide: make rustdoc links spiky so they are clickable 2021-06-13 21:58:05 -07:00
syntax feature: massively improve performance for large files 2021-06-21 20:14:38 +03:00
test_utils test_utils: Make overlapping annotations possible 2021-06-20 19:12:06 +02:00
text_edit Avoid turning completion objects into builders 2020-11-16 23:16:41 +02:00
toolchain Add description for crates that will be published 2020-08-24 13:07:22 +02:00
tt Merge #9260 2021-06-14 07:16:48 +00:00
vfs Nest all the or-patterns! 2021-06-17 17:37:14 +02:00
vfs-notify Fix slow tests sometimes failing 2021-02-12 16:31:16 +01:00