mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 17:28:09 +00:00
Merge #8254
8254: internal: switch from CLI to internal benchmarking r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
2490ab7718
6 changed files with 76 additions and 262 deletions
69
crates/rust-analyzer/src/benchmarks.rs
Normal file
69
crates/rust-analyzer/src/benchmarks.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//! Fully integrated benchmarks for rust-analyzer, which load real cargo
|
||||||
|
//! projects.
|
||||||
|
//!
|
||||||
|
//! The benchmark here is used to debug specific performance regressions. If you
|
||||||
|
//! notice that, eg, completion is slow in some specific case, you can modify
|
||||||
|
//! code here exercise this specific completion, and thus have a fast
|
||||||
|
//! edit/compile/test cycle.
|
||||||
|
//!
|
||||||
|
//! Note that "Rust Analyzer: Run" action does not allow running a single test
|
||||||
|
//! in release mode in VS Code. There's however "Rust Analyzer: Copy Run Command Line"
|
||||||
|
//! which you can use to paste the command in terminal and add `--release` manually.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ide::Change;
|
||||||
|
use test_utils::project_root;
|
||||||
|
use vfs::{AbsPathBuf, VfsPath};
|
||||||
|
|
||||||
|
use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn benchmark_integrated_highlighting() {
|
||||||
|
// Don't run slow benchmark by default
|
||||||
|
if true {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load rust-analyzer itself.
|
||||||
|
let workspace_to_load = project_root();
|
||||||
|
let file = "./crates/ide_db/src/apply_change.rs";
|
||||||
|
|
||||||
|
let cargo_config = Default::default();
|
||||||
|
let load_cargo_config =
|
||||||
|
LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: false };
|
||||||
|
|
||||||
|
let (mut host, vfs, _proc_macro) = {
|
||||||
|
let _it = stdx::timeit("workspace loading");
|
||||||
|
load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_id = {
|
||||||
|
let file = workspace_to_load.join(file);
|
||||||
|
let path = VfsPath::from(AbsPathBuf::assert(file));
|
||||||
|
vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {}", path))
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let _it = stdx::timeit("initial");
|
||||||
|
let analysis = host.analysis();
|
||||||
|
analysis.highlight_as_html(file_id, false).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
profile::init_from("*>100");
|
||||||
|
|
||||||
|
{
|
||||||
|
let _it = stdx::timeit("change");
|
||||||
|
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
|
||||||
|
text.push_str("\npub fn _dummy() {}\n");
|
||||||
|
let mut change = Change::new();
|
||||||
|
change.change_file(file_id, Some(Arc::new(text)));
|
||||||
|
host.apply_change(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let _it = stdx::timeit("after change");
|
||||||
|
let analysis = host.analysis();
|
||||||
|
analysis.highlight_as_html(file_id, false).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
//! Grammar for the command-line arguments.
|
//! Grammar for the command-line arguments.
|
||||||
#![allow(unreachable_pub)]
|
#![allow(unreachable_pub)]
|
||||||
use std::{env, path::PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use ide_ssr::{SsrPattern, SsrRule};
|
use ide_ssr::{SsrPattern, SsrRule};
|
||||||
use rust_analyzer::cli::{BenchWhat, Position, Verbosity};
|
use rust_analyzer::cli::Verbosity;
|
||||||
use vfs::AbsPathBuf;
|
|
||||||
|
|
||||||
xflags::xflags! {
|
xflags::xflags! {
|
||||||
src "./src/bin/flags.rs"
|
src "./src/bin/flags.rs"
|
||||||
|
@ -74,27 +73,6 @@ xflags::xflags! {
|
||||||
optional --with-proc-macro
|
optional --with-proc-macro
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Benchmark specific analysis operation
|
|
||||||
cmd analysis-bench
|
|
||||||
/// Directory with Cargo.toml.
|
|
||||||
required path: PathBuf
|
|
||||||
{
|
|
||||||
/// Collect memory usage statistics.
|
|
||||||
optional --memory-usage
|
|
||||||
|
|
||||||
/// Compute syntax highlighting for this file
|
|
||||||
optional --highlight path: PathBuf
|
|
||||||
/// Compute completions at file:line:column location.
|
|
||||||
optional --complete location: Position
|
|
||||||
/// Compute goto definition at file:line:column location.
|
|
||||||
optional --goto-def location: Position
|
|
||||||
|
|
||||||
/// Load OUT_DIR values by running `cargo check` before analysis.
|
|
||||||
optional --load-output-dirs
|
|
||||||
/// Use proc-macro-srv for proc-macro expanding.
|
|
||||||
optional --with-proc-macro
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd diagnostics
|
cmd diagnostics
|
||||||
/// Directory with Cargo.toml.
|
/// Directory with Cargo.toml.
|
||||||
required path: PathBuf
|
required path: PathBuf
|
||||||
|
@ -142,7 +120,6 @@ pub enum RustAnalyzerCmd {
|
||||||
Symbols(Symbols),
|
Symbols(Symbols),
|
||||||
Highlight(Highlight),
|
Highlight(Highlight),
|
||||||
AnalysisStats(AnalysisStats),
|
AnalysisStats(AnalysisStats),
|
||||||
AnalysisBench(AnalysisBench),
|
|
||||||
Diagnostics(Diagnostics),
|
Diagnostics(Diagnostics),
|
||||||
Ssr(Ssr),
|
Ssr(Ssr),
|
||||||
Search(Search),
|
Search(Search),
|
||||||
|
@ -183,18 +160,6 @@ pub struct AnalysisStats {
|
||||||
pub with_proc_macro: bool,
|
pub with_proc_macro: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AnalysisBench {
|
|
||||||
pub path: PathBuf,
|
|
||||||
|
|
||||||
pub memory_usage: bool,
|
|
||||||
pub highlight: Option<PathBuf>,
|
|
||||||
pub complete: Option<Position>,
|
|
||||||
pub goto_def: Option<Position>,
|
|
||||||
pub load_output_dirs: bool,
|
|
||||||
pub with_proc_macro: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Diagnostics {
|
pub struct Diagnostics {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
@ -239,17 +204,3 @@ impl RustAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnalysisBench {
|
|
||||||
pub(crate) fn what(&self) -> BenchWhat {
|
|
||||||
match (&self.highlight, &self.complete, &self.goto_def) {
|
|
||||||
(Some(path), None, None) => {
|
|
||||||
let path = env::current_dir().unwrap().join(path);
|
|
||||||
BenchWhat::Highlight { path: AbsPathBuf::assert(path) }
|
|
||||||
}
|
|
||||||
(None, Some(position), None) => BenchWhat::Complete(position.clone()),
|
|
||||||
(None, None, Some(position)) => BenchWhat::GotoDef(position.clone()),
|
|
||||||
_ => panic!("exactly one of `--highlight`, `--complete` or `--goto-def` must be set"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::{convert::TryFrom, env, fs, path::Path, process};
|
||||||
use lsp_server::Connection;
|
use lsp_server::Connection;
|
||||||
use project_model::ProjectManifest;
|
use project_model::ProjectManifest;
|
||||||
use rust_analyzer::{
|
use rust_analyzer::{
|
||||||
cli::{self, AnalysisStatsCmd, BenchCmd},
|
cli::{self, AnalysisStatsCmd},
|
||||||
config::Config,
|
config::Config,
|
||||||
from_json,
|
from_json,
|
||||||
lsp_ext::supports_utf8,
|
lsp_ext::supports_utf8,
|
||||||
|
@ -80,17 +80,6 @@ fn try_main() -> Result<()> {
|
||||||
with_proc_macro: cmd.with_proc_macro,
|
with_proc_macro: cmd.with_proc_macro,
|
||||||
}
|
}
|
||||||
.run(verbosity)?,
|
.run(verbosity)?,
|
||||||
flags::RustAnalyzerCmd::AnalysisBench(cmd) => {
|
|
||||||
let what = cmd.what();
|
|
||||||
BenchCmd {
|
|
||||||
memory_usage: cmd.memory_usage,
|
|
||||||
path: cmd.path,
|
|
||||||
load_output_dirs: cmd.load_output_dirs,
|
|
||||||
with_proc_macro: cmd.with_proc_macro,
|
|
||||||
what,
|
|
||||||
}
|
|
||||||
.run(verbosity)?
|
|
||||||
}
|
|
||||||
|
|
||||||
flags::RustAnalyzerCmd::Diagnostics(cmd) => {
|
flags::RustAnalyzerCmd::Diagnostics(cmd) => {
|
||||||
cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)?
|
cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)?
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
//! Various batch processing tasks, intended primarily for debugging.
|
//! Various batch processing tasks, intended primarily for debugging.
|
||||||
|
|
||||||
mod load_cargo;
|
pub(crate) mod load_cargo;
|
||||||
mod analysis_stats;
|
mod analysis_stats;
|
||||||
mod analysis_bench;
|
|
||||||
mod diagnostics;
|
mod diagnostics;
|
||||||
mod progress_report;
|
mod progress_report;
|
||||||
mod ssr;
|
mod ssr;
|
||||||
|
@ -15,7 +14,6 @@ use syntax::{AstNode, SourceFile};
|
||||||
use vfs::Vfs;
|
use vfs::Vfs;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
analysis_bench::{BenchCmd, BenchWhat, Position},
|
|
||||||
analysis_stats::AnalysisStatsCmd,
|
analysis_stats::AnalysisStatsCmd,
|
||||||
diagnostics::diagnostics,
|
diagnostics::diagnostics,
|
||||||
load_cargo::{load_workspace, load_workspace_at, LoadCargoConfig},
|
load_cargo::{load_workspace, load_workspace_at, LoadCargoConfig},
|
||||||
|
|
|
@ -1,196 +0,0 @@
|
||||||
//! Benchmark operations like highlighting or goto definition.
|
|
||||||
|
|
||||||
use std::{env, path::PathBuf, str::FromStr, sync::Arc, time::Instant};
|
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Result};
|
|
||||||
use hir::PrefixKind;
|
|
||||||
use ide::{
|
|
||||||
Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol,
|
|
||||||
};
|
|
||||||
use ide_db::{
|
|
||||||
base_db::{
|
|
||||||
salsa::{Database, Durability},
|
|
||||||
FileId,
|
|
||||||
},
|
|
||||||
helpers::{insert_use::InsertUseConfig, SnippetCap},
|
|
||||||
};
|
|
||||||
use vfs::AbsPathBuf;
|
|
||||||
|
|
||||||
use crate::cli::{
|
|
||||||
load_cargo::{load_workspace_at, LoadCargoConfig},
|
|
||||||
print_memory_usage, Verbosity,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct BenchCmd {
|
|
||||||
pub path: PathBuf,
|
|
||||||
pub what: BenchWhat,
|
|
||||||
pub memory_usage: bool,
|
|
||||||
pub load_output_dirs: bool,
|
|
||||||
pub with_proc_macro: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum BenchWhat {
|
|
||||||
Highlight { path: AbsPathBuf },
|
|
||||||
Complete(Position),
|
|
||||||
GotoDef(Position),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Position {
|
|
||||||
pub path: AbsPathBuf,
|
|
||||||
pub line: u32,
|
|
||||||
pub column: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Position {
|
|
||||||
type Err = anyhow::Error;
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
|
||||||
let mut split = s.rsplitn(3, ':');
|
|
||||||
match (split.next(), split.next(), split.next()) {
|
|
||||||
(Some(column), Some(line), Some(path)) => {
|
|
||||||
let path = env::current_dir().unwrap().join(path);
|
|
||||||
let path = AbsPathBuf::assert(path);
|
|
||||||
Ok(Position { path, line: line.parse()?, column: column.parse()? })
|
|
||||||
}
|
|
||||||
_ => bail!("position should be in file:line:column format: {:?}", s),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BenchCmd {
|
|
||||||
pub fn run(self, verbosity: Verbosity) -> Result<()> {
|
|
||||||
profile::init();
|
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
eprint!("loading: ");
|
|
||||||
|
|
||||||
let cargo_config = Default::default();
|
|
||||||
let load_cargo_config = LoadCargoConfig {
|
|
||||||
load_out_dirs_from_check: self.load_output_dirs,
|
|
||||||
with_proc_macro: self.with_proc_macro,
|
|
||||||
};
|
|
||||||
let (mut host, vfs, _proc_macro) =
|
|
||||||
load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
|
|
||||||
eprintln!("{:?}\n", start.elapsed());
|
|
||||||
|
|
||||||
let file_id = {
|
|
||||||
let path = match &self.what {
|
|
||||||
BenchWhat::Highlight { path } => path,
|
|
||||||
BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path,
|
|
||||||
};
|
|
||||||
let path = path.clone().into();
|
|
||||||
vfs.file_id(&path).ok_or_else(|| format_err!("Can't find {}", path))?
|
|
||||||
};
|
|
||||||
|
|
||||||
match &self.what {
|
|
||||||
BenchWhat::Highlight { .. } => {
|
|
||||||
let res = do_work(&mut host, file_id, |analysis| {
|
|
||||||
analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
|
|
||||||
analysis.highlight_as_html(file_id, false).unwrap()
|
|
||||||
});
|
|
||||||
if verbosity.is_verbose() {
|
|
||||||
println!("\n{}", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => {
|
|
||||||
let is_completion = matches!(self.what, BenchWhat::Complete(..));
|
|
||||||
|
|
||||||
let offset = host
|
|
||||||
.analysis()
|
|
||||||
.file_line_index(file_id)?
|
|
||||||
.offset(LineCol { line: pos.line - 1, col: pos.column });
|
|
||||||
let file_position = FilePosition { file_id, offset };
|
|
||||||
|
|
||||||
if is_completion {
|
|
||||||
let options = CompletionConfig {
|
|
||||||
enable_postfix_completions: true,
|
|
||||||
enable_imports_on_the_fly: true,
|
|
||||||
add_call_parenthesis: true,
|
|
||||||
add_call_argument_snippets: true,
|
|
||||||
snippet_cap: SnippetCap::new(true),
|
|
||||||
insert_use: InsertUseConfig {
|
|
||||||
merge: None,
|
|
||||||
prefix_kind: PrefixKind::Plain,
|
|
||||||
group: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let res = do_work(&mut host, file_id, |analysis| {
|
|
||||||
analysis.completions(&options, file_position)
|
|
||||||
});
|
|
||||||
if verbosity.is_verbose() {
|
|
||||||
println!("\n{:#?}", res);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let res = do_work(&mut host, file_id, |analysis| {
|
|
||||||
analysis.goto_definition(file_position)
|
|
||||||
});
|
|
||||||
if verbosity.is_verbose() {
|
|
||||||
println!("\n{:#?}", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.memory_usage {
|
|
||||||
print_memory_usage(host, vfs);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, work: F) -> T {
|
|
||||||
{
|
|
||||||
let start = Instant::now();
|
|
||||||
eprint!("from scratch: ");
|
|
||||||
work(&host.analysis());
|
|
||||||
eprintln!("{:?}", start.elapsed());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let start = Instant::now();
|
|
||||||
eprint!("no change: ");
|
|
||||||
work(&host.analysis());
|
|
||||||
eprintln!("{:?}", start.elapsed());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let start = Instant::now();
|
|
||||||
eprint!("trivial change: ");
|
|
||||||
host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::LOW);
|
|
||||||
work(&host.analysis());
|
|
||||||
eprintln!("{:?}", start.elapsed());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let start = Instant::now();
|
|
||||||
eprint!("comment change: ");
|
|
||||||
{
|
|
||||||
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
|
|
||||||
text.push_str("\n/* Hello world */\n");
|
|
||||||
let mut change = Change::new();
|
|
||||||
change.change_file(file_id, Some(Arc::new(text)));
|
|
||||||
host.apply_change(change);
|
|
||||||
}
|
|
||||||
work(&host.analysis());
|
|
||||||
eprintln!("{:?}", start.elapsed());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let start = Instant::now();
|
|
||||||
eprint!("item change: ");
|
|
||||||
{
|
|
||||||
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
|
|
||||||
text.push_str("\npub fn _dummy() {}\n");
|
|
||||||
let mut change = Change::new();
|
|
||||||
change.change_file(file_id, Some(Arc::new(text)));
|
|
||||||
host.apply_change(change);
|
|
||||||
}
|
|
||||||
work(&host.analysis());
|
|
||||||
eprintln!("{:?}", start.elapsed());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let start = Instant::now();
|
|
||||||
eprint!("const change: ");
|
|
||||||
host.raw_database_mut().salsa_runtime_mut().synthetic_write(Durability::HIGH);
|
|
||||||
let res = work(&host.analysis());
|
|
||||||
eprintln!("{:?}", start.elapsed());
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,6 +39,9 @@ mod op_queue;
|
||||||
pub mod lsp_ext;
|
pub mod lsp_ext;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod benchmarks;
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue