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:
bors[bot] 2021-03-30 09:47:49 +00:00 committed by GitHub
commit 2490ab7718
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 262 deletions

View 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();
}
}

View file

@ -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"),
}
}
}

View file

@ -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)?

View file

@ -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},

View file

@ -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
}
}

View file

@ -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;