stats: add busy_duration stats (#4179)

This commit is contained in:
Alice Ryhl
2021-11-08 14:59:36 -05:00
committed by GitHub
parent 1f8105588c
commit f45320a9c0
+79 -1
View File
@@ -1,6 +1,9 @@
//! This file contains the types necessary to collect various types of stats. //! This file contains the types necessary to collect various types of stats.
use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed}; 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. /// This type contains methods to retrieve stats from a Tokio runtime.
#[derive(Debug)] #[derive(Debug)]
pub struct RuntimeStats { pub struct RuntimeStats {
@@ -14,6 +17,10 @@ pub struct WorkerStats {
park_count: AtomicU64, park_count: AtomicU64,
steal_count: AtomicU64, steal_count: AtomicU64,
poll_count: AtomicU64, poll_count: AtomicU64,
busy_duration_min: AtomicU64,
busy_duration_max: AtomicU64,
busy_duration_last: AtomicU64,
busy_duration_total: AtomicU64,
} }
impl RuntimeStats { impl RuntimeStats {
@@ -24,6 +31,10 @@ impl RuntimeStats {
park_count: AtomicU64::new(0), park_count: AtomicU64::new(0),
steal_count: AtomicU64::new(0), steal_count: AtomicU64::new(0),
poll_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 { pub fn poll_count(&self) -> u64 {
self.poll_count.load(Relaxed) 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 { pub(crate) struct WorkerStatsBatcher {
@@ -61,6 +92,16 @@ pub(crate) struct WorkerStatsBatcher {
park_count: u64, park_count: u64,
steal_count: u64, steal_count: u64,
poll_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 { impl WorkerStatsBatcher {
@@ -70,6 +111,10 @@ impl WorkerStatsBatcher {
park_count: 0, park_count: 0,
steal_count: 0, steal_count: 0,
poll_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) { pub(crate) fn submit(&mut self, to: &RuntimeStats) {
@@ -78,13 +123,46 @@ impl WorkerStatsBatcher {
worker.park_count.store(self.park_count, Relaxed); worker.park_count.store(self.park_count, Relaxed);
worker.steal_count.store(self.steal_count, Relaxed); worker.steal_count.store(self.steal_count, Relaxed);
worker.poll_count.store(self.poll_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) { pub(crate) fn about_to_park(&mut self) {
self.park_count += 1; 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")] #[cfg(feature = "rt-multi-thread")]
pub(crate) fn incr_steal_count(&mut self, by: u16) { pub(crate) fn incr_steal_count(&mut self, by: u16) {