Skip to content

Commit a5dddd7

Browse files
committed
perf(sema): use a map in AST Sources
Removes O(num_sources^2) time complexity in `Sources::get_or_insert_file`.
1 parent e4102f6 commit a5dddd7

File tree

1 file changed

+57
-35
lines changed

1 file changed

+57
-35
lines changed

crates/sema/src/parse.rs

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rayon::prelude::*;
33
use solar_ast::{self as ast, Span};
44
use solar_data_structures::{
55
index::{Idx, IndexVec},
6-
map::FxHashSet,
6+
map::{FxHashMap, FxHashSet},
77
sync::Mutex,
88
};
99
use solar_interface::{
@@ -413,7 +413,8 @@ fn path_from_bytes(bytes: &[u8]) -> Option<&Path> {
413413
/// Sources.
414414
#[derive(Default)]
415415
pub struct Sources<'ast> {
416-
pub sources: IndexVec<SourceId, Source<'ast>>,
416+
sources: IndexVec<SourceId, Source<'ast>>,
417+
file_to_id: FxHashMap<Arc<SourceFile>, SourceId>,
417418
}
418419

419420
impl fmt::Debug for Sources<'_> {
@@ -426,7 +427,60 @@ impl fmt::Debug for Sources<'_> {
426427
impl<'ast> Sources<'ast> {
427428
/// Creates a new empty list of parsed sources.
428429
pub fn new() -> Self {
429-
Self { sources: IndexVec::new() }
430+
Self::default()
431+
}
432+
433+
/// Returns a reference to the source, if it exists.
434+
#[inline]
435+
pub fn get(&self, id: SourceId) -> Option<&Source<'ast>> {
436+
self.sources.get(id)
437+
}
438+
439+
/// Returns a mutable reference to the source, if it exists.
440+
#[inline]
441+
pub fn get_mut(&mut self, id: SourceId) -> Option<&mut Source<'ast>> {
442+
self.sources.get_mut(id)
443+
}
444+
445+
/// Returns the ID of the source file, if it exists.
446+
pub fn get_file(&self, file: &Arc<SourceFile>) -> Option<(SourceId, &Source<'ast>)> {
447+
self.file_to_id.get(file).map(|&id| (id, &self.sources[id]))
448+
}
449+
450+
/// Returns the ID of the source file, if it exists.
451+
pub fn get_file_mut(
452+
&mut self,
453+
file: &Arc<SourceFile>,
454+
) -> Option<(SourceId, &mut Source<'ast>)> {
455+
self.file_to_id.get(file).map(|&id| (id, &mut self.sources[id]))
456+
}
457+
458+
/// Returns the ID of the given file, or inserts it if it doesn't exist.
459+
///
460+
/// Returns `true` if the file was newly inserted.
461+
#[instrument(level = "debug", skip_all)]
462+
pub fn get_or_insert_file(&mut self, file: Arc<SourceFile>) -> (SourceId, bool) {
463+
let mut new = false;
464+
let id = *self.file_to_id.entry(file).or_insert_with_key(|file| {
465+
new = true;
466+
self.sources.push(Source::new(file.clone()))
467+
});
468+
(id, new)
469+
}
470+
471+
/// Removes the given file from the sources.
472+
pub fn remove_file(&mut self, file: &Arc<SourceFile>) -> Option<Source<'ast>> {
473+
self.file_to_id.remove(file).map(|id| self.sources.remove(id))
474+
}
475+
476+
/// Returns an iterator over all the ASTs.
477+
pub fn asts(&self) -> impl DoubleEndedIterator<Item = &ast::SourceUnit<'ast>> {
478+
self.sources.iter().filter_map(|source| source.ast.as_ref())
479+
}
480+
481+
/// Returns a parallel iterator over all the ASTs.
482+
pub fn par_asts(&self) -> impl ParallelIterator<Item = &ast::SourceUnit<'ast>> {
483+
self.sources.as_raw_slice().par_iter().filter_map(|source| source.ast.as_ref())
430484
}
431485

432486
fn count_parsed(&self) -> usize {
@@ -455,28 +509,6 @@ impl<'ast> Sources<'ast> {
455509
ret
456510
}
457511

458-
#[instrument(level = "debug", skip_all)]
459-
fn get_or_insert_file(&mut self, file: Arc<SourceFile>) -> (SourceId, bool) {
460-
if let Some((id, _)) = self.get_file(&file) {
461-
return (id, false);
462-
}
463-
(self.sources.push(Source::new(file)), true)
464-
}
465-
466-
/// Returns the ID of the source file, if it exists.
467-
pub fn get_file(&self, file: &Arc<SourceFile>) -> Option<(SourceId, &Source<'ast>)> {
468-
self.sources.iter_enumerated().find(|(_, source)| Arc::ptr_eq(&source.file, file))
469-
}
470-
471-
/// Returns the ID of the source file, if it exists.
472-
pub fn get_file_mut(
473-
&mut self,
474-
file: &Arc<SourceFile>,
475-
) -> Option<(SourceId, &mut Source<'ast>)> {
476-
let (id, _) = self.get_file(file)?;
477-
Some((id, &mut self.sources[id]))
478-
}
479-
480512
/// Asserts that all sources are unique.
481513
fn assert_unique(&self) {
482514
if self.sources.len() <= 1 {
@@ -490,16 +522,6 @@ impl<'ast> Sources<'ast> {
490522
);
491523
}
492524

493-
/// Returns an iterator over all the ASTs.
494-
pub fn asts(&self) -> impl DoubleEndedIterator<Item = &ast::SourceUnit<'ast>> {
495-
self.sources.iter().filter_map(|source| source.ast.as_ref())
496-
}
497-
498-
/// Returns a parallel iterator over all the ASTs.
499-
pub fn par_asts(&self) -> impl ParallelIterator<Item = &ast::SourceUnit<'ast>> {
500-
self.sources.as_raw_slice().par_iter().filter_map(|source| source.ast.as_ref())
501-
}
502-
503525
/// Sorts the sources topologically in-place. Invalidates all source IDs.
504526
#[instrument(level = "debug", skip_all)]
505527
pub fn topo_sort(&mut self) {

0 commit comments

Comments
 (0)