tokio/runtime/metrics/
batch.rs1use crate::runtime::metrics::WorkerMetrics;
2
3cfg_unstable_metrics! {
4 use crate::runtime::metrics::HistogramBatch;
5}
6
7use crate::runtime::metrics::ScheduleLatencyContext;
8use std::sync::atomic::Ordering::Relaxed;
9use std::time::{Duration, Instant};
10
11pub(crate) struct MetricsBatch {
12 busy_duration_total: u64,
14
15 processing_scheduled_tasks_started_at: Option<Instant>,
17
18 park_count: u64,
20
21 park_unpark_count: u64,
23
24 #[cfg(tokio_unstable)]
25 noop_count: u64,
27
28 #[cfg(tokio_unstable)]
29 steal_count: u64,
31
32 #[cfg(tokio_unstable)]
33 steal_operations: u64,
35
36 #[cfg(tokio_unstable)]
37 poll_count: u64,
39
40 #[cfg(tokio_unstable)]
41 poll_count_on_last_park: u64,
44
45 #[cfg(tokio_unstable)]
46 local_schedule_count: u64,
48
49 #[cfg(tokio_unstable)]
50 overflow_count: u64,
53
54 #[cfg(tokio_unstable)]
55 poll_timer: Option<PollTimer>,
57
58 #[cfg(feature = "schedule-latency")]
59 schedule_latencies: Option<HistogramBatch>,
60}
61
62cfg_unstable_metrics! {
63 struct PollTimer {
64 poll_counts: HistogramBatch,
66
67 poll_started_at: Instant,
69 }
70}
71
72impl MetricsBatch {
73 pub(crate) fn new(worker_metrics: &WorkerMetrics) -> MetricsBatch {
74 let maybe_now = now();
75 Self::new_unstable(worker_metrics, maybe_now)
76 }
77
78 cfg_metrics_variant! {
79 stable: {
80 #[inline(always)]
81 fn new_unstable(_worker_metrics: &WorkerMetrics, maybe_now: Option<Instant>) -> MetricsBatch {
82 MetricsBatch {
83 busy_duration_total: 0,
84 processing_scheduled_tasks_started_at: maybe_now,
85 park_count: 0,
86 park_unpark_count: 0,
87 }
88 }
89 },
90 unstable: {
91 #[inline(always)]
92 fn new_unstable(worker_metrics: &WorkerMetrics, maybe_now: Option<Instant>) -> MetricsBatch {
93 let poll_timer = maybe_now.and_then(|now| {
94 worker_metrics
95 .poll_count_histogram
96 .as_ref()
97 .map(|worker_poll_counts| PollTimer {
98 poll_counts: HistogramBatch::from_histogram(worker_poll_counts),
99 poll_started_at: now,
100 })
101 });
102 #[cfg(feature = "schedule-latency")]
104 let schedule_latencies = maybe_now.and_then(|_| {
105 worker_metrics
106 .schedule_latency_histogram
107 .as_ref()
108 .map(HistogramBatch::from_histogram)
109 });
110 MetricsBatch {
111 park_count: 0,
112 park_unpark_count: 0,
113 noop_count: 0,
114 steal_count: 0,
115 steal_operations: 0,
116 poll_count: 0,
117 poll_count_on_last_park: 0,
118 local_schedule_count: 0,
119 overflow_count: 0,
120 busy_duration_total: 0,
121 processing_scheduled_tasks_started_at: maybe_now,
122 poll_timer,
123 #[cfg(feature = "schedule-latency")]
124 schedule_latencies,
125 }
126 }
127 }
128 }
129
130 pub(crate) fn submit(&mut self, worker: &WorkerMetrics, mean_poll_time: u64) {
131 worker
132 .busy_duration_total
133 .store(self.busy_duration_total, Relaxed);
134
135 self.submit_unstable(worker, mean_poll_time);
136 }
137
138 cfg_metrics_variant! {
139 stable: {
140 #[inline(always)]
141 fn submit_unstable(&mut self, worker: &WorkerMetrics, _mean_poll_time: u64) {
142 worker.park_count.store(self.park_count, Relaxed);
143 worker
144 .park_unpark_count
145 .store(self.park_unpark_count, Relaxed);
146 }
147 },
148 unstable: {
149 #[inline(always)]
150 fn submit_unstable(&mut self, worker: &WorkerMetrics, mean_poll_time: u64) {
151 worker.mean_poll_time.store(mean_poll_time, Relaxed);
152 worker.park_count.store(self.park_count, Relaxed);
153 worker
154 .park_unpark_count
155 .store(self.park_unpark_count, Relaxed);
156 worker.noop_count.store(self.noop_count, Relaxed);
157 worker.steal_count.store(self.steal_count, Relaxed);
158 worker
159 .steal_operations
160 .store(self.steal_operations, Relaxed);
161 worker.poll_count.store(self.poll_count, Relaxed);
162
163 worker
164 .local_schedule_count
165 .store(self.local_schedule_count, Relaxed);
166 worker.overflow_count.store(self.overflow_count, Relaxed);
167
168 if let Some(poll_timer) = &self.poll_timer {
169 let dst = worker.poll_count_histogram.as_ref().unwrap();
170 poll_timer.poll_counts.submit(dst);
171 }
172
173 #[cfg(feature = "schedule-latency")]
174 if let Some(schedule_latencies) = &self.schedule_latencies {
175 let dst = worker.schedule_latency_histogram.as_ref().unwrap();
176 schedule_latencies.submit(dst);
177 }
178 }
179 }
180 }
181
182 cfg_metrics_variant! {
183 stable: {
184 pub(crate) fn about_to_park(&mut self) {
186 self.park_count += 1;
187 self.park_unpark_count += 1;
188 }
189 },
190 unstable: {
191 pub(crate) fn about_to_park(&mut self) {
193 {
194 self.park_count += 1;
195 self.park_unpark_count += 1;
196
197 if self.poll_count_on_last_park == self.poll_count {
198 self.noop_count += 1;
199 } else {
200 self.poll_count_on_last_park = self.poll_count;
201 }
202 }
203 }
204 }
205 }
206 pub(crate) fn unparked(&mut self) {
208 self.park_unpark_count += 1;
209 }
210
211 pub(crate) fn start_processing_scheduled_tasks(&mut self) {
213 self.processing_scheduled_tasks_started_at = now();
214 }
215
216 pub(crate) fn end_processing_scheduled_tasks(&mut self) {
218 if let Some(processing_scheduled_tasks_started_at) =
219 self.processing_scheduled_tasks_started_at
220 {
221 let busy_duration = processing_scheduled_tasks_started_at.elapsed();
222 self.busy_duration_total += duration_as_u64(busy_duration);
223 }
224 }
225
226 cfg_metrics_variant! {
227 stable: {
228 pub(crate) fn start_poll(&mut self, _task_scheduled_at: Option<ScheduleLatencyContext>) {}
230 },
231 unstable: {
232 pub(crate) fn start_poll(&mut self, _task_scheduled_at: Option<ScheduleLatencyContext>) {
240 self.poll_count += 1;
241 if let Some(poll_timer) = &mut self.poll_timer {
242 poll_timer.poll_started_at = Instant::now();
243 }
244 #[cfg(feature = "schedule-latency")]
245 if let Some(task_scheduled_at) = _task_scheduled_at {
246 if let Some(schedule_latencies) = &mut self.schedule_latencies {
247 if let Some(now) = self.poll_timer.as_ref().map(|p| p.poll_started_at).or_else(now) {
248 let elapsed = task_scheduled_at.elapsed_nanos(now);
249 schedule_latencies.measure(elapsed, 1);
250 }
251 }
252 }
253 }
254 }
255 }
256
257 cfg_metrics_variant! {
258 stable: {
259 pub(crate) fn end_poll(&mut self) {}
261 },
262 unstable: {
263 pub(crate) fn end_poll(&mut self) {
265 if let Some(poll_timer) = &mut self.poll_timer {
266 let elapsed = duration_as_u64(poll_timer.poll_started_at.elapsed());
267 poll_timer.poll_counts.measure(elapsed, 1);
268 }
269 }
270 }
271 }
272
273 cfg_metrics_variant! {
274 stable: {
275 pub(crate) fn inc_local_schedule_count(&mut self) {}
276 },
277 unstable: {
278 pub(crate) fn inc_local_schedule_count(&mut self) {
279 self.local_schedule_count += 1;
280 }
281 }
282 }
283}
284
285cfg_rt_multi_thread! {
286 impl MetricsBatch {
287 cfg_metrics_variant! {
288 stable: {
289 pub(crate) fn incr_steal_count(&mut self, _by: u16) {}
290 },
291 unstable: {
292 pub(crate) fn incr_steal_count(&mut self, by: u16) {
293 self.steal_count += by as u64;
294 }
295 }
296 }
297
298 cfg_metrics_variant! {
299 stable: {
300 pub(crate) fn incr_steal_operations(&mut self) {}
301 },
302 unstable: {
303 pub(crate) fn incr_steal_operations(&mut self) {
304 self.steal_operations += 1;
305 }
306 }
307 }
308
309 cfg_metrics_variant! {
310 stable: {
311 pub(crate) fn incr_overflow_count(&mut self) {}
312 },
313 unstable: {
314 pub(crate) fn incr_overflow_count(&mut self) {
315 self.overflow_count += 1;
316 }
317 }
318 }
319 }
320}
321
322pub(crate) fn duration_as_u64(dur: Duration) -> u64 {
323 u64::try_from(dur.as_nanos()).unwrap_or(u64::MAX)
324}
325
326fn now() -> Option<Instant> {
329 if cfg!(all(
330 target_arch = "wasm32",
331 target_os = "unknown",
332 target_vendor = "unknown"
333 )) {
334 None
335 } else {
336 Some(Instant::now())
337 }
338}