1 //! Fully integrated benchmarks for rust-analyzer, which load real cargo
2 //! projects.
3 //!
4 //! The benchmark here is used to debug specific performance regressions. If you
5 //! notice that, eg, completion is slow in some specific case, you can  modify
6 //! code here exercise this specific completion, and thus have a fast
7 //! edit/compile/test cycle.
8 //!
9 //! Note that "Rust Analyzer: Run" action does not allow running a single test
10 //! in release mode in VS Code. There's however "Rust Analyzer: Copy Run Command Line"
11 //! which you can use to paste the command in terminal and add `--release` manually.
12 
13 use std::sync::Arc;
14 
15 use ide::{Change, CompletionConfig, FilePosition, TextSize};
16 use ide_db::helpers::{
17     insert_use::{ImportGranularity, InsertUseConfig},
18     SnippetCap,
19 };
20 use project_model::CargoConfig;
21 use test_utils::project_root;
22 use vfs::{AbsPathBuf, VfsPath};
23 
24 use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig};
25 
26 #[test]
integrated_highlighting_benchmark()27 fn integrated_highlighting_benchmark() {
28     if std::env::var("RUN_SLOW_BENCHES").is_err() {
29         return;
30     }
31 
32     // Load rust-analyzer itself.
33     let workspace_to_load = project_root();
34     let file = "./crates/ide_db/src/apply_change.rs";
35 
36     let cargo_config = CargoConfig::default();
37     let load_cargo_config = LoadCargoConfig {
38         load_out_dirs_from_check: true,
39         with_proc_macro: false,
40         prefill_caches: false,
41     };
42 
43     let (mut host, vfs, _proc_macro) = {
44         let _it = stdx::timeit("workspace loading");
45         load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap()
46     };
47 
48     let file_id = {
49         let file = workspace_to_load.join(file);
50         let path = VfsPath::from(AbsPathBuf::assert(file));
51         vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {}", path))
52     };
53 
54     {
55         let _it = stdx::timeit("initial");
56         let analysis = host.analysis();
57         analysis.highlight_as_html(file_id, false).unwrap();
58     }
59 
60     profile::init_from("*>100");
61     // let _s = profile::heartbeat_span();
62 
63     {
64         let _it = stdx::timeit("change");
65         let mut text = host.analysis().file_text(file_id).unwrap().to_string();
66         text.push_str("\npub fn _dummy() {}\n");
67         let mut change = Change::new();
68         change.change_file(file_id, Some(Arc::new(text)));
69         host.apply_change(change);
70     }
71 
72     {
73         let _it = stdx::timeit("after change");
74         let _span = profile::cpu_span();
75         let analysis = host.analysis();
76         analysis.highlight_as_html(file_id, false).unwrap();
77     }
78 }
79 
80 #[test]
integrated_completion_benchmark()81 fn integrated_completion_benchmark() {
82     if std::env::var("RUN_SLOW_BENCHES").is_err() {
83         return;
84     }
85 
86     // Load rust-analyzer itself.
87     let workspace_to_load = project_root();
88     let file = "./crates/hir/src/lib.rs";
89 
90     let cargo_config = CargoConfig::default();
91     let load_cargo_config = LoadCargoConfig {
92         load_out_dirs_from_check: true,
93         with_proc_macro: false,
94         prefill_caches: true,
95     };
96 
97     let (mut host, vfs, _proc_macro) = {
98         let _it = stdx::timeit("workspace loading");
99         load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap()
100     };
101 
102     let file_id = {
103         let file = workspace_to_load.join(file);
104         let path = VfsPath::from(AbsPathBuf::assert(file));
105         vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {}", path))
106     };
107 
108     {
109         let _it = stdx::timeit("initial");
110         let analysis = host.analysis();
111         analysis.highlight_as_html(file_id, false).unwrap();
112     }
113 
114     profile::init_from("*>5");
115     // let _s = profile::heartbeat_span();
116 
117     let completion_offset = {
118         let _it = stdx::timeit("change");
119         let mut text = host.analysis().file_text(file_id).unwrap().to_string();
120         let completion_offset =
121             patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
122                 + "sel".len();
123         let mut change = Change::new();
124         change.change_file(file_id, Some(Arc::new(text)));
125         host.apply_change(change);
126         completion_offset
127     };
128 
129     {
130         let _p = profile::span("unqualified path completion");
131         let _span = profile::cpu_span();
132         let analysis = host.analysis();
133         let config = CompletionConfig {
134             enable_postfix_completions: true,
135             enable_imports_on_the_fly: true,
136             enable_self_on_the_fly: true,
137             add_call_parenthesis: true,
138             add_call_argument_snippets: true,
139             snippet_cap: SnippetCap::new(true),
140             insert_use: InsertUseConfig {
141                 granularity: ImportGranularity::Crate,
142                 prefix_kind: hir::PrefixKind::ByCrate,
143                 enforce_granularity: true,
144                 group: true,
145                 skip_glob_imports: true,
146             },
147             snippets: Vec::new(),
148         };
149         let position =
150             FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
151         analysis.completions(&config, position).unwrap();
152     }
153 
154     let completion_offset = {
155         let _it = stdx::timeit("change");
156         let mut text = host.analysis().file_text(file_id).unwrap().to_string();
157         let completion_offset =
158             patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)")
159                 + "self.".len();
160         let mut change = Change::new();
161         change.change_file(file_id, Some(Arc::new(text)));
162         host.apply_change(change);
163         completion_offset
164     };
165 
166     {
167         let _p = profile::span("dot completion");
168         let _span = profile::cpu_span();
169         let analysis = host.analysis();
170         let config = CompletionConfig {
171             enable_postfix_completions: true,
172             enable_imports_on_the_fly: true,
173             enable_self_on_the_fly: true,
174             add_call_parenthesis: true,
175             add_call_argument_snippets: true,
176             snippet_cap: SnippetCap::new(true),
177             insert_use: InsertUseConfig {
178                 granularity: ImportGranularity::Crate,
179                 prefix_kind: hir::PrefixKind::ByCrate,
180                 enforce_granularity: true,
181                 group: true,
182                 skip_glob_imports: true,
183             },
184             snippets: Vec::new(),
185         };
186         let position =
187             FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
188         analysis.completions(&config, position).unwrap();
189     }
190 }
191 
patch(what: &mut String, from: &str, to: &str) -> usize192 fn patch(what: &mut String, from: &str, to: &str) -> usize {
193     let idx = what.find(from).unwrap();
194     *what = what.replacen(from, to, 1);
195     idx
196 }
197