fix: review follow-ups for #5 and the Swift lexical preview #8
1 changed files with 24 additions and 13 deletions
perf(scan): index import edges once instead of scanning per file
imported_paths found the file node with a linear scan of all nodes and then filtered all edges, for every Python/Dart file it built a set for: O(files x (nodes + edges)) during resolution, about 10^10 comparisons for 10k files and a million edges. Imports edges are now indexed by source file on first use and the file node is looked up by its stable id, as resolve_import already does. The index is complete when first built: resolve_relationships resolves every import before any call. Output is unchanged (all 124 tests pass).
commit
3fbb9275a5
33
src/scan.rs
33
src/scan.rs
|
|
@ -248,6 +248,9 @@ struct ScanState {
|
||||||
references: HashMap<String, HashSet<String>>,
|
references: HashMap<String, HashSet<String>>,
|
||||||
/// Lexical-language file -> repository files it imports (resolved edges).
|
/// Lexical-language file -> repository files it imports (resolved edges).
|
||||||
imported_paths: HashMap<String, Rc<HashSet<String>>>,
|
imported_paths: HashMap<String, Rc<HashSet<String>>>,
|
||||||
|
/// Imports edges by source file id, built once on first use: scanning every
|
||||||
|
/// node and edge per file made resolution O(files x (nodes + edges)).
|
||||||
|
imports_by_file: Option<HashMap<String, Vec<String>>>,
|
||||||
/// `name:` of the root pubspec.yaml, so `package:<name>/x.dart` maps to lib/x.dart.
|
/// `name:` of the root pubspec.yaml, so `package:<name>/x.dart` maps to lib/x.dart.
|
||||||
dart_package: Option<String>,
|
dart_package: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
@ -283,6 +286,7 @@ impl ScanState {
|
||||||
rust: RustIndex::default(),
|
rust: RustIndex::default(),
|
||||||
references: HashMap::new(),
|
references: HashMap::new(),
|
||||||
imported_paths: HashMap::new(),
|
imported_paths: HashMap::new(),
|
||||||
|
imports_by_file: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1319,20 +1323,27 @@ impl ScanState {
|
||||||
if let Some(found) = self.imported_paths.get(path) {
|
if let Some(found) = self.imported_paths.get(path) {
|
||||||
return Rc::clone(found);
|
return Rc::clone(found);
|
||||||
}
|
}
|
||||||
|
if self.imports_by_file.is_none() {
|
||||||
|
let mut index: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
|
for edge in self.edges.iter().filter(|e| e.kind == EdgeKind::Imports) {
|
||||||
|
index
|
||||||
|
.entry(edge.source.clone())
|
||||||
|
.or_default()
|
||||||
|
.push(edge.target.clone());
|
||||||
|
}
|
||||||
|
self.imports_by_file = Some(index);
|
||||||
|
}
|
||||||
|
let file_id = stable_id("file", path);
|
||||||
let paths: HashSet<String> = self
|
let paths: HashSet<String> = self
|
||||||
.nodes
|
.imports_by_file
|
||||||
.iter()
|
.as_ref()
|
||||||
.find(|n| n.kind == NodeKind::File && n.path.as_deref() == Some(path))
|
.and_then(|index| index.get(&file_id))
|
||||||
.map(|file| {
|
.into_iter()
|
||||||
self.edges
|
.flatten()
|
||||||
.iter()
|
.filter_map(|target| self.node(target))
|
||||||
.filter(|e| e.kind == EdgeKind::Imports && e.source == file.id)
|
|
||||||
.filter_map(|e| self.node(&e.target))
|
|
||||||
.filter(|n| n.kind == NodeKind::File)
|
.filter(|n| n.kind == NodeKind::File)
|
||||||
.filter_map(|n| n.path.clone())
|
.filter_map(|n| n.path.clone())
|
||||||
.collect()
|
.collect();
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
let paths = Rc::new(paths);
|
let paths = Rc::new(paths);
|
||||||
self.imported_paths
|
self.imported_paths
|
||||||
.insert(path.to_owned(), Rc::clone(&paths));
|
.insert(path.to_owned(), Rc::clone(&paths));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue