Skip to content

Commit afe37f9

Browse files
committed
[summarize] Move summarize functionality to a sub command and add diff
1 parent 46b6b86 commit afe37f9

File tree

7 files changed

+378
-44
lines changed

7 files changed

+378
-44
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
### Added
55
- `measureme`: Added support for compiling the library under wasm/wasi ([GH-43])
66
- `mmview`: Added the `-t` flag to limit output to results on the specified thread id ([GH-49])
7+
- `summarize`: Added the `diff` sub command to compare two profiles ([GH-50])
8+
9+
### Changed
10+
- `summarize`: Moved summarization under the `summarize` sub command ([GH-50])
711

812
## [0.3.0] - 2019-05-14
913
### Added

summarize/Readme.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ You can now use the `summarize` tool we compiled in the previous section to view
4343
contents of these files:
4444

4545
```bash
46-
$ /path/to/measureme/target/release/summarize pid-{pid}
46+
$ /path/to/measureme/target/release/summarize summarize id-{pid}
4747
+------------------------+-----------+-----------------+------------+------------+--------------+-----------------------+
4848
| Item | Self time | % of total time | Item count | Cache hits | Blocked time | Incremental load time |
4949
+------------------------+-----------+-----------------+------------+------------+--------------+-----------------------+
@@ -98,7 +98,7 @@ as before: (with regex as example)
9898
$ git clone https://github.com/rust-lang/regex.git
9999
$ cd regex
100100
$ cargo +mytoolchain rustc -- -Z self-profile
101-
$ /path/to/measureme/target/release/summarize pid-{pid}
101+
$ /path/to/measureme/target/release/summarize summarize pid-{pid}
102102
+------------------------+-----------+-----------------+------------+------------+--------------+-----------------------+
103103
| Item | Self time | % of total time | Item count | Cache hits | Blocked time | Incremental load time |
104104
+------------------------+-----------+-----------------+------------+------------+--------------+-----------------------+
@@ -143,4 +143,31 @@ summarize the information for that event.
143143

144144
[query]: https://rust-lang.github.io/rustc-guide/query.html
145145

146-
The table is sorted by `Self time`.
146+
The table is sorted by `Self time` descending.
147+
148+
## The `diff` sub command
149+
150+
The `diff` sub command allows you to compare the performance of two different profiles by event.
151+
152+
The output is a table like that of the `summarize` sub command but it instead shows the differences in each metric.
153+
154+
```bash
155+
$ /path/to/measureme/target/release/summarize diff base-profile changed-profile
156+
+---------------------------+--------------+------------+------------+--------------+-----------------------+
157+
| Item | Self Time | Item count | Cache hits | Blocked time | Incremental load time |
158+
+---------------------------+--------------+------------+------------+--------------+-----------------------+
159+
| LLVM_module_passes | -66.626471ms | +0 | +0 | +0ns | +0ns |
160+
+---------------------------+--------------+------------+------------+--------------+-----------------------+
161+
| LLVM_emit_obj | -38.700719ms | +0 | +0 | +0ns | +0ns |
162+
+---------------------------+--------------+------------+------------+--------------+-----------------------+
163+
| LLVM_make_bitcode | +32.006706ms | +0 | +0 | +0ns | +0ns |
164+
+---------------------------+--------------+------------+------------+--------------+-----------------------+
165+
| mir_borrowck | -12.808322ms | +0 | +0 | +0ns | +0ns |
166+
+---------------------------+--------------+------------+------------+--------------+-----------------------+
167+
| typeck_tables_of | -10.325247ms | +0 | +0 | +0ns | +0ns |
168+
+---------------------------+--------------+------------+------------+--------------+-----------------------+
169+
(rows elided)
170+
Total cpu time: -155.177548ms
171+
```
172+
173+
The table is sorted by the absolute value of `Self time` descending.

summarize/src/analysis.rs

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,14 @@ use std::collections::HashMap;
33
use std::time::Duration;
44
use measureme::{ProfilingData, TimestampKind, Event};
55
use measureme::rustc::*;
6-
7-
use serde::{Serialize};
8-
9-
#[derive(Serialize)]
10-
pub struct QueryData {
11-
pub label: String,
12-
pub self_time: Duration,
13-
pub number_of_cache_misses: usize,
14-
pub number_of_cache_hits: usize,
15-
pub invocation_count: usize,
16-
pub blocked_time: Duration,
17-
pub incremental_load_time: Duration,
18-
}
19-
20-
impl QueryData {
21-
fn new(label: String) -> QueryData {
22-
QueryData {
23-
label,
24-
self_time: Duration::from_nanos(0),
25-
number_of_cache_misses: 0,
26-
number_of_cache_hits: 0,
27-
invocation_count: 0,
28-
blocked_time: Duration::from_nanos(0),
29-
incremental_load_time: Duration::from_nanos(0),
30-
}
31-
}
32-
}
33-
34-
#[derive(Serialize)]
35-
pub struct Results {
36-
pub query_data: Vec<QueryData>,
37-
pub total_time: Duration,
38-
}
6+
use crate::query_data::{QueryData, Results};
397

408
pub fn perform_analysis(data: ProfilingData) -> Results {
419
let mut query_data = HashMap::<String, QueryData>::new();
4210
let mut threads = HashMap::<_, Vec<Event>>::new();
4311
let mut total_time = Duration::from_nanos(0);
4412

45-
let mut record_event_data = |label: &Cow<'_, str>, f: &Fn(&mut QueryData)| {
13+
let mut record_event_data = |label: &Cow<'_, str>, f: &dyn Fn(&mut QueryData)| {
4614
if let Some(data) = query_data.get_mut(&label[..]) {
4715
f(data);
4816
} else {

summarize/src/diff.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use std::collections::{HashMap, HashSet};
2+
use std::time::Duration;
3+
use serde::{Deserialize, Serialize};
4+
use crate::query_data::{QueryData, QueryDataDiff, Results};
5+
use crate::signed_duration::SignedDuration;
6+
7+
#[derive(Serialize, Deserialize)]
8+
pub struct DiffResults {
9+
pub query_data: Vec<QueryDataDiff>,
10+
pub total_time: SignedDuration,
11+
}
12+
13+
fn build_query_lookup(query_data: &[QueryData]) -> HashMap<&str, usize> {
14+
let mut lookup = HashMap::with_capacity(query_data.len());
15+
for i in 0..query_data.len() {
16+
lookup.insert(&query_data[i].label[..], i);
17+
}
18+
19+
lookup
20+
}
21+
22+
pub fn calculate_diff(base: Results, change: Results) -> DiffResults {
23+
#[inline]
24+
fn sd(d: Duration) -> SignedDuration {
25+
d.into()
26+
}
27+
28+
let base_data = build_query_lookup(&base.query_data);
29+
let change_data = build_query_lookup(&change.query_data);
30+
31+
let mut all_labels = HashSet::with_capacity(base.query_data.len() + change.query_data.len());
32+
for query_data in base.query_data.iter().chain(&change.query_data) {
33+
all_labels.insert(&query_data.label[..]);
34+
}
35+
36+
let mut query_data: Vec<_> =
37+
all_labels
38+
.iter()
39+
.map(|l| {
40+
let b = base_data.get(l).map(|i| &base.query_data[*i]);
41+
let c = change_data.get(l).map(|i| &change.query_data[*i]);
42+
43+
match (b, c) {
44+
(Some(b), Some(c)) => c.clone() - b.clone(),
45+
(Some(b), None) => b.as_query_data_diff(),
46+
(None, Some(c)) => c.invert(),
47+
(None, None) => unreachable!(),
48+
}
49+
})
50+
.collect();
51+
52+
query_data.sort_by(|l, r| r.self_time.duration.cmp(&l.self_time.duration));
53+
54+
DiffResults {
55+
query_data,
56+
total_time: sd(change.total_time) - sd(base.total_time),
57+
}
58+
}

summarize/src/main.rs

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,35 @@ extern crate prettytable;
33

44
use std::error::Error;
55
use std::fs::File;
6-
use std::io::BufWriter;
6+
use std::io::{BufReader, BufWriter};
77
use std::path::PathBuf;
88
use measureme::ProfilingData;
99

1010
use prettytable::{Table};
11+
use serde::Serialize;
1112
use structopt::StructOpt;
1213

1314
mod analysis;
15+
mod diff;
16+
mod query_data;
17+
mod signed_duration;
18+
19+
use query_data::Results;
20+
21+
#[derive(StructOpt, Debug)]
22+
struct DiffOpt {
23+
base: PathBuf,
24+
change: PathBuf,
25+
26+
#[structopt(short = "e", long = "exclude")]
27+
exclude: Vec<String>,
28+
29+
#[structopt(long = "json")]
30+
json: bool,
31+
}
1432

1533
#[derive(StructOpt, Debug)]
16-
struct Opt {
34+
struct SummarizeOpt {
1735
file_prefix: PathBuf,
1836

1937
/// Writes the analysis to a json file next to <file_prefix> instead of stdout
@@ -25,17 +43,96 @@ struct Opt {
2543
percent_above: f64,
2644
}
2745

28-
fn main() -> Result<(), Box<dyn Error>> {
29-
let opt = Opt::from_args();
46+
#[derive(StructOpt, Debug)]
47+
enum Opt {
48+
#[structopt(name = "diff")]
49+
Diff(DiffOpt),
50+
51+
/// Processes trace files and produces a summary
52+
#[structopt(name = "summarize")]
53+
Summarize(SummarizeOpt),
54+
}
55+
56+
fn process_results(file: &PathBuf) -> Result<Results, Box<dyn Error>> {
57+
if file.ends_with("json") {
58+
let reader = BufReader::new(File::open(&file)?);
59+
60+
let results: Results = serde_json::from_reader(reader)?;
61+
Ok(results)
62+
} else {
63+
let data = ProfilingData::new(&file)?;
64+
65+
Ok(analysis::perform_analysis(data))
66+
}
67+
}
68+
69+
fn write_results_json(file: &PathBuf, results: impl Serialize) -> Result<(), Box<dyn Error>> {
70+
let file = BufWriter::new(File::create(file.with_extension("json"))?);
71+
serde_json::to_writer(file, &results)?;
72+
Ok(())
73+
}
74+
75+
fn diff(opt: DiffOpt) -> Result<(), Box<dyn Error>> {
76+
let base = process_results(&opt.base)?;
77+
let change = process_results(&opt.change)?;
78+
79+
let results = diff::calculate_diff(base, change);
80+
81+
if opt.json {
82+
write_results_json(&opt.change, results)?;
83+
return Ok(());
84+
}
85+
86+
let mut table = Table::new();
3087

88+
table.add_row(row!(
89+
"Item",
90+
"Self Time",
91+
"Item count",
92+
"Cache hits",
93+
"Blocked time",
94+
"Incremental load time"
95+
));
96+
97+
for query_data in results.query_data {
98+
let exclude = opt.exclude.iter().any(|e| query_data.label.contains(e));
99+
if exclude {
100+
continue;
101+
}
102+
103+
fn print_i64(i: i64) -> String {
104+
if i >= 0 {
105+
format!("+{}", i)
106+
} else {
107+
format!("{}", i)
108+
}
109+
}
110+
111+
table.add_row(row![
112+
query_data.label,
113+
format!("{:.2?}", query_data.self_time),
114+
print_i64(query_data.invocation_count),
115+
print_i64(query_data.number_of_cache_hits),
116+
format!("{:.2?}", query_data.blocked_time),
117+
format!("{:.2?}", query_data.incremental_load_time),
118+
]);
119+
}
120+
121+
table.printstd();
122+
123+
println!("Total cpu time: {:?}", results.total_time);
124+
125+
Ok(())
126+
}
127+
128+
fn summarize(opt: SummarizeOpt) -> Result<(), Box<dyn Error>> {
31129
let data = ProfilingData::new(&opt.file_prefix)?;
32130

33131
let mut results = analysis::perform_analysis(data);
34132

35133
//just output the results into a json file
36134
if opt.json {
37-
let file = BufWriter::new(File::create(opt.file_prefix.with_extension("json"))?);
38-
serde_json::to_writer(file, &results)?;
135+
write_results_json(&opt.file_prefix, &results)?;
39136
return Ok(());
40137
}
41138

@@ -68,7 +165,6 @@ fn main() -> Result<(), Box<dyn Error>> {
68165
let mut percent_total_time: f64 = 0.0;
69166

70167
for query_data in results.query_data {
71-
72168
let curr_percent = (query_data.self_time.as_nanos() as f64) / total_time * 100.0;
73169
if curr_percent < percent_above { break } //no need to run entire loop if filtering by % time
74170

@@ -95,3 +191,12 @@ fn main() -> Result<(), Box<dyn Error>> {
95191

96192
Ok(())
97193
}
194+
195+
fn main() -> Result<(), Box<dyn Error>> {
196+
let opt = Opt::from_args();
197+
198+
match opt {
199+
Opt::Summarize(opt) => summarize(opt),
200+
Opt::Diff(opt) => diff(opt),
201+
}
202+
}

0 commit comments

Comments
 (0)