Diff nix stats

This commit is contained in:
Graham Christensen 2019-04-13 18:52:06 -04:00
parent 3a4bff2450
commit a114e86cbb
No known key found for this signature in database
GPG key ID: ACA1C1D120C83D5C
5 changed files with 410 additions and 8 deletions

26
ofborg/Cargo.lock generated
View file

@ -47,6 +47,14 @@ name = "antidote"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "arrayvec"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "0.1.1" version = "0.1.1"
@ -399,6 +407,11 @@ dependencies = [
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "nodrop"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "nom" name = "nom"
version = "4.1.1" version = "4.1.1"
@ -407,6 +420,15 @@ dependencies = [
"memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "num-format"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.39" version = "0.1.39"
@ -452,6 +474,7 @@ dependencies = [
"lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-format 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
@ -850,6 +873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum amq-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "66d79639b71f74c7006c12683cc2ff221615a51a741688fa7798ccd080dc54d3" "checksum amq-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "66d79639b71f74c7006c12683cc2ff221615a51a741688fa7798ccd080dc54d3"
"checksum amqp 0.1.0 (git+https://github.com/grahamc/rust-amqp.git)" = "<none>" "checksum amqp 0.1.0 (git+https://github.com/grahamc/rust-amqp.git)" = "<none>"
"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" "checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727"
"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" "checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5"
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
@ -896,7 +920,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
"checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" "checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a" "checksum nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a"
"checksum num-format 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"

View file

@ -27,6 +27,7 @@ lru-cache = "0.1.1"
nom = "4.0.0-beta3" nom = "4.0.0-beta3"
sys-info = "0.5.6" sys-info = "0.5.6"
chrono = "0.4.6" chrono = "0.4.6"
num-format = "0.4.0"
[patch.crates-io] [patch.crates-io]
#hubcaps = { path = "../hubcaps" } #hubcaps = { path = "../hubcaps" }

View file

@ -27,6 +27,7 @@ extern crate hyper;
extern crate hyper_native_tls; extern crate hyper_native_tls;
extern crate lru_cache; extern crate lru_cache;
extern crate md5; extern crate md5;
extern crate num_format;
extern crate tempfile; extern crate tempfile;
extern crate uuid; extern crate uuid;

View file

@ -1,4 +1,6 @@
use num_format::{Locale, ToFormattedString};
/// Statistics emitted by Nix when NIX_SHOW_STATS=1 /// Statistics emitted by Nix when NIX_SHOW_STATS=1
use std::collections::HashMap;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct EvaluationStats { pub struct EvaluationStats {
@ -94,14 +96,273 @@ pub struct GarbageCollector {
pub total_bytes: u64, pub total_bytes: u64,
} }
pub struct EvaluationStatsDiff<'a> {
left: &'a EvaluationStats,
right: &'a EvaluationStats,
}
impl<'a> EvaluationStatsDiff<'a> {
pub fn compare(
left: &'a EvaluationStats,
right: &'a EvaluationStats,
) -> EvaluationStatsDiff<'a> {
EvaluationStatsDiff { left, right }
}
pub fn markdown(&self) -> String {
struct Row {
before: String,
after: String,
diff: String,
diff_pct: String,
}
impl Row {
fn from_u64(left: u64, right: u64) -> Row {
let diff: u64;
let direction: &str;
let diff_pct: String;
if left > right {
diff = left - right;
direction = "🡖 ";
} else if left < right {
diff = right - left;
direction = "🡕 ";
} else {
diff = 0;
direction = "";
}
if diff > 0 {
diff_pct = format!(
"{:.2}%",
((right as f64) - (left as f64)) / (left as f64) * 100.0
);
} else {
diff_pct = String::from("");
}
Row {
before: left.to_formatted_string(&Locale::en),
after: right.to_formatted_string(&Locale::en),
diff: format!("{}{}", direction, diff.to_formatted_string(&Locale::en)),
diff_pct: diff_pct,
}
}
fn from_f32(left: f32, right: f32) -> Row {
let diff: f32;
let direction: &str;
let diff_pct: String;
if left > right {
diff = left - right;
direction = "🡖 ";
} else if left < right {
diff = right - left;
direction = "🡕 ";
} else {
diff = 0.0;
direction = "";
}
if diff > 0.0 {
diff_pct = format!(
"{:.2}%",
((right as f64) - (left as f64)) / (left as f64) * 100.0
);
} else {
diff_pct = String::from("");
}
Row {
before: format!("{:.2}", left),
after: format!("{:.2}", right),
diff: format!("{}{:.2}", direction, diff),
diff_pct: diff_pct,
}
}
}
let mut data: HashMap<&str, Row> = HashMap::new();
data.insert(
"cpuTime",
Row::from_f32(self.left.cpu_time, self.right.cpu_time),
);
data.insert(
"envs-number",
Row::from_u64(self.left.envs.number, self.right.envs.number),
);
data.insert(
"envs-elements",
Row::from_u64(self.left.envs.elements, self.right.envs.elements),
);
data.insert(
"envs-bytes",
Row::from_u64(self.left.envs.bytes, self.right.envs.bytes),
);
data.insert(
"list-elements",
Row::from_u64(self.left.list.elements, self.right.list.elements),
);
data.insert(
"list-bytes",
Row::from_u64(self.left.list.bytes, self.right.list.bytes),
);
data.insert(
"list-concats",
Row::from_u64(self.left.list.concats, self.right.list.concats),
);
data.insert(
"values-number",
Row::from_u64(self.left.values.number, self.right.values.number),
);
data.insert(
"values-bytes",
Row::from_u64(self.left.values.bytes, self.right.values.bytes),
);
data.insert(
"symbols-number",
Row::from_u64(self.left.symbols.number, self.right.symbols.number),
);
data.insert(
"symbols-bytes",
Row::from_u64(self.left.symbols.bytes, self.right.symbols.bytes),
);
data.insert(
"sets-number",
Row::from_u64(self.left.sets.number, self.right.sets.number),
);
data.insert(
"sets-bytes",
Row::from_u64(self.left.sets.bytes, self.right.sets.bytes),
);
data.insert(
"sets-elements",
Row::from_u64(self.left.sets.elements, self.right.sets.elements),
);
data.insert(
"sizes-Env",
Row::from_u64(self.left.sizes.env, self.right.sizes.env),
);
data.insert(
"sizes-Value",
Row::from_u64(self.left.sizes.value, self.right.sizes.value),
);
data.insert(
"sizes-Bindings",
Row::from_u64(self.left.sizes.bindings, self.right.sizes.bindings),
);
data.insert(
"sizes-Attr",
Row::from_u64(self.left.sizes.attr, self.right.sizes.attr),
);
data.insert(
"nrOpUpdates",
Row::from_u64(self.left.nr_op_updates, self.right.nr_op_updates),
);
data.insert(
"nrOpUpdateValuesCopied",
Row::from_u64(
self.left.nr_op_update_values_copied,
self.right.nr_op_update_values_copied,
),
);
data.insert(
"nrThunks",
Row::from_u64(self.left.nr_thunks, self.right.nr_thunks),
);
data.insert(
"nrAvoided",
Row::from_u64(self.left.nr_avoided, self.right.nr_avoided),
);
data.insert(
"nrLookups",
Row::from_u64(self.left.nr_lookups, self.right.nr_lookups),
);
data.insert(
"nrPrimOpCalls",
Row::from_u64(self.left.nr_prim_op_calls, self.right.nr_prim_op_calls),
);
data.insert(
"nrFunctionCalls",
Row::from_u64(self.left.nr_function_calls, self.right.nr_function_calls),
);
data.insert(
"gc-heapSize",
Row::from_u64(self.left.gc.heap_size, self.right.gc.heap_size),
);
data.insert(
"gc-totalBytes",
Row::from_u64(self.left.gc.total_bytes, self.right.gc.total_bytes),
);
let (keylen, beforelen, afterlen, difflen, diff_pctlen): (
usize,
usize,
usize,
usize,
usize,
) = data.iter().fold(
(0, 0, 0, 0, 0),
|(keylen, before, after, diff, diff_pct), (key, row)| {
(
std::cmp::max(keylen, key.chars().count()),
std::cmp::max(before, row.before.chars().count()),
std::cmp::max(after, row.after.chars().count()),
std::cmp::max(diff, row.diff.chars().count()),
std::cmp::max(diff_pct, row.diff_pct.chars().count()),
)
},
);
let mut keys = data.keys().cloned().collect::<Vec<&str>>();
keys.sort();
let rows = keys
.into_iter()
.map(|key| {
let row = data.get(&key).unwrap();
format!("| {key:<keywidth$} | {before:>beforewidth$} | {after:>afterwidth$} | {diff:<diffwidth$} | {diff_pct:>diff_pctwidth$} |",
key=format!("**{}**", key), keywidth=(keylen + 4),
before=row.before, beforewidth=beforelen,
after=row.after, afterwidth=afterlen,
diff=row.diff, diffwidth=difflen,
diff_pct=row.diff_pct, diff_pctwidth=diff_pctlen)
})
.collect::<Vec<String>>();
let header = format!(
"
|{key:^keywidth$}|{before:^beforewidth$}|{after:^afterwidth$}|{diff:^diffwidth$}|{diff_pct:^diff_pctwidth$}|
|{keydash:-<keywidth$}|{beforedash:->beforewidth$}|{afterdash:->afterwidth$}|{diffdash:-<diffwidth$}|{diff_pctdash:->diff_pctwidth$}|
",
key="stat", keywidth=(keylen + 6),
before="before", beforewidth=(beforelen + 2),
after="after", afterwidth=(afterlen + 2),
diff="Δ", diffwidth=(difflen + 2),
diff_pct="Δ%", diff_pctwidth=(diff_pctlen + 2),
keydash=":", beforedash=":", afterdash=":", diffdash=":", diff_pctdash=":"
);
format!("{}\n{}", header.trim(), rows.join("\n"))
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::EvaluationStats; use super::EvaluationStats;
use super::EvaluationStatsDiff;
use serde_json; use serde_json;
#[test]
fn verify_load() { const EXAMPLE: &'static str = r#"
let load: EvaluationStats = serde_json::from_str(
r#"
{ {
"cpuTime": 135.2, "cpuTime": 135.2,
"envs": { "envs": {
@ -145,9 +406,57 @@ mod tests {
"totalBytes": 24191819392 "totalBytes": 24191819392
} }
} }
"#, "#;
)
.unwrap(); const EXAMPLE2: &'static str = r#"
{
"cpuTime": 132.897,
"envs": {
"number": 124766593,
"elements": 177627124,
"bytes": 3417282480
},
"list": {
"elements": 204449868,
"bytes": 1635598944,
"concats": 6988658
},
"values": {
"number": 244542804,
"bytes": 5869027296
},
"symbols": {
"number": 372917,
"bytes": 16324250
},
"sets": {
"number": 27307373,
"bytes": 7133945368,
"elements": 288145266
},
"sizes": {
"Env": 16,
"Value": 24,
"Bindings": 8,
"Attr": 24
},
"nrOpUpdates": 11881928,
"nrOpUpdateValuesCopied": 208814478,
"nrThunks": 167655588,
"nrAvoided": 170493166,
"nrLookups": 75275349,
"nrPrimOpCalls": 80373629,
"nrFunctionCalls": 109822957,
"gc": {
"heapSize": 11433721856,
"totalBytes": 23468008832
}
}
"#;
#[test]
fn verify_load() {
let load: EvaluationStats = serde_json::from_str(EXAMPLE).unwrap();
assert_eq!(load.cpu_time, 135.2); assert_eq!(load.cpu_time, 135.2);
assert_eq!(load.envs.number, 130714125); assert_eq!(load.envs.number, 130714125);
@ -184,4 +493,57 @@ mod tests {
assert_eq!(load.gc.heap_size, 12104687616); assert_eq!(load.gc.heap_size, 12104687616);
assert_eq!(load.gc.total_bytes, 24191819392); assert_eq!(load.gc.total_bytes, 24191819392);
} }
fn diff_text(left: &str, right: &str) {
println!("left:\n{}", left);
println!("right:\n{}", right);
let lines = left.split("\n").zip(right.split("\n"));
for (idx, (linea, lineb)) in lines.enumerate() {
assert_eq!(linea, lineb, "Line {}", idx);
}
}
#[test]
fn markdown() {
let left: EvaluationStats = serde_json::from_str(EXAMPLE).unwrap();
let right: EvaluationStats = serde_json::from_str(EXAMPLE2).unwrap();
diff_text(
&EvaluationStatsDiff::compare(&left, &right).markdown(),
r#"
| stat | before | after | Δ | Δ% |
|:---------------------------|---------------:|---------------:|:--------------|-------:|
| **cpuTime** | 135.20 | 132.90 | 🡖 2.30 | -1.70% |
| **envs-bytes** | 3,563,057,008 | 3,417,282,480 | 🡖 145,774,528 | -4.09% |
| **envs-elements** | 183,953,876 | 177,627,124 | 🡖 6,326,752 | -3.44% |
| **envs-number** | 130,714,125 | 124,766,593 | 🡖 5,947,532 | -4.55% |
| **gc-heapSize** | 12,104,687,616 | 11,433,721,856 | 🡖 670,965,760 | -5.54% |
| **gc-totalBytes** | 24,191,819,392 | 23,468,008,832 | 🡖 723,810,560 | -2.99% |
| **list-bytes** | 1,659,372,128 | 1,635,598,944 | 🡖 23,773,184 | -1.43% |
| **list-concats** | 7,194,150 | 6,988,658 | 🡖 205,492 | -2.86% |
| **list-elements** | 207,421,516 | 204,449,868 | 🡖 2,971,648 | -1.43% |
| **nrAvoided** | 177,840,681 | 170,493,166 | 🡖 7,347,515 | -4.13% |
| **nrFunctionCalls** | 115,193,164 | 109,822,957 | 🡖 5,370,207 | -4.66% |
| **nrLookups** | 75,292,052 | 75,275,349 | 🡖 16,703 | -0.02% |
| **nrOpUpdateValuesCopied** | 208,834,564 | 208,814,478 | 🡖 20,086 | -0.01% |
| **nrOpUpdates** | 11,883,339 | 11,881,928 | 🡖 1,411 | -0.01% |
| **nrPrimOpCalls** | 85,571,252 | 80,373,629 | 🡖 5,197,623 | -6.07% |
| **nrThunks** | 173,325,665 | 167,655,588 | 🡖 5,670,077 | -3.27% |
| **sets-bytes** | 7,134,676,648 | 7,133,945,368 | 🡖 731,280 | -0.01% |
| **sets-elements** | 288,174,680 | 288,145,266 | 🡖 29,414 | -0.01% |
| **sets-number** | 27,310,541 | 27,307,373 | 🡖 3,168 | -0.01% |
| **sizes-Attr** | 24 | 24 | 0 | |
| **sizes-Bindings** | 8 | 8 | 0 | |
| **sizes-Env** | 16 | 16 | 0 | |
| **sizes-Value** | 24 | 24 | 0 | |
| **symbols-bytes** | 16,324,262 | 16,324,250 | 🡖 12 | -0.00% |
| **symbols-number** | 372,918 | 372,917 | 🡖 1 | -0.00% |
| **values-bytes** | 6,250,904,880 | 5,869,027,296 | 🡖 381,877,584 | -6.11% |
| **values-number** | 260,454,370 | 244,542,804 | 🡖 15,911,566 | -6.11% |
"#
.trim_start(),
);
}
} }

View file

@ -1,6 +1,6 @@
use crate::nixenv::Error as NixEnvError; use crate::nixenv::Error as NixEnvError;
use crate::nixenv::HydraNixEnv; use crate::nixenv::HydraNixEnv;
use crate::nixstats::EvaluationStats; use crate::nixstats::{EvaluationStats, EvaluationStatsDiff};
use ofborg::nix; use ofborg::nix;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::io::BufRead; use std::io::BufRead;
@ -37,6 +37,18 @@ impl OutPathDiff {
Ok(()) Ok(())
} }
pub fn performance_diff(&self) -> Option<EvaluationStatsDiff> {
if let Some((_, ref cur)) = self.current {
if let Some((_, ref orig)) = self.original {
Some(EvaluationStatsDiff::compare(orig, cur))
} else {
None
}
} else {
None
}
}
pub fn package_diff(&self) -> Option<(Vec<PackageArch>, Vec<PackageArch>)> { pub fn package_diff(&self) -> Option<(Vec<PackageArch>, Vec<PackageArch>)> {
if let Some((ref cur, _)) = self.current { if let Some((ref cur, _)) = self.current {
if let Some((ref orig, _)) = self.original { if let Some((ref orig, _)) = self.original {