Skip to main content

rustfs_erasure_codec/core/
metrics.rs

1#[cfg(all(feature = "std", feature = "benchmark-metrics"))]
2use std::sync::atomic::AtomicUsize;
3#[cfg(feature = "std")]
4use std::sync::atomic::Ordering;
5
6#[cfg(feature = "std")]
7#[derive(Debug, Default)]
8pub(crate) struct MetricCounter {
9    #[cfg(feature = "benchmark-metrics")]
10    value: AtomicUsize,
11}
12
13#[cfg(feature = "std")]
14impl MetricCounter {
15    #[inline]
16    fn load(&self, ordering: Ordering) -> usize {
17        #[cfg(feature = "benchmark-metrics")]
18        {
19            self.value.load(ordering)
20        }
21        #[cfg(not(feature = "benchmark-metrics"))]
22        {
23            let _ = ordering;
24            0
25        }
26    }
27
28    #[inline]
29    fn store(&self, value: usize, ordering: Ordering) {
30        #[cfg(feature = "benchmark-metrics")]
31        {
32            self.value.store(value, ordering);
33        }
34        #[cfg(not(feature = "benchmark-metrics"))]
35        {
36            let _ = (value, ordering);
37        }
38    }
39
40    #[inline]
41    pub(crate) fn fetch_add(&self, value: usize, ordering: Ordering) -> usize {
42        #[cfg(feature = "benchmark-metrics")]
43        {
44            self.value.fetch_add(value, ordering)
45        }
46        #[cfg(not(feature = "benchmark-metrics"))]
47        {
48            let _ = (value, ordering);
49            0
50        }
51    }
52}
53
54#[cfg(feature = "std")]
55#[derive(Debug, Default)]
56pub(crate) struct ReconstructionCacheMetrics {
57    pub(crate) requests: MetricCounter,
58    pub(crate) hits: MetricCounter,
59    pub(crate) misses: MetricCounter,
60    pub(crate) inserts: MetricCounter,
61    pub(crate) evictions: MetricCounter,
62}
63
64#[cfg(feature = "std")]
65#[derive(Debug, Default)]
66pub(crate) struct RuntimeProfileMetrics {
67    code_some_serial_calls: MetricCounter,
68    code_some_parallel_calls: MetricCounter,
69    code_some_total_bytes: MetricCounter,
70    code_some_total_chunks: MetricCounter,
71    code_some_small_output_chunk_parallel_calls: MetricCounter,
72    code_some_small_output_chunk_parallel_outputs: MetricCounter,
73    code_some_small_output_chunk_parallel_chunks: MetricCounter,
74    code_single_serial_calls: MetricCounter,
75    code_single_parallel_calls: MetricCounter,
76    code_single_total_bytes: MetricCounter,
77    code_single_total_chunks: MetricCounter,
78    parallel_policy_calls: MetricCounter,
79    parallel_policy_parallel: MetricCounter,
80    parallel_policy_serial: MetricCounter,
81    parallel_policy_total_jobs: MetricCounter,
82    parallel_policy_total_chunk_len: MetricCounter,
83    reconstruct_calls: MetricCounter,
84    reconstruct_data_only_calls: MetricCounter,
85    reconstruct_entry_parallel_calls: MetricCounter,
86    reconstruct_entry_serial_calls: MetricCounter,
87    reconstruct_opt_fallback_serial_calls: MetricCounter,
88    reconstruct_total_missing_data: MetricCounter,
89    reconstruct_total_missing_parity: MetricCounter,
90    reconstruct_all_present_fast_path: MetricCounter,
91    reconstruct_data_stage_calls: MetricCounter,
92    reconstruct_data_stage_bytes: MetricCounter,
93    reconstruct_parity_stage_calls: MetricCounter,
94    reconstruct_parity_stage_bytes: MetricCounter,
95    reconstruct_data_small_output_specialized_calls: MetricCounter,
96}
97
98#[cfg(feature = "std")]
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct ReconstructionCacheStats {
101    pub requests: usize,
102    pub hits: usize,
103    pub misses: usize,
104    pub inserts: usize,
105    pub evictions: usize,
106}
107
108#[cfg(feature = "std")]
109#[derive(Debug, Clone, Copy, PartialEq)]
110pub struct ReconstructionCacheAnalysis {
111    pub hit_rate: f64,
112    pub reuse_ratio: f64,
113    pub miss_cost_per_request: f64,
114}
115
116#[cfg(feature = "std")]
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub struct RuntimeProfileStats {
119    pub code_some_serial_calls: usize,
120    pub code_some_parallel_calls: usize,
121    pub code_some_total_bytes: usize,
122    pub code_some_total_chunks: usize,
123    pub code_some_small_output_chunk_parallel_calls: usize,
124    pub code_some_small_output_chunk_parallel_outputs: usize,
125    pub code_some_small_output_chunk_parallel_chunks: usize,
126    pub code_single_serial_calls: usize,
127    pub code_single_parallel_calls: usize,
128    pub code_single_total_bytes: usize,
129    pub code_single_total_chunks: usize,
130    pub parallel_policy_calls: usize,
131    pub parallel_policy_parallel: usize,
132    pub parallel_policy_serial: usize,
133    pub parallel_policy_total_jobs: usize,
134    pub parallel_policy_total_chunk_len: usize,
135    pub reconstruct_calls: usize,
136    pub reconstruct_data_only_calls: usize,
137    pub reconstruct_entry_parallel_calls: usize,
138    pub reconstruct_entry_serial_calls: usize,
139    pub reconstruct_opt_fallback_serial_calls: usize,
140    pub reconstruct_total_missing_data: usize,
141    pub reconstruct_total_missing_parity: usize,
142    pub reconstruct_all_present_fast_path: usize,
143    pub reconstruct_data_stage_calls: usize,
144    pub reconstruct_data_stage_bytes: usize,
145    pub reconstruct_parity_stage_calls: usize,
146    pub reconstruct_parity_stage_bytes: usize,
147    pub reconstruct_data_small_output_specialized_calls: usize,
148}
149
150#[cfg(feature = "std")]
151impl ReconstructionCacheMetrics {
152    pub(crate) fn snapshot(&self) -> ReconstructionCacheStats {
153        ReconstructionCacheStats {
154            requests: self.requests.load(Ordering::Relaxed),
155            hits: self.hits.load(Ordering::Relaxed),
156            misses: self.misses.load(Ordering::Relaxed),
157            inserts: self.inserts.load(Ordering::Relaxed),
158            evictions: self.evictions.load(Ordering::Relaxed),
159        }
160    }
161}
162
163#[cfg(feature = "std")]
164impl ReconstructionCacheStats {
165    /// Returns the cache hit rate (hits / requests).
166    #[inline]
167    pub fn hit_rate(&self) -> f64 {
168        if self.requests == 0 {
169            0.0
170        } else {
171            self.hits as f64 / self.requests as f64
172        }
173    }
174
175    /// Returns the cache reuse ratio (hits / inserts).
176    #[inline]
177    pub fn reuse_ratio(&self) -> f64 {
178        if self.inserts == 0 {
179            0.0
180        } else {
181            self.hits as f64 / self.inserts as f64
182        }
183    }
184
185    /// Returns the miss cost per request (misses / requests).
186    #[inline]
187    pub fn miss_cost_per_request(&self) -> f64 {
188        if self.requests == 0 {
189            0.0
190        } else {
191            self.misses as f64 / self.requests as f64
192        }
193    }
194
195    /// Returns a combined analysis of all cache metrics.
196    #[inline]
197    pub fn analysis(&self) -> ReconstructionCacheAnalysis {
198        ReconstructionCacheAnalysis {
199            hit_rate: self.hit_rate(),
200            reuse_ratio: self.reuse_ratio(),
201            miss_cost_per_request: self.miss_cost_per_request(),
202        }
203    }
204}
205
206#[cfg(feature = "std")]
207impl RuntimeProfileMetrics {
208    pub(crate) fn snapshot(&self) -> RuntimeProfileStats {
209        RuntimeProfileStats {
210            code_some_serial_calls: self.code_some_serial_calls.load(Ordering::Relaxed),
211            code_some_parallel_calls: self.code_some_parallel_calls.load(Ordering::Relaxed),
212            code_some_total_bytes: self.code_some_total_bytes.load(Ordering::Relaxed),
213            code_some_total_chunks: self.code_some_total_chunks.load(Ordering::Relaxed),
214            code_some_small_output_chunk_parallel_calls: self
215                .code_some_small_output_chunk_parallel_calls
216                .load(Ordering::Relaxed),
217            code_some_small_output_chunk_parallel_outputs: self
218                .code_some_small_output_chunk_parallel_outputs
219                .load(Ordering::Relaxed),
220            code_some_small_output_chunk_parallel_chunks: self
221                .code_some_small_output_chunk_parallel_chunks
222                .load(Ordering::Relaxed),
223            code_single_serial_calls: self.code_single_serial_calls.load(Ordering::Relaxed),
224            code_single_parallel_calls: self.code_single_parallel_calls.load(Ordering::Relaxed),
225            code_single_total_bytes: self.code_single_total_bytes.load(Ordering::Relaxed),
226            code_single_total_chunks: self.code_single_total_chunks.load(Ordering::Relaxed),
227            parallel_policy_calls: self.parallel_policy_calls.load(Ordering::Relaxed),
228            parallel_policy_parallel: self.parallel_policy_parallel.load(Ordering::Relaxed),
229            parallel_policy_serial: self.parallel_policy_serial.load(Ordering::Relaxed),
230            parallel_policy_total_jobs: self.parallel_policy_total_jobs.load(Ordering::Relaxed),
231            parallel_policy_total_chunk_len: self
232                .parallel_policy_total_chunk_len
233                .load(Ordering::Relaxed),
234            reconstruct_calls: self.reconstruct_calls.load(Ordering::Relaxed),
235            reconstruct_data_only_calls: self.reconstruct_data_only_calls.load(Ordering::Relaxed),
236            reconstruct_entry_parallel_calls: self
237                .reconstruct_entry_parallel_calls
238                .load(Ordering::Relaxed),
239            reconstruct_entry_serial_calls: self
240                .reconstruct_entry_serial_calls
241                .load(Ordering::Relaxed),
242            reconstruct_opt_fallback_serial_calls: self
243                .reconstruct_opt_fallback_serial_calls
244                .load(Ordering::Relaxed),
245            reconstruct_total_missing_data: self
246                .reconstruct_total_missing_data
247                .load(Ordering::Relaxed),
248            reconstruct_total_missing_parity: self
249                .reconstruct_total_missing_parity
250                .load(Ordering::Relaxed),
251            reconstruct_all_present_fast_path: self
252                .reconstruct_all_present_fast_path
253                .load(Ordering::Relaxed),
254            reconstruct_data_stage_calls: self.reconstruct_data_stage_calls.load(Ordering::Relaxed),
255            reconstruct_data_stage_bytes: self.reconstruct_data_stage_bytes.load(Ordering::Relaxed),
256            reconstruct_parity_stage_calls: self
257                .reconstruct_parity_stage_calls
258                .load(Ordering::Relaxed),
259            reconstruct_parity_stage_bytes: self
260                .reconstruct_parity_stage_bytes
261                .load(Ordering::Relaxed),
262            reconstruct_data_small_output_specialized_calls: self
263                .reconstruct_data_small_output_specialized_calls
264                .load(Ordering::Relaxed),
265        }
266    }
267
268    pub(crate) fn reset(&self) {
269        self.code_some_serial_calls.store(0, Ordering::Relaxed);
270        self.code_some_parallel_calls.store(0, Ordering::Relaxed);
271        self.code_some_total_bytes.store(0, Ordering::Relaxed);
272        self.code_some_total_chunks.store(0, Ordering::Relaxed);
273        self.code_some_small_output_chunk_parallel_calls
274            .store(0, Ordering::Relaxed);
275        self.code_some_small_output_chunk_parallel_outputs
276            .store(0, Ordering::Relaxed);
277        self.code_some_small_output_chunk_parallel_chunks
278            .store(0, Ordering::Relaxed);
279        self.code_single_serial_calls.store(0, Ordering::Relaxed);
280        self.code_single_parallel_calls.store(0, Ordering::Relaxed);
281        self.code_single_total_bytes.store(0, Ordering::Relaxed);
282        self.code_single_total_chunks.store(0, Ordering::Relaxed);
283        self.parallel_policy_calls.store(0, Ordering::Relaxed);
284        self.parallel_policy_parallel.store(0, Ordering::Relaxed);
285        self.parallel_policy_serial.store(0, Ordering::Relaxed);
286        self.parallel_policy_total_jobs.store(0, Ordering::Relaxed);
287        self.parallel_policy_total_chunk_len
288            .store(0, Ordering::Relaxed);
289        self.reconstruct_calls.store(0, Ordering::Relaxed);
290        self.reconstruct_data_only_calls.store(0, Ordering::Relaxed);
291        self.reconstruct_entry_parallel_calls
292            .store(0, Ordering::Relaxed);
293        self.reconstruct_entry_serial_calls
294            .store(0, Ordering::Relaxed);
295        self.reconstruct_opt_fallback_serial_calls
296            .store(0, Ordering::Relaxed);
297        self.reconstruct_total_missing_data
298            .store(0, Ordering::Relaxed);
299        self.reconstruct_total_missing_parity
300            .store(0, Ordering::Relaxed);
301        self.reconstruct_all_present_fast_path
302            .store(0, Ordering::Relaxed);
303        self.reconstruct_data_stage_calls
304            .store(0, Ordering::Relaxed);
305        self.reconstruct_data_stage_bytes
306            .store(0, Ordering::Relaxed);
307        self.reconstruct_parity_stage_calls
308            .store(0, Ordering::Relaxed);
309        self.reconstruct_parity_stage_bytes
310            .store(0, Ordering::Relaxed);
311        self.reconstruct_data_small_output_specialized_calls
312            .store(0, Ordering::Relaxed);
313    }
314
315    pub(crate) fn record_code_some(
316        &self,
317        parallel: bool,
318        shard_len: usize,
319        inputs: usize,
320        outputs: usize,
321        chunk_len: usize,
322    ) {
323        if parallel {
324            self.code_some_parallel_calls
325                .fetch_add(1, Ordering::Relaxed);
326        } else {
327            self.code_some_serial_calls.fetch_add(1, Ordering::Relaxed);
328        }
329        self.code_some_total_bytes.fetch_add(
330            shard_len.saturating_mul(inputs).saturating_mul(outputs),
331            Ordering::Relaxed,
332        );
333        let chunks = if chunk_len == 0 {
334            0
335        } else {
336            shard_len.div_ceil(chunk_len)
337        };
338        self.code_some_total_chunks
339            .fetch_add(chunks, Ordering::Relaxed);
340    }
341
342    pub(crate) fn record_code_some_small_output_chunk_parallel(
343        &self,
344        outputs: usize,
345        chunks: usize,
346    ) {
347        self.code_some_small_output_chunk_parallel_calls
348            .fetch_add(1, Ordering::Relaxed);
349        self.code_some_small_output_chunk_parallel_outputs
350            .fetch_add(outputs, Ordering::Relaxed);
351        self.code_some_small_output_chunk_parallel_chunks
352            .fetch_add(chunks, Ordering::Relaxed);
353    }
354
355    pub(crate) fn record_code_single(
356        &self,
357        parallel: bool,
358        shard_len: usize,
359        outputs: usize,
360        chunk_len: usize,
361    ) {
362        if parallel {
363            self.code_single_parallel_calls
364                .fetch_add(1, Ordering::Relaxed);
365        } else {
366            self.code_single_serial_calls
367                .fetch_add(1, Ordering::Relaxed);
368        }
369        self.code_single_total_bytes
370            .fetch_add(shard_len.saturating_mul(outputs), Ordering::Relaxed);
371        let chunks = if chunk_len == 0 {
372            0
373        } else {
374            shard_len.div_ceil(chunk_len)
375        };
376        self.code_single_total_chunks
377            .fetch_add(chunks, Ordering::Relaxed);
378    }
379
380    pub(crate) fn record_parallel_policy(&self, decision: crate::ParallelDecision) {
381        self.parallel_policy_calls.fetch_add(1, Ordering::Relaxed);
382        if decision.use_parallel {
383            self.parallel_policy_parallel
384                .fetch_add(1, Ordering::Relaxed);
385        } else {
386            self.parallel_policy_serial.fetch_add(1, Ordering::Relaxed);
387        }
388        self.parallel_policy_total_jobs
389            .fetch_add(decision.jobs, Ordering::Relaxed);
390        self.parallel_policy_total_chunk_len
391            .fetch_add(decision.chunk_len, Ordering::Relaxed);
392    }
393
394    pub(crate) fn record_reconstruct(
395        &self,
396        data_only: bool,
397        missing_data_count: usize,
398        missing_parity_count: usize,
399        all_present_fast_path: bool,
400    ) {
401        self.reconstruct_calls.fetch_add(1, Ordering::Relaxed);
402        if data_only {
403            self.reconstruct_data_only_calls
404                .fetch_add(1, Ordering::Relaxed);
405        }
406        if all_present_fast_path {
407            self.reconstruct_all_present_fast_path
408                .fetch_add(1, Ordering::Relaxed);
409        }
410        self.reconstruct_total_missing_data
411            .fetch_add(missing_data_count, Ordering::Relaxed);
412        self.reconstruct_total_missing_parity
413            .fetch_add(missing_parity_count, Ordering::Relaxed);
414    }
415
416    pub(crate) fn record_reconstruct_entry(&self, parallel: bool) {
417        if parallel {
418            self.reconstruct_entry_parallel_calls
419                .fetch_add(1, Ordering::Relaxed);
420        } else {
421            self.reconstruct_entry_serial_calls
422                .fetch_add(1, Ordering::Relaxed);
423        }
424    }
425
426    pub(crate) fn record_reconstruct_opt_fallback_serial(&self) {
427        self.reconstruct_opt_fallback_serial_calls
428            .fetch_add(1, Ordering::Relaxed);
429    }
430
431    pub(crate) fn record_reconstruct_data_stage(&self, shard_len: usize, output_count: usize) {
432        self.reconstruct_data_stage_calls
433            .fetch_add(1, Ordering::Relaxed);
434        self.reconstruct_data_stage_bytes
435            .fetch_add(shard_len.saturating_mul(output_count), Ordering::Relaxed);
436    }
437
438    pub(crate) fn record_reconstruct_parity_stage(&self, shard_len: usize, output_count: usize) {
439        self.reconstruct_parity_stage_calls
440            .fetch_add(1, Ordering::Relaxed);
441        self.reconstruct_parity_stage_bytes
442            .fetch_add(shard_len.saturating_mul(output_count), Ordering::Relaxed);
443    }
444
445    pub(crate) fn record_reconstruct_data_small_output_specialized(&self) {
446        self.reconstruct_data_small_output_specialized_calls
447            .fetch_add(1, Ordering::Relaxed);
448    }
449}