diff --git a/tokio/src/runtime/stats/stats.rs b/tokio/src/runtime/stats/stats.rs index 39a48aee6..1dc184438 100644 --- a/tokio/src/runtime/stats/stats.rs +++ b/tokio/src/runtime/stats/stats.rs @@ -1,6 +1,9 @@ //! This file contains the types necessary to collect various types of stats. use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed}; +use std::convert::TryFrom; +use std::time::{Duration, Instant}; + /// This type contains methods to retrieve stats from a Tokio runtime. #[derive(Debug)] pub struct RuntimeStats { @@ -14,6 +17,10 @@ pub struct WorkerStats { park_count: AtomicU64, steal_count: AtomicU64, poll_count: AtomicU64, + busy_duration_min: AtomicU64, + busy_duration_max: AtomicU64, + busy_duration_last: AtomicU64, + busy_duration_total: AtomicU64, } impl RuntimeStats { @@ -24,6 +31,10 @@ impl RuntimeStats { park_count: AtomicU64::new(0), steal_count: AtomicU64::new(0), poll_count: AtomicU64::new(0), + busy_duration_min: AtomicU64::new(0), + busy_duration_max: AtomicU64::new(0), + busy_duration_last: AtomicU64::new(0), + busy_duration_total: AtomicU64::new(0), }); } @@ -54,6 +65,26 @@ impl WorkerStats { pub fn poll_count(&self) -> u64 { self.poll_count.load(Relaxed) } + + /// Returns the duration for which the runtime was busy last time it was busy. + pub fn busy_duration_last(&self) -> Duration { + Duration::from_nanos(self.busy_duration_last.load(Relaxed)) + } + + /// Returns the total amount of time this worker has been busy for. + pub fn busy_duration_total(&self) -> Duration { + Duration::from_nanos(self.busy_duration_total.load(Relaxed)) + } + + /// Returns the smallest busy duration since the last 16 parks. + pub fn busy_duration_min(&self) -> Duration { + Duration::from_nanos(self.busy_duration_min.load(Relaxed)) + } + + /// Returns the largest busy duration since the last 16 parks. + pub fn busy_duration_max(&self) -> Duration { + Duration::from_nanos(self.busy_duration_max.load(Relaxed)) + } } pub(crate) struct WorkerStatsBatcher { @@ -61,6 +92,16 @@ pub(crate) struct WorkerStatsBatcher { park_count: u64, steal_count: u64, poll_count: u64, + /// The last 16 busy durations in nanoseconds. + /// + /// This array is set to contain the same value 16 times after the first + /// iteration. Since we only publish the min, max and last value in the + /// array, then this gives the correct result. + busy_duration: [u64; 16], + busy_duration_i: usize, + /// The total busy duration in nanoseconds. + busy_duration_total: u64, + last_resume_time: Instant, } impl WorkerStatsBatcher { @@ -70,6 +111,10 @@ impl WorkerStatsBatcher { park_count: 0, steal_count: 0, poll_count: 0, + busy_duration: [0; 16], + busy_duration_i: usize::MAX, + busy_duration_total: 0, + last_resume_time: Instant::now(), } } pub(crate) fn submit(&mut self, to: &RuntimeStats) { @@ -78,13 +123,46 @@ impl WorkerStatsBatcher { worker.park_count.store(self.park_count, Relaxed); worker.steal_count.store(self.steal_count, Relaxed); worker.poll_count.store(self.poll_count, Relaxed); + + let mut min = u64::MAX; + let mut max = 0; + let last = self.busy_duration[self.busy_duration_i % 16]; + for &val in &self.busy_duration { + if val <= min { + min = val; + } + if val >= max { + max = val; + } + } + worker.busy_duration_min.store(min, Relaxed); + worker.busy_duration_max.store(max, Relaxed); + worker.busy_duration_last.store(last, Relaxed); + worker + .busy_duration_total + .store(self.busy_duration_total, Relaxed); } pub(crate) fn about_to_park(&mut self) { self.park_count += 1; + + let busy_duration = self.last_resume_time.elapsed(); + let busy_duration = u64::try_from(busy_duration.as_nanos()).unwrap_or(u64::MAX); + self.busy_duration_total += busy_duration; + if self.busy_duration_i == usize::MAX { + // We are parking for the first time. Set array to contain current + // duration in every slot. + self.busy_duration_i = 0; + self.busy_duration = [busy_duration; 16]; + } else { + self.busy_duration_i = (self.busy_duration_i + 1) % 16; + self.busy_duration[self.busy_duration_i] = busy_duration; + } } - pub(crate) fn returned_from_park(&mut self) {} + pub(crate) fn returned_from_park(&mut self) { + self.last_resume_time = Instant::now(); + } #[cfg(feature = "rt-multi-thread")] pub(crate) fn incr_steal_count(&mut self, by: u16) {