aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-06-16 17:19:38 +0100
committerAleksey Kladov <[email protected]>2019-06-16 17:45:05 +0100
commit6314e62cfb06ea7bbe5f530f2824010be0ffa4c7 (patch)
tree022742121ea273b6e90f6fbfa342660f46e9f68e /crates
parentb81caed43f1886024ededad41a1baa8a03f1d2f4 (diff)
add analysis-bench to benchmark incremental analysis
Can be used like this: ``` $ cargo run --release -p ra_cli -- \ analysis-bench ../chalk/ \ --complete ../chalk/chalk-engine/src/logic.rs:94:0 loading: 225.970093ms from scratch: 8.492373325s no change: 445.265µs trivial change: 95.631242ms ``` Or like this: ``` $ cargo run --release -p ra_cli -- \ analysis-bench ../chalk/ \ --highlight ../chalk/chalk-engine/src/logic.rs loading: 209.873484ms from scratch: 9.504916942s no change: 7.731119ms trivial change: 124.984039ms ``` "from scratch" includes initial analysis of the relevant bits of the project "no change" just asks the same question for the second time. It measures overhead on assembling the answer outside of salsa. "trivial change" doesn't do an actual salsa change, it just advances the revision. This test how fast is salsa at validating things.
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_batch/src/lib.rs42
-rw-r--r--crates/ra_batch/src/vfs_filter.rs108
-rw-r--r--crates/ra_cli/src/analysis_bench.rs92
-rw-r--r--crates/ra_cli/src/analysis_stats.rs19
-rw-r--r--crates/ra_cli/src/main.rs47
-rw-r--r--crates/ra_ide_api/src/lib.rs2
-rw-r--r--crates/ra_ide_api/src/line_index.rs2
-rw-r--r--crates/ra_project_model/src/lib.rs1
8 files changed, 232 insertions, 81 deletions
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs
index fa244e86c..43d3fb7e3 100644
--- a/crates/ra_batch/src/lib.rs
+++ b/crates/ra_batch/src/lib.rs
@@ -8,7 +8,7 @@ use ra_db::{
8 CrateGraph, FileId, SourceRootId, 8 CrateGraph, FileId, SourceRootId,
9}; 9};
10use ra_ide_api::{AnalysisHost, AnalysisChange}; 10use ra_ide_api::{AnalysisHost, AnalysisChange};
11use ra_project_model::ProjectWorkspace; 11use ra_project_model::{ProjectWorkspace, ProjectRoot};
12use ra_vfs::{Vfs, VfsChange}; 12use ra_vfs::{Vfs, VfsChange};
13use vfs_filter::IncludeRustFiles; 13use vfs_filter::IncludeRustFiles;
14 14
@@ -21,13 +21,11 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
21 SourceRootId(r.0) 21 SourceRootId(r.0)
22} 22}
23 23
24pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, Vec<SourceRootId>)> { 24pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, ProjectRoot>)> {
25 let root = std::env::current_dir()?.join(root); 25 let root = std::env::current_dir()?.join(root);
26 let ws = ProjectWorkspace::discover(root.as_ref())?; 26 let ws = ProjectWorkspace::discover(root.as_ref())?;
27 let mut roots = Vec::new(); 27 let project_roots = ws.to_roots();
28 roots.push(IncludeRustFiles::member(root.clone())); 28 let (mut vfs, roots) = Vfs::new(IncludeRustFiles::from_roots(project_roots.clone()).collect());
29 roots.extend(IncludeRustFiles::from_roots(ws.to_roots()));
30 let (mut vfs, roots) = Vfs::new(roots);
31 let crate_graph = ws.to_crate_graph(&mut |path: &Path| { 29 let crate_graph = ws.to_crate_graph(&mut |path: &Path| {
32 let vfs_file = vfs.load(path); 30 let vfs_file = vfs.load(path);
33 log::debug!("vfs file {:?} -> {:?}", path, vfs_file); 31 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
@@ -35,17 +33,27 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, Vec<SourceRootId>)> {
35 }); 33 });
36 log::debug!("crate graph: {:?}", crate_graph); 34 log::debug!("crate graph: {:?}", crate_graph);
37 35
38 let local_roots = roots 36 let source_roots = roots
39 .into_iter() 37 .iter()
40 .filter(|r| vfs.root2path(*r).starts_with(&root)) 38 .map(|&vfs_root| {
41 .map(vfs_root_to_id) 39 let source_root_id = vfs_root_to_id(vfs_root);
42 .collect(); 40 let project_root = project_roots
43 41 .iter()
44 let host = load(root.as_path(), crate_graph, &mut vfs); 42 .find(|it| it.path() == &vfs.root2path(vfs_root))
45 Ok((host, local_roots)) 43 .unwrap()
44 .clone();
45 (source_root_id, project_root)
46 })
47 .collect::<FxHashMap<_, _>>();
48 let host = load(&source_roots, crate_graph, &mut vfs);
49 Ok((host, source_roots))
46} 50}
47 51
48pub fn load(project_root: &Path, crate_graph: CrateGraph, vfs: &mut Vfs) -> AnalysisHost { 52pub fn load(
53 source_roots: &FxHashMap<SourceRootId, ProjectRoot>,
54 crate_graph: CrateGraph,
55 vfs: &mut Vfs,
56) -> AnalysisHost {
49 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); 57 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
50 let mut host = AnalysisHost::new(lru_cap); 58 let mut host = AnalysisHost::new(lru_cap);
51 let mut analysis_change = AnalysisChange::new(); 59 let mut analysis_change = AnalysisChange::new();
@@ -60,8 +68,8 @@ pub fn load(project_root: &Path, crate_graph: CrateGraph, vfs: &mut Vfs) -> Anal
60 for change in vfs.commit_changes() { 68 for change in vfs.commit_changes() {
61 match change { 69 match change {
62 VfsChange::AddRoot { root, files } => { 70 VfsChange::AddRoot { root, files } => {
63 let is_local = vfs.root2path(root).starts_with(&project_root);
64 let source_root_id = vfs_root_to_id(root); 71 let source_root_id = vfs_root_to_id(root);
72 let is_local = source_roots[&source_root_id].is_member();
65 log::debug!( 73 log::debug!(
66 "loaded source root {:?} with path {:?}", 74 "loaded source root {:?} with path {:?}",
67 source_root_id, 75 source_root_id,
@@ -106,7 +114,7 @@ mod tests {
106 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); 114 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
107 let (host, roots) = load_cargo(path).unwrap(); 115 let (host, roots) = load_cargo(path).unwrap();
108 let mut n_crates = 0; 116 let mut n_crates = 0;
109 for root in roots { 117 for (root, _) in roots {
110 for _krate in Crate::source_root_crates(host.raw_database(), root) { 118 for _krate in Crate::source_root_crates(host.raw_database(), root) {
111 n_crates += 1; 119 n_crates += 1;
112 } 120 }
diff --git a/crates/ra_batch/src/vfs_filter.rs b/crates/ra_batch/src/vfs_filter.rs
index dd20c1203..8552ac999 100644
--- a/crates/ra_batch/src/vfs_filter.rs
+++ b/crates/ra_batch/src/vfs_filter.rs
@@ -1,54 +1,54 @@
1use std::path::PathBuf; 1use std::path::PathBuf;
2use ra_project_model::ProjectRoot; 2use ra_project_model::ProjectRoot;
3use ra_vfs::{RootEntry, Filter, RelativePath}; 3use ra_vfs::{RootEntry, Filter, RelativePath};
4 4
5/// `IncludeRustFiles` is used to convert 5/// `IncludeRustFiles` is used to convert
6/// from `ProjectRoot` to `RootEntry` for VFS 6/// from `ProjectRoot` to `RootEntry` for VFS
7pub struct IncludeRustFiles { 7pub struct IncludeRustFiles {
8 root: ProjectRoot, 8 root: ProjectRoot,
9} 9}
10 10
11impl IncludeRustFiles { 11impl IncludeRustFiles {
12 pub fn from_roots<R>(roots: R) -> impl Iterator<Item = RootEntry> 12 pub fn from_roots<R>(roots: R) -> impl Iterator<Item = RootEntry>
13 where 13 where
14 R: IntoIterator<Item = ProjectRoot>, 14 R: IntoIterator<Item = ProjectRoot>,
15 { 15 {
16 roots.into_iter().map(IncludeRustFiles::from_root) 16 roots.into_iter().map(IncludeRustFiles::from_root)
17 } 17 }
18 18
19 pub fn from_root(root: ProjectRoot) -> RootEntry { 19 pub fn from_root(root: ProjectRoot) -> RootEntry {
20 IncludeRustFiles::from(root).into() 20 IncludeRustFiles::from(root).into()
21 } 21 }
22 22
23 #[allow(unused)] 23 #[allow(unused)]
24 pub fn external(path: PathBuf) -> RootEntry { 24 pub fn external(path: PathBuf) -> RootEntry {
25 IncludeRustFiles::from_root(ProjectRoot::new(path, false)) 25 IncludeRustFiles::from_root(ProjectRoot::new(path, false))
26 } 26 }
27 27
28 pub fn member(path: PathBuf) -> RootEntry { 28 pub fn member(path: PathBuf) -> RootEntry {
29 IncludeRustFiles::from_root(ProjectRoot::new(path, true)) 29 IncludeRustFiles::from_root(ProjectRoot::new(path, true))
30 } 30 }
31} 31}
32 32
33impl Filter for IncludeRustFiles { 33impl Filter for IncludeRustFiles {
34 fn include_dir(&self, dir_path: &RelativePath) -> bool { 34 fn include_dir(&self, dir_path: &RelativePath) -> bool {
35 self.root.include_dir(dir_path) 35 self.root.include_dir(dir_path)
36 } 36 }
37 37
38 fn include_file(&self, file_path: &RelativePath) -> bool { 38 fn include_file(&self, file_path: &RelativePath) -> bool {
39 self.root.include_file(file_path) 39 self.root.include_file(file_path)
40 } 40 }
41} 41}
42 42
43impl std::convert::From<ProjectRoot> for IncludeRustFiles { 43impl From<ProjectRoot> for IncludeRustFiles {
44 fn from(v: ProjectRoot) -> IncludeRustFiles { 44 fn from(v: ProjectRoot) -> IncludeRustFiles {
45 IncludeRustFiles { root: v } 45 IncludeRustFiles { root: v }
46 } 46 }
47} 47}
48 48
49impl std::convert::From<IncludeRustFiles> for RootEntry { 49impl From<IncludeRustFiles> for RootEntry {
50 fn from(v: IncludeRustFiles) -> RootEntry { 50 fn from(v: IncludeRustFiles) -> RootEntry {
51 let path = v.root.path().clone(); 51 let path = v.root.path().clone();
52 RootEntry::new(path, Box::new(v)) 52 RootEntry::new(path, Box::new(v))
53 } 53 }
54} 54}
diff --git a/crates/ra_cli/src/analysis_bench.rs b/crates/ra_cli/src/analysis_bench.rs
new file mode 100644
index 000000000..33d472838
--- /dev/null
+++ b/crates/ra_cli/src/analysis_bench.rs
@@ -0,0 +1,92 @@
1use std::{
2 path::{PathBuf, Path},
3 time::Instant,
4};
5
6use ra_db::{SourceDatabase, salsa::Database};
7use ra_ide_api::{AnalysisHost, Analysis, LineCol, FilePosition};
8
9use crate::Result;
10
11pub(crate) enum Op {
12 Highlight { path: PathBuf },
13 Complete { path: PathBuf, line: u32, column: u32 },
14}
15
16pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> {
17 let start = Instant::now();
18 eprint!("loading: ");
19 let (host, roots) = ra_batch::load_cargo(path)?;
20 let db = host.raw_database();
21 eprintln!("{:?}\n", start.elapsed());
22
23 let file_id = {
24 let path = match &op {
25 Op::Highlight { path } => path,
26 Op::Complete { path, .. } => path,
27 };
28 let path = std::env::current_dir()?.join(path).canonicalize()?;
29 roots
30 .iter()
31 .find_map(|(source_root_id, project_root)| {
32 if project_root.is_member() {
33 for (rel_path, file_id) in &db.source_root(*source_root_id).files {
34 let abs_path = rel_path.to_path(project_root.path());
35 if abs_path == path {
36 return Some(*file_id);
37 }
38 }
39 }
40 None
41 })
42 .ok_or_else(|| format!("Can't find {:?}", path))?
43 };
44
45 match op {
46 Op::Highlight { .. } => {
47 let res = do_work(&host, |analysis| {
48 analysis.diagnostics(file_id).unwrap();
49 analysis.highlight_as_html(file_id, false).unwrap()
50 });
51 if verbose {
52 println!("\n{}", res);
53 }
54 }
55 Op::Complete { line, column, .. } => {
56 let offset = host
57 .analysis()
58 .file_line_index(file_id)
59 .offset(LineCol { line, col_utf16: column });
60 let file_postion = FilePosition { file_id, offset };
61
62 let res = do_work(&host, |analysis| analysis.completions(file_postion));
63 if verbose {
64 println!("\n{:#?}", res);
65 }
66 }
67 }
68 Ok(())
69}
70
71fn do_work<F: Fn(&Analysis) -> T, T>(host: &AnalysisHost, work: F) -> T {
72 {
73 let start = Instant::now();
74 eprint!("from scratch: ");
75 work(&host.analysis());
76 eprintln!("{:?}", start.elapsed());
77 }
78 {
79 let start = Instant::now();
80 eprint!("no change: ");
81 work(&host.analysis());
82 eprintln!("{:?}", start.elapsed());
83 }
84 {
85 let start = Instant::now();
86 eprint!("trivial change: ");
87 host.raw_database().salsa_runtime().next_revision();
88 let res = work(&host.analysis());
89 eprintln!("{:?}", start.elapsed());
90 res
91 }
92}
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs
index d76c37d84..ed98fc7f6 100644
--- a/crates/ra_cli/src/analysis_stats.rs
+++ b/crates/ra_cli/src/analysis_stats.rs
@@ -1,4 +1,4 @@
1use std::{collections::HashSet, time::Instant, fmt::Write}; 1use std::{collections::HashSet, time::Instant, fmt::Write, path::Path};
2 2
3use ra_db::SourceDatabase; 3use ra_db::SourceDatabase;
4use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource}; 4use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource};
@@ -6,20 +6,23 @@ use ra_syntax::AstNode;
6 6
7use crate::Result; 7use crate::Result;
8 8
9pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> { 9pub fn run(verbose: bool, path: &Path, only: Option<&str>) -> Result<()> {
10 let db_load_time = Instant::now(); 10 let db_load_time = Instant::now();
11 let (host, roots) = ra_batch::load_cargo(path.as_ref())?; 11 let (host, roots) = ra_batch::load_cargo(path)?;
12 let db = host.raw_database(); 12 let db = host.raw_database();
13 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed()); 13 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
14 let analysis_time = Instant::now(); 14 let analysis_time = Instant::now();
15 let mut num_crates = 0; 15 let mut num_crates = 0;
16 let mut visited_modules = HashSet::new(); 16 let mut visited_modules = HashSet::new();
17 let mut visit_queue = Vec::new(); 17 let mut visit_queue = Vec::new();
18 for root in roots { 18 for (source_root_id, project_root) in roots {
19 for krate in Crate::source_root_crates(db, root) { 19 if project_root.is_member() {
20 num_crates += 1; 20 for krate in Crate::source_root_crates(db, source_root_id) {
21 let module = krate.root_module(db).expect("crate in source root without root module"); 21 num_crates += 1;
22 visit_queue.push(module); 22 let module =
23 krate.root_module(db).expect("crate in source root without root module");
24 visit_queue.push(module);
25 }
23 } 26 }
24 } 27 }
25 println!("Crates in this dir: {}", num_crates); 28 println!("Crates in this dir: {}", num_crates);
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index 1db98aec1..5adf8b096 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -1,4 +1,5 @@
1mod analysis_stats; 1mod analysis_stats;
2mod analysis_bench;
2 3
3use std::{io::Read, error::Error}; 4use std::{io::Read, error::Error};
4 5
@@ -26,6 +27,27 @@ fn main() -> Result<()> {
26 .arg(Arg::with_name("only").short("o").takes_value(true)) 27 .arg(Arg::with_name("only").short("o").takes_value(true))
27 .arg(Arg::with_name("path")), 28 .arg(Arg::with_name("path")),
28 ) 29 )
30 .subcommand(
31 SubCommand::with_name("analysis-bench")
32 .arg(Arg::with_name("verbose").short("v").long("verbose"))
33 .arg(
34 Arg::with_name("highlight")
35 .long("highlight")
36 .takes_value(true)
37 .conflicts_with("complete")
38 .value_name("PATH")
39 .help("highlight this file"),
40 )
41 .arg(
42 Arg::with_name("complete")
43 .long("complete")
44 .takes_value(true)
45 .conflicts_with("highlight")
46 .value_name("PATH:LINE:COLUMN")
47 .help("compute completions at this location"),
48 )
49 .arg(Arg::with_name("path").value_name("PATH").help("project to analyze")),
50 )
29 .get_matches(); 51 .get_matches();
30 match matches.subcommand() { 52 match matches.subcommand() {
31 ("parse", Some(matches)) => { 53 ("parse", Some(matches)) => {
@@ -51,7 +73,25 @@ fn main() -> Result<()> {
51 let verbose = matches.is_present("verbose"); 73 let verbose = matches.is_present("verbose");
52 let path = matches.value_of("path").unwrap_or(""); 74 let path = matches.value_of("path").unwrap_or("");
53 let only = matches.value_of("only"); 75 let only = matches.value_of("only");
54 analysis_stats::run(verbose, path, only)?; 76 analysis_stats::run(verbose, path.as_ref(), only)?;
77 }
78 ("analysis-bench", Some(matches)) => {
79 let verbose = matches.is_present("verbose");
80 let path = matches.value_of("path").unwrap_or("");
81 let op = if let Some(path) = matches.value_of("highlight") {
82 analysis_bench::Op::Highlight { path: path.into() }
83 } else if let Some(path_line_col) = matches.value_of("complete") {
84 let (path_line, column) = rsplit_at_char(path_line_col, ':')?;
85 let (path, line) = rsplit_at_char(path_line, ':')?;
86 analysis_bench::Op::Complete {
87 path: path.into(),
88 line: line.parse()?,
89 column: column.parse()?,
90 }
91 } else {
92 panic!("either --highlight or --complete must be set")
93 };
94 analysis_bench::run(verbose, path.as_ref(), op)?;
55 } 95 }
56 _ => unreachable!(), 96 _ => unreachable!(),
57 } 97 }
@@ -68,3 +108,8 @@ fn read_stdin() -> Result<String> {
68 std::io::stdin().read_to_string(&mut buff)?; 108 std::io::stdin().read_to_string(&mut buff)?;
69 Ok(buff) 109 Ok(buff)
70} 110}
111
112fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> {
113 let idx = s.rfind(":").ok_or_else(|| format!("no `{}` in {}", c, s))?;
114 Ok((&s[..idx], &s[idx + 1..]))
115}
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index a68c5e2a5..e61d5627e 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -276,7 +276,7 @@ impl AnalysisHost {
276 pub fn collect_garbage(&mut self) { 276 pub fn collect_garbage(&mut self) {
277 self.db.collect_garbage(); 277 self.db.collect_garbage();
278 } 278 }
279 pub fn raw_database(&self) -> &impl hir::db::HirDatabase { 279 pub fn raw_database(&self) -> &(impl hir::db::HirDatabase + salsa::Database) {
280 &self.db 280 &self.db
281 } 281 }
282} 282}
diff --git a/crates/ra_ide_api/src/line_index.rs b/crates/ra_ide_api/src/line_index.rs
index 087dfafed..a53cf9ee0 100644
--- a/crates/ra_ide_api/src/line_index.rs
+++ b/crates/ra_ide_api/src/line_index.rs
@@ -10,7 +10,9 @@ pub struct LineIndex {
10 10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub struct LineCol { 12pub struct LineCol {
13 /// Zero-based
13 pub line: u32, 14 pub line: u32,
15 /// Zero-based
14 pub col_utf16: u32, 16 pub col_utf16: u32,
15} 17}
16 18
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a3af153f1..42156bea6 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -37,6 +37,7 @@ pub enum ProjectWorkspace {
37/// `ProjectRoot` describes a workspace root folder. 37/// `ProjectRoot` describes a workspace root folder.
38/// Which may be an external dependency, or a member of 38/// Which may be an external dependency, or a member of
39/// the current workspace. 39/// the current workspace.
40#[derive(Clone)]
40pub struct ProjectRoot { 41pub struct ProjectRoot {
41 /// Path to the root folder 42 /// Path to the root folder
42 path: PathBuf, 43 path: PathBuf,