criterion_cycles_per_byte/
lib.rs1use criterion::{
29 measurement::{Measurement, ValueFormatter},
30 Throughput,
31};
32
33pub struct CyclesPerByte;
36
37#[inline(always)]
39fn cycle_counter() -> u64 {
40 #[cfg(target_arch = "x86")]
41 use core::arch::x86::*;
42 #[cfg(target_arch = "x86_64")]
43 use core::arch::x86_64::*;
44
45 unsafe {
46 cfg_if::cfg_if! {
47 if #[cfg(all(rdpru, any(target_arch = "x86_64", target_arch = "x86")))] {
48 let [hi, lo]: [u32; 2];
50 _mm_lfence();
51 core::arch::asm!(
52 "rdpru",
53 out("edx") hi,
54 out("eax") lo,
55 in("ecx") 1u32,
56 options(nostack, nomem, preserves_flags),
57 );
58 let ret = (u64::from(hi) << 32) | u64::from(lo);
59 _mm_lfence();
60 ret
61 } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
62 _mm_lfence();
66 let ret = _rdtsc();
67 _mm_lfence();
68 ret
69 } else if #[cfg(all(target_arch = "aarch64", target_os = "linux"))] {
70 let counter: u64;
79 core::arch::asm!("dsb sy", "mrs {}, pmccntr_el0", out(reg) counter);
80 counter
81 } else if #[cfg(target_arch = "loongarch64")] {
82 let counter: u64;
83 core::arch::asm!("rdtime.d {0}, $zero", out(reg) counter);
84 counter
85 } else {
86 compile_error!(
87 "criterion-cycles-per-byte currently works only on x86 or x86_64 or aarch64 or loongarch64."
88 );
89 }
90 }
91 }
92}
93
94impl Measurement for CyclesPerByte {
95 type Intermediate = u64;
96 type Value = u64;
97
98 #[inline]
99 fn start(&self) -> Self::Intermediate {
100 cycle_counter()
101 }
102
103 #[inline]
104 fn end(&self, i: Self::Intermediate) -> Self::Value {
105 cycle_counter().saturating_sub(i)
106 }
107
108 #[inline]
109 fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value {
110 v1 + v2
111 }
112
113 #[inline]
114 fn zero(&self) -> Self::Value {
115 0
116 }
117
118 #[inline]
119 fn to_f64(&self, value: &Self::Value) -> f64 {
120 *value as f64
121 }
122
123 fn formatter(&self) -> &dyn ValueFormatter {
124 &CyclesPerByteFormatter
125 }
126}
127
128struct CyclesPerByteFormatter;
129
130impl ValueFormatter for CyclesPerByteFormatter {
131 fn format_value(&self, value: f64) -> String {
132 format!("{:.4} cycles", value)
133 }
134
135 fn format_throughput(&self, throughput: &Throughput, value: f64) -> String {
136 match throughput {
137 Throughput::Bits(b) => format!("{:.4} cycles per bit", value / *b as f64),
138 Throughput::Bytes(b) => format!("{:.4} cpb", value / *b as f64),
139 Throughput::Elements(b) => format!("{:.4} cycles/{}", value, b),
140 Throughput::ElementsAndBytes { elements, bytes: _ } => {
141 format!("{:.4} cycles/{}", value, elements)
142 }
143 Throughput::BytesDecimal(b) => format!("{:.4} cpb (decimal)", value / *b as f64),
144 }
145 }
146
147 fn scale_values(&self, _typical_value: f64, _values: &mut [f64]) -> &'static str {
148 "cycles"
149 }
150
151 fn scale_throughputs(
152 &self,
153 _typical_value: f64,
154 throughput: &Throughput,
155 values: &mut [f64],
156 ) -> &'static str {
157 match throughput {
158 Throughput::Bits(n) => {
159 for val in values {
160 *val /= *n as f64;
161 }
162 "cycles per bit"
163 }
164 Throughput::Bytes(n) => {
165 for val in values {
166 *val /= *n as f64;
167 }
168 "cpb"
169 }
170 Throughput::Elements(n) => {
171 for val in values {
172 *val /= *n as f64;
173 }
174 "c/e"
175 }
176 Throughput::ElementsAndBytes { elements: n, bytes: _ } => {
177 for val in values {
178 *val /= *n as f64;
179 }
180 "c/e"
181 }
182
183 Throughput::BytesDecimal(n) => {
184 for val in values {
185 *val /= *n as f64;
186 }
187 "cpb (decimal)"
188 }
189 }
190 }
191
192 fn scale_for_machines(&self, _values: &mut [f64]) -> &'static str {
193 "cycles"
194 }
195}