foyer_storage/io/device/
statistics.rs1use std::{
16 sync::atomic::{AtomicIsize, AtomicUsize, Ordering},
17 time::Duration,
18};
19
20use fastant::{Atomic, Instant};
21
22use crate::Throttle;
23
24#[derive(Debug)]
25struct Metric {
26 value: AtomicUsize,
27 throttle: f64,
28 quota: AtomicIsize,
29 update: Atomic,
30}
31
32impl Metric {
33 fn new(throttle: f64) -> Self {
34 Self {
35 value: AtomicUsize::new(0),
36 throttle,
37 quota: AtomicIsize::new(0),
38 update: Atomic::new(Instant::now()),
39 }
40 }
41
42 fn load(&self) -> usize {
43 self.value.load(Ordering::Relaxed)
44 }
45
46 fn record(&self, value: usize) {
47 self.value.fetch_add(value, Ordering::Relaxed);
48 if self.throttle != 0.0 {
50 self.quota.fetch_sub(value as _, Ordering::Relaxed);
51 }
52 }
53
54 fn throttle(&self) -> Duration {
58 if self.throttle == 0.0 {
60 return Duration::ZERO;
61 }
62
63 let now = Instant::now();
64 let update = self.update.load(Ordering::Relaxed);
65
66 let dur = now.duration_since(update).as_secs_f64();
67 let fill = dur * self.throttle;
68
69 let quota = f64::min(self.throttle, self.quota.load(Ordering::Relaxed) as f64 + fill);
70
71 self.update.store(now, Ordering::Relaxed);
72 self.quota.store(quota as isize, Ordering::Relaxed);
73
74 if quota >= 0.0 {
75 Duration::ZERO
76 } else {
77 Duration::from_secs_f64(-quota / self.throttle)
78 }
79 }
80}
81
82#[derive(Debug)]
84pub struct Statistics {
85 throttle: Throttle,
86
87 disk_write_bytes: Metric,
88 disk_read_bytes: Metric,
89 disk_write_ios: Metric,
90 disk_read_ios: Metric,
91}
92
93impl Statistics {
94 pub fn new(throttle: Throttle) -> Self {
96 let disk_write_bytes = Metric::new(throttle.write_throughput.map(|v| v.get()).unwrap_or_default() as f64);
97 let disk_read_bytes = Metric::new(throttle.read_throughput.map(|v| v.get()).unwrap_or_default() as f64);
98 let disk_write_ios = Metric::new(throttle.write_iops.map(|v| v.get()).unwrap_or_default() as f64);
99 let disk_read_ios = Metric::new(throttle.read_iops.map(|v| v.get()).unwrap_or_default() as f64);
100 Self {
101 throttle,
102 disk_write_bytes,
103 disk_read_bytes,
104 disk_write_ios,
105 disk_read_ios,
106 }
107 }
108
109 pub fn disk_write_bytes(&self) -> usize {
111 self.disk_write_bytes.load()
112 }
113
114 pub fn disk_read_bytes(&self) -> usize {
116 self.disk_read_bytes.load()
117 }
118
119 pub fn disk_write_ios(&self) -> usize {
121 self.disk_write_ios.load()
122 }
123
124 pub fn disk_read_ios(&self) -> usize {
126 self.disk_read_ios.load()
127 }
128
129 pub fn record_disk_write(&self, bytes: usize) {
131 self.disk_write_bytes.record(bytes);
132 self.disk_write_ios.record(self.throttle.iops_counter.count(bytes));
133 }
134
135 pub fn record_disk_read(&self, bytes: usize) {
137 self.disk_read_bytes.record(bytes);
138 self.disk_read_ios.record(self.throttle.iops_counter.count(bytes));
139 }
140
141 pub fn read_throttle(&self) -> Duration {
145 std::cmp::max(self.disk_read_bytes.throttle(), self.disk_read_ios.throttle())
146 }
147
148 pub fn write_throttle(&self) -> Duration {
152 std::cmp::max(self.disk_write_bytes.throttle(), self.disk_write_ios.throttle())
153 }
154
155 pub fn is_read_throttled(&self) -> bool {
157 self.read_throttle() > Duration::ZERO
158 }
159
160 pub fn is_write_throttled(&self) -> bool {
162 self.write_throttle() > Duration::ZERO
163 }
164
165 pub fn throttle(&self) -> &Throttle {
167 &self.throttle
168 }
169}