Skip to content

Commit 7d451d9

Browse files
authored
Add cache hit percentage to stats (#2211)
* Add cache hit percentage to stats * Change macro to a function
1 parent 14e465a commit 7d451d9

File tree

5 files changed

+478
-155
lines changed

5 files changed

+478
-155
lines changed

src/server.rs

Lines changed: 200 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use futures::{future, stream, Sink, SinkExt, Stream, StreamExt, TryFutureExt};
3838
use number_prefix::NumberPrefix;
3939
use serde::{Deserialize, Serialize};
4040
use std::cell::Cell;
41-
use std::collections::HashMap;
41+
use std::collections::{HashMap, HashSet};
4242
use std::env;
4343
use std::ffi::{OsStr, OsString};
4444
use std::future::Future;
@@ -1510,11 +1510,23 @@ impl Default for ServerStats {
15101510
}
15111511
}
15121512

1513+
pub trait ServerStatsWriter {
1514+
fn write(&mut self, text: &str);
1515+
}
1516+
1517+
pub struct StdoutServerStatsWriter;
1518+
1519+
impl ServerStatsWriter for StdoutServerStatsWriter {
1520+
fn write(&mut self, text: &str) {
1521+
println!("{text}");
1522+
}
1523+
}
1524+
15131525
impl ServerStats {
1514-
/// Print stats to stdout in a human-readable format.
1526+
/// Print stats in a human-readable format.
15151527
///
15161528
/// Return the formatted width of each of the (name, value) columns.
1517-
fn print(&self, advanced: bool) -> (usize, usize) {
1529+
fn print<T: ServerStatsWriter>(&self, writer: &mut T, advanced: bool) -> (usize, usize) {
15181530
macro_rules! set_stat {
15191531
($vec:ident, $var:expr, $name:expr) => {{
15201532
// name, value, suffix length
@@ -1570,6 +1582,9 @@ impl ServerStats {
15701582
set_lang_stat!(stats_vec, self.cache_hits, "Cache hits");
15711583
set_lang_stat!(stats_vec, self.cache_misses, "Cache misses");
15721584
}
1585+
1586+
self.set_percentage_stats(&mut stats_vec, advanced);
1587+
15731588
set_stat!(stats_vec, self.cache_timeouts, "Cache timeouts");
15741589
set_stat!(stats_vec, self.cache_read_errors, "Cache read errors");
15751590
set_stat!(stats_vec, self.forced_recaches, "Forced recaches");
@@ -1627,45 +1642,110 @@ impl ServerStats {
16271642
let name_width = stats_vec.iter().map(|(n, _, _)| n.len()).max().unwrap();
16281643
let stat_width = stats_vec.iter().map(|(_, s, _)| s.len()).max().unwrap();
16291644
for (name, stat, suffix_len) in stats_vec {
1630-
println!(
1645+
writer.write(&format!(
16311646
"{:<name_width$} {:>stat_width$}",
16321647
name,
16331648
stat,
16341649
name_width = name_width,
16351650
stat_width = stat_width + suffix_len
1636-
);
1651+
));
16371652
}
16381653
if !self.dist_compiles.is_empty() {
1639-
println!("\nSuccessful distributed compiles");
1654+
writer.write("\nSuccessful distributed compiles");
16401655
let mut counts: Vec<_> = self.dist_compiles.iter().collect();
16411656
counts.sort_by(|(_, c1), (_, c2)| c1.cmp(c2).reverse());
16421657
for (reason, count) in counts {
1643-
println!(
1658+
writer.write(&format!(
16441659
" {:<name_width$} {:>stat_width$}",
16451660
reason,
16461661
count,
16471662
name_width = name_width - 2,
1648-
stat_width = stat_width
1649-
);
1663+
stat_width = stat_width,
1664+
));
16501665
}
16511666
}
16521667
if !self.not_cached.is_empty() {
1653-
println!("\nNon-cacheable reasons:");
1668+
writer.write("\nNon-cacheable reasons:");
16541669
let mut counts: Vec<_> = self.not_cached.iter().collect();
16551670
counts.sort_by(|(_, c1), (_, c2)| c1.cmp(c2).reverse());
16561671
for (reason, count) in counts {
1657-
println!(
1672+
writer.write(&format!(
16581673
"{:<name_width$} {:>stat_width$}",
16591674
reason,
16601675
count,
16611676
name_width = name_width,
1662-
stat_width = stat_width
1663-
);
1677+
stat_width = stat_width,
1678+
));
16641679
}
1665-
println!();
1680+
writer.write("");
16661681
}
16671682
(name_width, stat_width)
16681683
}
1684+
1685+
fn set_percentage_stats(&self, stats_vec: &mut Vec<(String, String, usize)>, advanced: bool) {
1686+
set_percentage_stat(
1687+
stats_vec,
1688+
self.cache_hits.all(),
1689+
self.cache_misses.all() + self.cache_hits.all(),
1690+
"Cache hits rate",
1691+
);
1692+
1693+
let (stats_hits, stats_misses): (Vec<_>, Vec<_>) = if advanced {
1694+
(
1695+
self.cache_hits.adv_counts.iter().collect(),
1696+
self.cache_misses.adv_counts.iter().collect(),
1697+
)
1698+
} else {
1699+
(
1700+
self.cache_hits.counts.iter().collect(),
1701+
self.cache_misses.counts.iter().collect(),
1702+
)
1703+
};
1704+
1705+
let mut all_languages: HashSet<&String> = HashSet::new();
1706+
for (lang, _) in &stats_hits {
1707+
all_languages.insert(lang);
1708+
}
1709+
for (lang, _) in &stats_misses {
1710+
all_languages.insert(lang);
1711+
}
1712+
1713+
let mut all_languages: Vec<&String> = all_languages.into_iter().collect();
1714+
all_languages.sort();
1715+
1716+
for lang in all_languages {
1717+
let count_hits = stats_hits
1718+
.iter()
1719+
.find(|&&(l, _)| l == lang)
1720+
.map_or(0, |&(_, &count)| count);
1721+
1722+
let count_misses = stats_misses
1723+
.iter()
1724+
.find(|&&(l, _)| l == lang)
1725+
.map_or(0, |&(_, &count)| count);
1726+
1727+
set_percentage_stat(
1728+
stats_vec,
1729+
count_hits,
1730+
count_hits + count_misses,
1731+
&format!("Cache hits rate ({})", lang),
1732+
);
1733+
}
1734+
}
1735+
}
1736+
1737+
fn set_percentage_stat(
1738+
vec: &mut Vec<(String, String, usize)>,
1739+
count_hits: u64,
1740+
total: u64,
1741+
name: &str,
1742+
) {
1743+
if total == 0 {
1744+
vec.push((name.to_string(), "-".to_string(), 0));
1745+
} else {
1746+
let ratio = count_hits as f64 / total as f64;
1747+
vec.push((name.to_string(), format!("{:.2} %", ratio * 100.0), 2));
1748+
}
16691749
}
16701750

16711751
impl ServerInfo {
@@ -1700,7 +1780,7 @@ impl ServerInfo {
17001780

17011781
/// Print info to stdout in a human-readable format.
17021782
pub fn print(&self, advanced: bool) {
1703-
let (name_width, stat_width) = self.stats.print(advanced);
1783+
let (name_width, stat_width) = self.stats.print(&mut StdoutServerStatsWriter, advanced);
17041784
println!(
17051785
"{:<name_width$} {}",
17061786
"Cache location",
@@ -1978,3 +2058,108 @@ fn waits_until_zero() {
19782058
drop(active2);
19792059
assert_eq!(wait.now_or_never(), Some(()));
19802060
}
2061+
2062+
#[cfg(test)]
2063+
mod tests {
2064+
use super::*;
2065+
2066+
struct StringWriter {
2067+
buffer: String,
2068+
}
2069+
2070+
impl StringWriter {
2071+
fn new() -> StringWriter {
2072+
StringWriter {
2073+
buffer: String::new(),
2074+
}
2075+
}
2076+
2077+
fn get_output(self) -> String {
2078+
self.buffer
2079+
}
2080+
}
2081+
2082+
impl ServerStatsWriter for StringWriter {
2083+
fn write(&mut self, text: &str) {
2084+
self.buffer.push_str(&format!("{}\n", text));
2085+
}
2086+
}
2087+
2088+
#[test]
2089+
fn test_print_cache_hits_rate_default_server_stats() {
2090+
let stats = ServerStats::default();
2091+
2092+
let mut writer = StringWriter::new();
2093+
stats.print(&mut writer, false);
2094+
2095+
let output = writer.get_output();
2096+
2097+
assert!(output.contains("Cache hits rate -"));
2098+
}
2099+
2100+
#[test]
2101+
fn test_print_cache_hits_rate_server_stats() {
2102+
let mut cache_hits_counts = HashMap::new();
2103+
cache_hits_counts.insert("Rust".to_string(), 100);
2104+
cache_hits_counts.insert("C/C++".to_string(), 200);
2105+
2106+
let mut cache_misses_counts = HashMap::new();
2107+
cache_misses_counts.insert("Rust".to_string(), 50);
2108+
cache_misses_counts.insert("Cuda".to_string(), 300);
2109+
2110+
let stats = ServerStats {
2111+
cache_hits: PerLanguageCount {
2112+
counts: cache_hits_counts,
2113+
..Default::default()
2114+
},
2115+
cache_misses: PerLanguageCount {
2116+
counts: cache_misses_counts,
2117+
..Default::default()
2118+
},
2119+
..Default::default()
2120+
};
2121+
2122+
let mut writer = StringWriter::new();
2123+
stats.print(&mut writer, false);
2124+
2125+
let output = writer.get_output();
2126+
2127+
assert!(output.contains("Cache hits rate 46.15 %"));
2128+
assert!(output.contains("Cache hits rate (C/C++) 100.00 %"));
2129+
assert!(output.contains("Cache hits rate (Cuda) 0.00 %"));
2130+
assert!(output.contains("Cache hits rate (Rust) 66.67 %"));
2131+
}
2132+
2133+
#[test]
2134+
fn test_print_cache_hits_rate_advanced_server_stats() {
2135+
let mut cache_hits_counts = HashMap::new();
2136+
cache_hits_counts.insert("rust".to_string(), 50);
2137+
cache_hits_counts.insert("c/c++ [clang]".to_string(), 30);
2138+
2139+
let mut cache_misses_counts = HashMap::new();
2140+
cache_misses_counts.insert("rust".to_string(), 100);
2141+
cache_misses_counts.insert("cuda".to_string(), 70);
2142+
2143+
let stats = ServerStats {
2144+
cache_hits: PerLanguageCount {
2145+
adv_counts: cache_hits_counts,
2146+
..Default::default()
2147+
},
2148+
cache_misses: PerLanguageCount {
2149+
adv_counts: cache_misses_counts,
2150+
..Default::default()
2151+
},
2152+
..Default::default()
2153+
};
2154+
2155+
let mut writer = StringWriter::new();
2156+
stats.print(&mut writer, true);
2157+
2158+
let output = writer.get_output();
2159+
2160+
assert!(output.contains("Cache hits rate -"));
2161+
assert!(output.contains("Cache hits rate (c/c++ [clang]) 100.00 %"));
2162+
assert!(output.contains("Cache hits rate (cuda) 0.00 %"));
2163+
assert!(output.contains("Cache hits rate (rust) 33.33 %"));
2164+
}
2165+
}

0 commit comments

Comments
 (0)