rustfs_erasure_codec/galois_8/
profile.rs1#[cfg(feature = "std")]
2use core::sync::atomic::{AtomicUsize, Ordering};
3#[cfg(feature = "std")]
4use std::sync::OnceLock;
5
6#[cfg(feature = "std")]
7#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
8pub struct RustNeonProfileStats {
9 pub mul_calls: usize,
10 pub mul_xor_calls: usize,
11 pub total_bytes: usize,
12 pub vector_64b_chunks: usize,
13 pub vector_16b_chunks: usize,
14 pub tail_bytes: usize,
15 pub tail_calls: usize,
16 pub table_lookups: usize,
17}
18
19#[cfg(feature = "std")]
20impl RustNeonProfileStats {
21 pub fn saturating_sub(self, baseline: Self) -> Self {
22 Self {
23 mul_calls: self.mul_calls.saturating_sub(baseline.mul_calls),
24 mul_xor_calls: self.mul_xor_calls.saturating_sub(baseline.mul_xor_calls),
25 total_bytes: self.total_bytes.saturating_sub(baseline.total_bytes),
26 vector_64b_chunks: self
27 .vector_64b_chunks
28 .saturating_sub(baseline.vector_64b_chunks),
29 vector_16b_chunks: self
30 .vector_16b_chunks
31 .saturating_sub(baseline.vector_16b_chunks),
32 tail_bytes: self.tail_bytes.saturating_sub(baseline.tail_bytes),
33 tail_calls: self.tail_calls.saturating_sub(baseline.tail_calls),
34 table_lookups: self.table_lookups.saturating_sub(baseline.table_lookups),
35 }
36 }
37}
38
39#[cfg(feature = "std")]
40#[derive(Debug, Default)]
41pub(crate) struct RustNeonProfileMetrics {
42 mul_calls: AtomicUsize,
43 mul_xor_calls: AtomicUsize,
44 total_bytes: AtomicUsize,
45 vector_64b_chunks: AtomicUsize,
46 vector_16b_chunks: AtomicUsize,
47 tail_bytes: AtomicUsize,
48 tail_calls: AtomicUsize,
49 table_lookups: AtomicUsize,
50}
51
52#[cfg(feature = "std")]
53impl RustNeonProfileMetrics {
54 fn snapshot(&self) -> RustNeonProfileStats {
55 RustNeonProfileStats {
56 mul_calls: self.mul_calls.load(Ordering::Relaxed),
57 mul_xor_calls: self.mul_xor_calls.load(Ordering::Relaxed),
58 total_bytes: self.total_bytes.load(Ordering::Relaxed),
59 vector_64b_chunks: self.vector_64b_chunks.load(Ordering::Relaxed),
60 vector_16b_chunks: self.vector_16b_chunks.load(Ordering::Relaxed),
61 tail_bytes: self.tail_bytes.load(Ordering::Relaxed),
62 tail_calls: self.tail_calls.load(Ordering::Relaxed),
63 table_lookups: self.table_lookups.load(Ordering::Relaxed),
64 }
65 }
66
67 fn reset(&self) {
68 self.mul_calls.store(0, Ordering::Relaxed);
69 self.mul_xor_calls.store(0, Ordering::Relaxed);
70 self.total_bytes.store(0, Ordering::Relaxed);
71 self.vector_64b_chunks.store(0, Ordering::Relaxed);
72 self.vector_16b_chunks.store(0, Ordering::Relaxed);
73 self.tail_bytes.store(0, Ordering::Relaxed);
74 self.tail_calls.store(0, Ordering::Relaxed);
75 self.table_lookups.store(0, Ordering::Relaxed);
76 }
77
78 #[cfg(all(
79 feature = "simd-neon",
80 target_arch = "aarch64",
81 not(target_env = "msvc"),
82 not(any(target_os = "android", target_os = "ios"))
83 ))]
84 pub(crate) fn record_call(
85 &self,
86 is_xor: bool,
87 input_len: usize,
88 vector_64b_chunks: usize,
89 vector_16b_chunks: usize,
90 tail_bytes: usize,
91 ) {
92 if is_xor {
93 self.mul_xor_calls.fetch_add(1, Ordering::Relaxed);
94 } else {
95 self.mul_calls.fetch_add(1, Ordering::Relaxed);
96 }
97 self.total_bytes.fetch_add(input_len, Ordering::Relaxed);
98 self.vector_64b_chunks
99 .fetch_add(vector_64b_chunks, Ordering::Relaxed);
100 self.vector_16b_chunks
101 .fetch_add(vector_16b_chunks, Ordering::Relaxed);
102 if tail_bytes > 0 {
103 self.tail_calls.fetch_add(1, Ordering::Relaxed);
104 self.tail_bytes.fetch_add(tail_bytes, Ordering::Relaxed);
105 }
106 let lookups = vector_64b_chunks
107 .saturating_mul(8)
108 .saturating_add(vector_16b_chunks.saturating_mul(2));
109 self.table_lookups.fetch_add(lookups, Ordering::Relaxed);
110 }
111}
112
113#[cfg(feature = "std")]
114pub(crate) static RUST_NEON_PROFILE_METRICS: RustNeonProfileMetrics = RustNeonProfileMetrics {
115 mul_calls: AtomicUsize::new(0),
116 mul_xor_calls: AtomicUsize::new(0),
117 total_bytes: AtomicUsize::new(0),
118 vector_64b_chunks: AtomicUsize::new(0),
119 vector_16b_chunks: AtomicUsize::new(0),
120 tail_bytes: AtomicUsize::new(0),
121 tail_calls: AtomicUsize::new(0),
122 table_lookups: AtomicUsize::new(0),
123};
124
125#[cfg(feature = "std")]
126const RS_NEON_MUL_SLICE_XOR_UNROLL_ENV: &str = "RS_NEON_MUL_SLICE_XOR_UNROLL";
127#[cfg(feature = "std")]
128pub(crate) const RS_NEON_MUL_SLICE_XOR_SCHEDULE_ENV: &str = "RS_NEON_MUL_SLICE_XOR_SCHEDULE";
129
130#[cfg(feature = "std")]
131pub(crate) fn parse_rust_neon_xor_unroll(value: &str) -> Option<usize> {
132 match value {
133 "2" => Some(2),
134 "4" => Some(4),
135 _ => None,
136 }
137}
138
139#[cfg(feature = "std")]
140pub(crate) fn rust_neon_mul_slice_xor_unroll() -> usize {
141 static UNROLL: OnceLock<usize> = OnceLock::new();
142 *UNROLL.get_or_init(|| {
143 std::env::var(RS_NEON_MUL_SLICE_XOR_UNROLL_ENV)
144 .ok()
145 .as_deref()
146 .and_then(parse_rust_neon_xor_unroll)
147 .unwrap_or(4)
148 })
149}
150
151#[cfg(feature = "std")]
152pub(crate) fn rust_neon_mul_slice_xor_schedule_split() -> bool {
153 static SPLIT: OnceLock<bool> = OnceLock::new();
154 *SPLIT.get_or_init(|| {
155 std::env::var(RS_NEON_MUL_SLICE_XOR_SCHEDULE_ENV)
156 .ok()
157 .is_some_and(|value| value == "split")
158 })
159}
160
161#[cfg(feature = "std")]
162pub fn rust_neon_profile_stats() -> RustNeonProfileStats {
163 RUST_NEON_PROFILE_METRICS.snapshot()
164}
165
166#[cfg(feature = "std")]
167pub fn reset_rust_neon_profile_stats() {
168 RUST_NEON_PROFILE_METRICS.reset();
169}