scx_p2dq/
stats.rs

1use std::io::Write;
2use std::sync::Arc;
3use std::sync::atomic::AtomicBool;
4use std::sync::atomic::Ordering;
5use std::time::Duration;
6
7use anyhow::Result;
8use scx_stats::prelude::*;
9use scx_stats_derive::Stats;
10use scx_stats_derive::stat_doc;
11use serde::Deserialize;
12use serde::Serialize;
13
14#[stat_doc]
15#[derive(Clone, Debug, Default, Serialize, Deserialize, Stats)]
16#[stat(top)]
17pub struct Metrics {
18    #[stat(desc = "Scheduler mode")]
19    pub sched_mode: u32,
20    #[stat(desc = "Number of times tasks have switched DSQs")]
21    pub dsq_change: u64,
22    #[stat(desc = "Number of times tasks have stayed on the same DSQ")]
23    pub same_dsq: u64,
24    #[stat(desc = "Number of times a task kept running")]
25    pub keep: u64,
26    #[stat(desc = "Number of times a select_cpu pick 2 load balancing occured")]
27    pub select_pick2: u64,
28    #[stat(desc = "Number of times a dispatch pick 2 load balancing occured")]
29    pub dispatch_pick2: u64,
30    #[stat(desc = "Number of times a task migrated LLCs")]
31    pub llc_migrations: u64,
32    #[stat(desc = "Number of times a task migrated NUMA nodes")]
33    pub node_migrations: u64,
34    #[stat(desc = "Number of times tasks have directly been dispatched to local per CPU DSQs")]
35    pub direct: u64,
36    #[stat(desc = "Number of times tasks have dispatched to an idle local per CPU DSQs")]
37    pub idle: u64,
38    #[stat(desc = "Number of times tasks have been woken to the previous CPU")]
39    pub wake_prev: u64,
40    #[stat(desc = "Number of times tasks have been woken to the previous llc")]
41    pub wake_llc: u64,
42    #[stat(desc = "Number of times tasks have been woken and migrated llc")]
43    pub wake_mig: u64,
44}
45
46impl Metrics {
47    fn format<W: Write>(&self, w: &mut W) -> Result<()> {
48        writeln!(
49            w,
50            "direct/idle {}/{}\n\tdsq same/migrate/keep {}/{}/{}\n\twake_prev/llc/mig {}/{}/{}\n\tpick2 select/dispatch {}/{}\n\tmigrations llc/node: {}/{}",
51            self.direct,
52            self.idle,
53            self.same_dsq,
54            self.dsq_change,
55            self.keep,
56            self.wake_prev,
57            self.wake_llc,
58            self.wake_mig,
59            self.select_pick2,
60            self.dispatch_pick2,
61            self.llc_migrations,
62            self.node_migrations,
63        )?;
64        Ok(())
65    }
66
67    fn delta(&self, rhs: &Self) -> Self {
68        Self {
69            direct: self.direct - rhs.direct,
70            idle: self.idle - rhs.idle,
71            dsq_change: self.dsq_change - rhs.dsq_change,
72            same_dsq: self.same_dsq - rhs.same_dsq,
73            keep: self.keep - rhs.keep,
74            select_pick2: self.select_pick2 - rhs.select_pick2,
75            dispatch_pick2: self.dispatch_pick2 - rhs.dispatch_pick2,
76            llc_migrations: self.llc_migrations - rhs.llc_migrations,
77            node_migrations: self.node_migrations - rhs.node_migrations,
78            wake_prev: self.wake_prev - rhs.wake_prev,
79            wake_llc: self.wake_llc - rhs.wake_llc,
80            wake_mig: self.wake_mig - rhs.wake_mig,
81            ..self.clone()
82        }
83    }
84}
85pub fn server_data() -> StatsServerData<(), Metrics> {
86    let open: Box<dyn StatsOpener<(), Metrics>> = Box::new(move |(req_ch, res_ch)| {
87        req_ch.send(())?;
88        let mut prev = res_ch.recv()?;
89
90        let read: Box<dyn StatsReader<(), Metrics>> = Box::new(move |_args, (req_ch, res_ch)| {
91            req_ch.send(())?;
92            let cur = res_ch.recv()?;
93            let delta = cur.delta(&prev);
94            prev = cur;
95            delta.to_json()
96        });
97
98        Ok(read)
99    });
100
101    StatsServerData::new()
102        .add_meta(Metrics::meta())
103        .add_ops("top", StatsOps { open, close: None })
104}
105
106pub fn monitor(intv: Duration, shutdown: Arc<AtomicBool>) -> Result<()> {
107    scx_utils::monitor_stats::<Metrics>(
108        &[],
109        intv,
110        || shutdown.load(Ordering::Relaxed),
111        |metrics| metrics.format(&mut std::io::stdout()),
112    )
113}