Add rustc-perf to metrics

This commit is contained in:
Aleksey Kladov 2020-07-25 10:35:45 +02:00
parent 0a4e90c0f8
commit 451edcc098
6 changed files with 102 additions and 30 deletions

View file

@ -31,6 +31,12 @@ impl fmt::Display for MemoryUsage {
#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct Bytes(usize); pub struct Bytes(usize);
impl Bytes {
pub fn megabytes(self) -> usize {
self.0 / 1024 / 1024
}
}
impl fmt::Display for Bytes { impl fmt::Display for Bytes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.0; let bytes = self.0;

View file

@ -74,3 +74,10 @@ fn read_stdin() -> Result<String> {
std::io::stdin().read_to_string(&mut buff)?; std::io::stdin().read_to_string(&mut buff)?;
Ok(buff) Ok(buff)
} }
fn report_metric(metric: &str, value: u64, unit: &str) {
if std::env::var("RA_METRICS").is_err() {
return;
}
println!("METRIC:{}:{}:{}", metric, value, unit)
}

View file

@ -3,26 +3,27 @@
use std::{path::Path, time::Instant}; use std::{path::Path, time::Instant};
use itertools::Itertools;
use rand::{seq::SliceRandom, thread_rng};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
use hir::{ use hir::{
db::{AstDatabase, DefDatabase, HirDatabase}, db::{AstDatabase, DefDatabase, HirDatabase},
original_range, AssocItem, Crate, HasSource, HirDisplay, ModuleDef, original_range, AssocItem, Crate, HasSource, HirDisplay, ModuleDef,
}; };
use hir_def::FunctionId; use hir_def::FunctionId;
use hir_ty::{Ty, TypeWalk}; use hir_ty::{Ty, TypeWalk};
use itertools::Itertools;
use ra_db::{ use ra_db::{
salsa::{self, ParallelDatabase}, salsa::{self, ParallelDatabase},
SourceDatabaseExt, SourceDatabaseExt,
}; };
use ra_syntax::AstNode; use ra_syntax::AstNode;
use rand::{seq::SliceRandom, thread_rng};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
use stdx::format_to; use stdx::format_to;
use crate::{ use crate::{
cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity}, cli::{
load_cargo::load_cargo, progress_report::ProgressReport, report_metric, Result, Verbosity,
},
print_memory_usage, print_memory_usage,
}; };
@ -48,7 +49,7 @@ pub fn analysis_stats(
let db_load_time = Instant::now(); let db_load_time = Instant::now();
let (host, vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?; let (host, vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
let db = host.raw_database(); let db = host.raw_database();
println!("Database loaded {:?}", db_load_time.elapsed()); eprintln!("Database loaded {:?}", db_load_time.elapsed());
let analysis_time = Instant::now(); let analysis_time = Instant::now();
let mut num_crates = 0; let mut num_crates = 0;
let mut visited_modules = FxHashSet::default(); let mut visited_modules = FxHashSet::default();
@ -74,7 +75,7 @@ pub fn analysis_stats(
visit_queue.shuffle(&mut thread_rng()); visit_queue.shuffle(&mut thread_rng());
} }
println!("Crates in this dir: {}", num_crates); eprintln!("Crates in this dir: {}", num_crates);
let mut num_decls = 0; let mut num_decls = 0;
let mut funcs = Vec::new(); let mut funcs = Vec::new();
while let Some(module) = visit_queue.pop() { while let Some(module) = visit_queue.pop() {
@ -98,10 +99,15 @@ pub fn analysis_stats(
} }
} }
} }
println!("Total modules found: {}", visited_modules.len()); eprintln!("Total modules found: {}", visited_modules.len());
println!("Total declarations: {}", num_decls); eprintln!("Total declarations: {}", num_decls);
println!("Total functions: {}", funcs.len()); eprintln!("Total functions: {}", funcs.len());
println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); let item_collection_memory = ra_prof::memory_usage();
eprintln!(
"Item Collection: {:?}, {}",
analysis_time.elapsed(),
item_collection_memory.allocated
);
if randomize { if randomize {
funcs.shuffle(&mut thread_rng()); funcs.shuffle(&mut thread_rng());
@ -123,7 +129,11 @@ pub fn analysis_stats(
snap.0.infer(f_id.into()); snap.0.infer(f_id.into());
}) })
.count(); .count();
println!("Parallel Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage()); eprintln!(
"Parallel Inference: {:?}, {}",
inference_time.elapsed(),
ra_prof::memory_usage().allocated
);
} }
let inference_time = Instant::now(); let inference_time = Instant::now();
@ -260,20 +270,31 @@ pub fn analysis_stats(
bar.inc(1); bar.inc(1);
} }
bar.finish_and_clear(); bar.finish_and_clear();
println!("Total expressions: {}", num_exprs); eprintln!("Total expressions: {}", num_exprs);
println!( eprintln!(
"Expressions of unknown type: {} ({}%)", "Expressions of unknown type: {} ({}%)",
num_exprs_unknown, num_exprs_unknown,
if num_exprs > 0 { num_exprs_unknown * 100 / num_exprs } else { 100 } if num_exprs > 0 { num_exprs_unknown * 100 / num_exprs } else { 100 }
); );
println!( eprintln!(
"Expressions of partially unknown type: {} ({}%)", "Expressions of partially unknown type: {} ({}%)",
num_exprs_partially_unknown, num_exprs_partially_unknown,
if num_exprs > 0 { num_exprs_partially_unknown * 100 / num_exprs } else { 100 } if num_exprs > 0 { num_exprs_partially_unknown * 100 / num_exprs } else { 100 }
); );
println!("Type mismatches: {}", num_type_mismatches); eprintln!("Type mismatches: {}", num_type_mismatches);
println!("Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); let inference_time = inference_time.elapsed();
let total_memory = ra_prof::memory_usage();
eprintln!(
"Inference: {:?}, {}",
inference_time,
total_memory.allocated - item_collection_memory.allocated
);
let analysis_time = analysis_time.elapsed();
eprintln!("Total: {:?}, {}", analysis_time, total_memory);
report_metric("total time", analysis_time.as_millis() as u64, "ms");
report_metric("total memory", total_memory.allocated.megabytes() as u64, "MB");
if memory_usage { if memory_usage {
print_memory_usage(host, vfs); print_memory_usage(host, vfs);

View file

@ -86,6 +86,6 @@ fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) {
mem.push(("Remaining".into(), ra_prof::memory_usage().allocated)); mem.push(("Remaining".into(), ra_prof::memory_usage().allocated));
for (name, bytes) in mem { for (name, bytes) in mem {
println!("{:>8} {}", bytes, name); eprintln!("{:>8} {}", bytes, name);
} }
} }

View file

@ -3,14 +3,15 @@ use std::{
env, env,
fmt::{self, Write as _}, fmt::{self, Write as _},
io::Write as _, io::Write as _,
path::Path,
time::{Instant, SystemTime, UNIX_EPOCH}, time::{Instant, SystemTime, UNIX_EPOCH},
}; };
use anyhow::{bail, format_err, Result}; use anyhow::{bail, format_err, Result};
use crate::not_bash::{fs2, pushd, rm_rf, run}; use crate::not_bash::{fs2, pushd, pushenv, rm_rf, run};
type Unit = &'static str; type Unit = String;
pub struct MetricsCmd { pub struct MetricsCmd {
pub dry_run: bool, pub dry_run: bool,
@ -22,9 +23,21 @@ impl MetricsCmd {
if !self.dry_run { if !self.dry_run {
rm_rf("./target/release")?; rm_rf("./target/release")?;
} }
if !Path::new("./target/rustc-perf").exists() {
fs2::create_dir_all("./target/rustc-perf")?;
run!("git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf")?;
}
{
let _d = pushd("./target/rustc-perf");
run!("git reset --hard 1d9288b0da7febf2599917da1b57dc241a1af033")?;
}
let _env = pushenv("RA_METRICS", "1");
metrics.measure_build()?; metrics.measure_build()?;
metrics.measure_analysis_stats_self()?; metrics.measure_analysis_stats_self()?;
metrics.measure_analysis_stats("ripgrep")?;
metrics.measure_analysis_stats("webrender")?;
if !self.dry_run { if !self.dry_run {
let _d = pushd("target"); let _d = pushd("target");
@ -46,23 +59,47 @@ impl MetricsCmd {
impl Metrics { impl Metrics {
fn measure_build(&mut self) -> Result<()> { fn measure_build(&mut self) -> Result<()> {
eprintln!("\nMeasuring build");
run!("cargo fetch")?; run!("cargo fetch")?;
let time = Instant::now(); let time = Instant::now();
run!("cargo build --release --package rust-analyzer --bin rust-analyzer")?; run!("cargo build --release --package rust-analyzer --bin rust-analyzer")?;
let time = time.elapsed(); let time = time.elapsed();
self.report("build", time.as_millis() as u64, "ms"); self.report("build", time.as_millis() as u64, "ms".into());
Ok(()) Ok(())
} }
fn measure_analysis_stats_self(&mut self) -> Result<()> { fn measure_analysis_stats_self(&mut self) -> Result<()> {
let time = Instant::now(); self.measure_analysis_stats_path("self", &".")
run!("./target/release/rust-analyzer analysis-stats .")?; }
let time = time.elapsed(); fn measure_analysis_stats(&mut self, bench: &str) -> Result<()> {
self.report("analysis-stats/self", time.as_millis() as u64, "ms"); self.measure_analysis_stats_path(
bench,
&format!("./target/rustc-perf/collector/benchmarks/{}", bench),
)
}
fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> {
eprintln!("\nMeasuring analysis-stats/{}", name);
let output = run!("./target/release/rust-analyzer analysis-stats --quiet {}", path)?;
for (metric, value, unit) in parse_metrics(&output) {
self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into());
}
Ok(()) Ok(())
} }
} }
fn parse_metrics(output: &str) -> Vec<(&str, u64, &str)> {
output
.lines()
.filter_map(|it| {
let entry = it.split(':').collect::<Vec<_>>();
match entry.as_slice() {
["METRIC", name, value, unit] => Some((*name, value.parse().unwrap(), *unit)),
_ => None,
}
})
.collect()
}
#[derive(Debug)] #[derive(Debug)]
struct Metrics { struct Metrics {
host: Host, host: Host,
@ -111,11 +148,11 @@ impl Metrics {
json.field("metrics"); json.field("metrics");
json.begin_object(); json.begin_object();
{ {
for (k, &(value, unit)) in &self.metrics { for (k, (value, unit)) in &self.metrics {
json.field(k); json.field(k);
json.begin_array(); json.begin_array();
{ {
json.number(value as f64); json.number(*value as f64);
json.string(unit); json.string(unit);
} }
json.end_array(); json.end_array();

View file

@ -186,7 +186,8 @@ impl Env {
fn pushd(&mut self, dir: PathBuf) { fn pushd(&mut self, dir: PathBuf) {
let dir = self.cwd().join(dir); let dir = self.cwd().join(dir);
self.pushd_stack.push(dir); self.pushd_stack.push(dir);
env::set_current_dir(self.cwd()).unwrap(); env::set_current_dir(self.cwd())
.unwrap_or_else(|err| panic!("Failed to set cwd to {}: {}", self.cwd().display(), err));
} }
fn popd(&mut self) { fn popd(&mut self) {
self.pushd_stack.pop().unwrap(); self.pushd_stack.pop().unwrap();