feat: make hightlighting linear

In https://youtu.be/qvIZZf5dmTE, we've noticed that AstIdMap does a
linear lookup when going from SyntaxNode to Id. This leads to
accidentally quadratic overall performance. Replace linear lookup with a
O(1) hashmap lookup.

Future work: don't duplicate `SyntaxNodePtr` in `AstIdMap` and switch to
"call site dependency injection" style storage (eg, store a
`HashSet<ErasedFileAstId>`).

See the explanation of the work here on YouTube
https://youtu.be/wvEgymUm7cY :-)
This commit is contained in:
Aleksey Kladov 2021-11-27 20:50:07 +03:00
parent b519a179b4
commit c603b9043f

View file

@ -14,6 +14,7 @@ use std::{
use la_arena::{Arena, Idx}; use la_arena::{Arena, Idx};
use profile::Count; use profile::Count;
use rustc_hash::FxHashMap;
use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
/// `AstId` points to an AST node in a specific file. /// `AstId` points to an AST node in a specific file.
@ -63,6 +64,7 @@ type ErasedFileAstId = Idx<SyntaxNodePtr>;
#[derive(Debug, PartialEq, Eq, Default)] #[derive(Debug, PartialEq, Eq, Default)]
pub struct AstIdMap { pub struct AstIdMap {
arena: Arena<SyntaxNodePtr>, arena: Arena<SyntaxNodePtr>,
map: FxHashMap<SyntaxNodePtr, ErasedFileAstId>,
_c: Count<Self>, _c: Count<Self>,
} }
@ -89,6 +91,7 @@ impl AstIdMap {
} }
} }
}); });
res.map.extend(res.arena.iter().map(|(idx, ptr)| (ptr.clone(), idx)));
res res
} }
@ -96,16 +99,16 @@ impl AstIdMap {
let raw = self.erased_ast_id(item.syntax()); let raw = self.erased_ast_id(item.syntax());
FileAstId { raw, _ty: PhantomData } FileAstId { raw, _ty: PhantomData }
} }
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId { fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
let ptr = SyntaxNodePtr::new(item); let ptr = SyntaxNodePtr::new(item);
match self.arena.iter().find(|(_id, i)| **i == ptr) { *self.map.get(&ptr).unwrap_or_else(|| {
Some((it, _)) => it, panic!(
None => panic!(
"Can't find {:?} in AstIdMap:\n{:?}", "Can't find {:?} in AstIdMap:\n{:?}",
item, item,
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
), )
} })
} }
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> { pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {