sandpit/
metrics.rs

1use core::sync::atomic::{AtomicU64, AtomicU8, Ordering};
2
3/// A 'snapshot' of the metrics relevant to the GC's internal triggers.
4///
5/// Obtained by calling [`crate::Arena::metrics`].
6#[derive(Debug)]
7pub struct Metrics {
8    /// Number of major collections that have occured.
9    pub major_collections: AtomicU64,
10
11    /// Number of minor collections that have occured.
12    pub minor_collections: AtomicU64,
13
14    /// Average time that takes for a major collection to complete.
15    pub major_collect_avg_time: AtomicU64,
16
17    /// Average time that takes for a minor collection to complete.
18    pub minor_collect_avg_time: AtomicU64,
19
20    /// Once the old objects count reaches this number, a major collection will
21    /// be triggered
22    pub max_old_objects: AtomicU64,
23
24    /// Running total of object that have been traced since the last major
25    /// collection and all succeeding minor collections.
26    pub old_objects_count: AtomicU64,
27
28    /// Total amount of memory allocated by the arena.
29    pub arena_size: AtomicU64,
30
31    /// The arena size at the start of the last collection.
32    pub prev_arena_size: AtomicU64,
33
34    /// The current state of the GC.
35    pub state: AtomicU8,
36
37    pub max_yield_time: AtomicU64,
38    pub avg_yield_time: AtomicU64,
39    pub max_arena_size: AtomicU64,
40    pub monitor_is_on: bool,
41}
42
43impl Metrics {
44    pub fn new() -> Self {
45        Self {
46            major_collections: AtomicU64::new(0),
47            minor_collections: AtomicU64::new(0),
48            major_collect_avg_time: AtomicU64::new(0),
49            minor_collect_avg_time: AtomicU64::new(0),
50            max_yield_time: AtomicU64::new(0),
51            avg_yield_time: AtomicU64::new(0),
52            max_old_objects: AtomicU64::new(0),
53            old_objects_count: AtomicU64::new(0),
54            arena_size: AtomicU64::new(0),
55            max_arena_size: AtomicU64::new(0),
56            prev_arena_size: AtomicU64::new(0),
57            state: AtomicU8::new(GC_STATE_SLEEPING),
58            monitor_is_on: true,
59        }
60    }
61
62    pub fn get_major_collections(&self) -> u64 {
63        self.major_collections.load(Ordering::Relaxed)
64    }
65
66    pub fn get_minor_collections(&self) -> u64 {
67        self.minor_collections.load(Ordering::Relaxed)
68    }
69
70    pub fn update_minor_collection_avg_time(&self, new_value: u64) {
71        update_avg_u64(
72            &self.minor_collect_avg_time,
73            new_value,
74            self.minor_collections.load(Ordering::Relaxed),
75        );
76    }
77
78    pub fn update_major_collection_avg_time(&self, new_value: u64) {
79        update_avg_u64(
80            &self.major_collect_avg_time,
81            new_value,
82            self.major_collections.load(Ordering::Relaxed),
83        );
84    }
85
86    pub fn get_major_collect_avg_time(&self) -> u64 {
87        self.major_collect_avg_time.load(Ordering::Relaxed)
88    }
89
90    pub fn get_minor_collect_avg_time(&self) -> u64 {
91        self.minor_collect_avg_time.load(Ordering::Relaxed)
92    }
93
94    pub fn get_max_old_objects(&self) -> u64 {
95        self.max_old_objects.load(Ordering::Relaxed)
96    }
97
98    pub fn get_old_objects_count(&self) -> u64 {
99        self.old_objects_count.load(Ordering::Relaxed)
100    }
101
102    pub fn get_arena_size(&self) -> u64 {
103        self.arena_size.load(Ordering::Relaxed)
104    }
105
106    pub fn get_prev_arena_size(&self) -> u64 {
107        self.prev_arena_size.load(Ordering::Relaxed)
108    }
109
110    pub fn get_state(&self) -> u8 {
111        self.state.load(Ordering::Relaxed)
112    }
113
114    pub fn get_max_yield_time(&self) -> u64 {
115        self.max_yield_time.load(Ordering::Relaxed)
116    }
117
118    pub fn get_avg_yield_time(&self) -> u64 {
119        self.avg_yield_time.load(Ordering::Relaxed)
120    }
121
122    pub fn get_max_arena_size(&self) -> u64 {
123        self.max_arena_size.load(Ordering::Relaxed)
124    }
125}
126
127pub fn update_avg_u64(running_avg: &AtomicU64, new_value: u64, sample_size: u64) {
128    let avg = running_avg.load(Ordering::Relaxed);
129    let update = new_value.abs_diff(avg) / (sample_size + 1);
130
131    // When new_value > avg, we add the update; when new_value < avg, we subtract.
132    // Use saturating arithmetic to prevent overflow/underflow.
133    let new_avg = if new_value > avg {
134        avg.saturating_add(update)
135    } else {
136        avg.saturating_sub(update)
137    };
138
139    running_avg.store(new_avg, Ordering::Relaxed);
140}
141
142pub const GC_STATE_SLEEPING: u8 = 0;
143pub const GC_STATE_TRACING: u8 = 1;
144pub const GC_STATE_SWEEPING: u8 = 2;
145#[cfg(feature = "multi_threaded")]
146pub const GC_STATE_WAITING_ON_MUTATORS: u8 = 3;