Skip to content

Commit f2f69e8

Browse files
flxoluser
authored andcommitted
Fix average write duration and extend server stats (#52)
* Fix avg write duration and extend server stats The average duration that is taken by a miss or hit is interesting when testing different cache types. This patch adds the average time taken by a cache miss and hit to the server stats. This measurements can be entirely done in the server and do not need any changes to the cache implementations. Furthermore the calculation of the average duration for cache writes is fixed (wrong factor used, that produced micros instead of millis).
1 parent d8a28bf commit f2f69e8

File tree

2 files changed

+79
-36
lines changed

2 files changed

+79
-36
lines changed

src/compiler/compiler.rs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ use std::path::Path;
4848
use std::process::{self,Stdio};
4949
use std::str;
5050
use std::thread;
51-
use std::time::Duration;
51+
use std::time::{
52+
Duration,
53+
Instant,
54+
};
5255
use tempdir::TempDir;
5356

5457
/// Supported compilers.
@@ -171,12 +174,12 @@ pub enum CompileResult {
171174
/// An error made the compilation not possible.
172175
Error,
173176
/// Result was found in cache.
174-
CacheHit,
177+
CacheHit(Duration),
175178
/// Result was not found in cache.
176179
///
177180
/// The `CacheWriteFuture` will resolve when the result is finished
178181
/// being stored in the cache.
179-
CacheMiss(MissType, CacheWriteFuture),
182+
CacheMiss(MissType, Duration, CacheWriteFuture),
180183
/// Not in cache, but the compilation result was determined to be not cacheable.
181184
NotCacheable,
182185
/// Not in cache, but compilation failed.
@@ -189,8 +192,8 @@ impl fmt::Debug for CompileResult {
189192
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190193
match self {
191194
&CompileResult::Error => write!(f, "CompileResult::Error"),
192-
&CompileResult::CacheHit => write!(f, "CompileResult::CacheHit"),
193-
&CompileResult::CacheMiss(ref m, _) => write!(f, "CompileResult::CacheMiss({:?}, _)", m),
195+
&CompileResult::CacheHit(ref d) => write!(f, "CompileResult::CacheHit({:?})", d),
196+
&CompileResult::CacheMiss(ref m, ref d, _) => write!(f, "CompileResult::CacheMiss({:?}, {:?}, _)", d, m),
194197
&CompileResult::NotCacheable => write!(f, "CompileResult::NotCacheable"),
195198
&CompileResult::CompileFailed => write!(f, "CompileResult::CompileFailed"),
196199
}
@@ -202,8 +205,8 @@ impl PartialEq<CompileResult> for CompileResult {
202205
fn eq(&self, other: &CompileResult) -> bool {
203206
match (self, other) {
204207
(&CompileResult::Error, &CompileResult::Error) => true,
205-
(&CompileResult::CacheHit, &CompileResult::CacheHit) => true,
206-
(&CompileResult::CacheMiss(ref m, _), &CompileResult::CacheMiss(ref n, _)) => m == n,
208+
(&CompileResult::CacheHit(_), &CompileResult::CacheHit(_)) => true,
209+
(&CompileResult::CacheMiss(ref m, _, _), &CompileResult::CacheMiss(ref n, _, _)) => m == n,
207210
(&CompileResult::NotCacheable, &CompileResult::NotCacheable) => true,
208211
(&CompileResult::CompileFailed, &CompileResult::CompileFailed) => true,
209212
_ => false,
@@ -294,11 +297,13 @@ impl Compiler {
294297
.map(|(key, path)| (key, pwd.join(path)))
295298
.collect::<HashMap<_, _>>();
296299
// If `ForceRecache` is enabled, we won't check the cache.
300+
let start = Instant::now();
297301
let cache_status = if cache_control == CacheControl::ForceRecache {
298302
Cache::Recache
299303
} else {
300304
storage.get(&key)
301305
};
306+
let duration = start.elapsed();
302307
match cache_status {
303308
Cache::Hit(mut entry) => {
304309
debug!("[{}]: Cache hit!", out_file);
@@ -310,7 +315,7 @@ impl Compiler {
310315
let mut stderr = io::Cursor::new(vec!());
311316
entry.get_object("stdout", &mut stdout).unwrap_or(());
312317
entry.get_object("stderr", &mut stderr).unwrap_or(());
313-
Ok((CompileResult::CacheHit,
318+
Ok((CompileResult::CacheHit(duration),
314319
process::Output {
315320
status: exit_status(0),
316321
stdout: stdout.into_inner(),
@@ -330,7 +335,9 @@ impl Compiler {
330335
Cache::Hit(_) => MissType::Normal,
331336
};
332337
let process::Output { stdout, .. } = preprocessor_result;
338+
let start = Instant::now();
333339
let (cacheable, compiler_result) = try!(self.kind.compile(creator, self, stdout, parsed_args, cwd));
340+
let duration = start.elapsed();
334341
if compiler_result.status.success() {
335342
if cacheable == Cacheable::Yes {
336343
debug!("[{}]: Compiled, storing in cache", out_file);
@@ -363,7 +370,7 @@ impl Compiler {
363370
duration: duration,
364371
})
365372
}).boxed();
366-
Ok((CompileResult::CacheMiss(miss_type, future), compiler_result))
373+
Ok((CompileResult::CacheMiss(miss_type, duration, future), compiler_result))
367374
} else {
368375
// Not cacheable
369376
debug!("[{}]: Compiled but not cacheable", out_file);
@@ -507,6 +514,7 @@ mod test {
507514
use mock_command::*;
508515
use std::fs::{self,File};
509516
use std::io::Write;
517+
use std::time::Duration;
510518
use std::usize;
511519
use test::utils::*;
512520

@@ -614,7 +622,7 @@ mod test {
614622
// Ensure that the object file was created.
615623
assert_eq!(true, fs::metadata(&obj).and_then(|m| Ok(m.len() > 0)).unwrap());
616624
match cached {
617-
CompileResult::CacheMiss(MissType::Normal, f) => {
625+
CompileResult::CacheMiss(MissType::Normal, _, f) => {
618626
// wait on cache write future so we don't race with it!
619627
f.wait().unwrap().unwrap();
620628
}
@@ -631,7 +639,7 @@ mod test {
631639
let (cached, res) = c.get_cached_or_compile(creator.clone(), &storage, &arguments, &parsed_args, cwd, CacheControl::Default).unwrap();
632640
// Ensure that the object file was created.
633641
assert_eq!(true, fs::metadata(&obj).and_then(|m| Ok(m.len() > 0)).unwrap());
634-
assert_eq!(CompileResult::CacheHit, cached);
642+
assert_eq!(CompileResult::CacheHit(Duration::new(0, 0)), cached);
635643
assert_eq!(exit_status(0), res.status);
636644
assert_eq!(COMPILER_STDOUT, res.stdout.as_slice());
637645
assert_eq!(COMPILER_STDERR, res.stderr.as_slice());
@@ -676,7 +684,7 @@ mod test {
676684
// Ensure that the object file was created.
677685
assert_eq!(true, fs::metadata(&obj).and_then(|m| Ok(m.len() > 0)).unwrap());
678686
match cached {
679-
CompileResult::CacheMiss(MissType::Normal, f) => {
687+
CompileResult::CacheMiss(MissType::Normal, _, f) => {
680688
// wait on cache write future so we don't race with it!
681689
f.wait().unwrap().unwrap();
682690
}
@@ -694,7 +702,7 @@ mod test {
694702
let (cached, res) = c.get_cached_or_compile(creator.clone(), &storage, &arguments, &parsed_args, cwd, CacheControl::Default).unwrap();
695703
// Ensure that the object file was created.
696704
assert_eq!(true, fs::metadata(&obj).and_then(|m| Ok(m.len() > 0)).unwrap());
697-
assert_eq!(CompileResult::CacheHit, cached);
705+
assert_eq!(CompileResult::CacheHit(Duration::new(0, 0)), cached);
698706
assert_eq!(exit_status(0), res.status);
699707
assert_eq!(COMPILER_STDOUT, res.stdout.as_slice());
700708
assert_eq!(COMPILER_STDERR, res.stderr.as_slice());
@@ -743,7 +751,7 @@ mod test {
743751
// Ensure that the object file was created.
744752
assert_eq!(true, fs::metadata(&obj).and_then(|m| Ok(m.len() > 0)).unwrap());
745753
match cached {
746-
CompileResult::CacheMiss(MissType::Normal, f) => {
754+
CompileResult::CacheMiss(MissType::Normal, _, f) => {
747755
// wait on cache write future so we don't race with it!
748756
f.wait().unwrap().unwrap();
749757
}
@@ -758,7 +766,7 @@ mod test {
758766
// Ensure that the object file was created.
759767
assert_eq!(true, fs::metadata(&obj).and_then(|m| Ok(m.len() > 0)).unwrap());
760768
match cached {
761-
CompileResult::CacheMiss(MissType::ForcedRecache, f) => {
769+
CompileResult::CacheMiss(MissType::ForcedRecache, _, f) => {
762770
// wait on cache write future so we don't race with it!
763771
f.wait().unwrap().unwrap();
764772
}

src/server.rs

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ pub struct SccacheServer<C: CommandCreatorSync + 'static> {
119119
}
120120

121121
/// Statistics about the cache.
122-
#[derive(Default)]
123122
struct ServerStats {
124123
/// The count of client compile requests.
125124
pub compile_requests: u64,
@@ -146,15 +145,41 @@ struct ServerStats {
146145
/// The count of errors writing to cache.
147146
pub cache_write_errors: u64,
148147
/// The number of successful cache writes.
149-
pub cache_writes: u32,
150-
/// The total seconds spent writing cache entries.
151-
pub cache_write_duration_s: u64,
152-
/// The total nanoseconds spent writing cache entries.
153-
pub cache_write_duration_ns: u32,
148+
pub cache_writes: u64,
149+
/// The total time spent writing cache entries.
150+
pub cache_write_duration: Duration,
151+
/// The total time spent reading cache hits.
152+
pub cache_read_hit_duration: Duration,
153+
/// The total time spent reading cache misses.
154+
pub cache_read_miss_duration: Duration,
154155
/// The count of compilation failures.
155156
pub compile_fails: u64,
156157
}
157158

159+
impl Default for ServerStats {
160+
fn default() -> ServerStats {
161+
ServerStats {
162+
compile_requests: u64::default(),
163+
requests_unsupported_compiler: u64::default(),
164+
requests_not_compile: u64::default(),
165+
requests_not_cacheable: u64::default(),
166+
requests_executed: u64::default(),
167+
cache_errors: u64::default(),
168+
cache_hits: u64::default(),
169+
cache_misses: u64::default(),
170+
non_cacheable_compilations: u64::default(),
171+
forced_recaches: u64::default(),
172+
cache_read_errors: u64::default(),
173+
cache_write_errors: u64::default(),
174+
cache_writes: u64::default(),
175+
cache_write_duration: Duration::new(0, 0),
176+
cache_read_hit_duration: Duration::new(0, 0),
177+
cache_read_miss_duration: Duration::new(0, 0),
178+
compile_fails: u64::default(),
179+
}
180+
}
181+
}
182+
158183
impl ServerStats {
159184
fn to_cache_statistics(&self) -> Vec<CacheStatistic> {
160185
macro_rules! set_stat {
@@ -166,6 +191,20 @@ impl ServerStats {
166191
}};
167192
}
168193

194+
macro_rules! set_duration_stat {
195+
($vec:ident, $dur:expr, $num:expr, $name:expr) => {{
196+
let mut stat = CacheStatistic::new();
197+
stat.set_name(String::from($name));
198+
if $num > 0 {
199+
let duration = $dur / $num as u32;
200+
stat.set_str(format!("{}.{:03} s", duration.as_secs(), duration.subsec_nanos() / 1000_000));
201+
} else {
202+
stat.set_str("0.000 s".to_owned());
203+
}
204+
$vec.push(stat);
205+
}};
206+
}
207+
169208
let mut stats_vec = vec!();
170209
set_stat!(stats_vec, self.compile_requests, "Compile requests");
171210
set_stat!(stats_vec, self.requests_executed, "Compile requests executed");
@@ -180,16 +219,9 @@ impl ServerStats {
180219
set_stat!(stats_vec, self.requests_not_cacheable, "Non-cacheable calls");
181220
set_stat!(stats_vec, self.requests_not_compile, "Non-compilation calls");
182221
set_stat!(stats_vec, self.requests_unsupported_compiler, "Unsupported compiler calls");
183-
// Set this as a string so we can view subsecond values.
184-
let mut stat = CacheStatistic::new();
185-
stat.set_name(String::from("Average cache write"));
186-
if self.cache_writes > 0 {
187-
let avg_write_duration = Duration::new(self.cache_write_duration_s, self.cache_write_duration_ns) / self.cache_writes;
188-
stat.set_str(format!("{}.{:03}s", avg_write_duration.as_secs(), avg_write_duration.subsec_nanos() / 1000));
189-
} else {
190-
stat.set_str(String::from("0s"));
191-
}
192-
stats_vec.push(stat);
222+
set_duration_stat!(stats_vec, self.cache_write_duration, self.cache_writes, "Average cache write");
223+
set_duration_stat!(stats_vec, self.cache_read_miss_duration, self.cache_misses, "Average cache read miss");
224+
set_duration_stat!(stats_vec, self.cache_read_hit_duration, self.cache_hits, "Average cache read hit");
193225
stats_vec
194226
}
195227
}
@@ -450,8 +482,11 @@ impl<C : CommandCreatorSync + 'static> SccacheServer<C> {
450482
Some((compiled, out)) => {
451483
match compiled {
452484
CompileResult::Error => self.stats.cache_errors += 1,
453-
CompileResult::CacheHit => self.stats.cache_hits += 1,
454-
CompileResult::CacheMiss(miss_type, future) => {
485+
CompileResult::CacheHit(duration) => {
486+
self.stats.cache_hits += 1;
487+
self.stats.cache_read_hit_duration += duration;
488+
},
489+
CompileResult::CacheMiss(miss_type, duration, future) => {
455490
match miss_type {
456491
MissType::Normal => self.stats.cache_misses += 1,
457492
MissType::CacheReadError => self.stats.cache_read_errors += 1,
@@ -460,6 +495,7 @@ impl<C : CommandCreatorSync + 'static> SccacheServer<C> {
460495
self.stats.forced_recaches += 1;
461496
}
462497
}
498+
self.stats.cache_read_miss_duration += duration;
463499
self.await_cache_write(&mut event_loop, future)
464500
}
465501
CompileResult::NotCacheable => {
@@ -727,10 +763,9 @@ impl<C : CommandCreatorSync + 'static> Handler for SccacheServer<C> {
727763
}
728764
//TODO: save cache stats!
729765
Ok(info) => {
730-
debug!("[{}]: Cache write finished in {}.{:03}s", info.object_file, info.duration.as_secs(), info.duration.subsec_nanos() / 1000);
766+
debug!("[{}]: Cache write finished in {}.{:03}s", info.object_file, info.duration.as_secs(), info.duration.subsec_nanos() / 1000_000);
731767
self.stats.cache_writes += 1;
732-
self.stats.cache_write_duration_s += info.duration.as_secs();
733-
self.stats.cache_write_duration_ns += info.duration.subsec_nanos();
768+
self.stats.cache_write_duration += info.duration;
734769
}
735770
}
736771
}

0 commit comments

Comments
 (0)