criterion_perf_events/
lib.rs1extern crate perfcnt;
37
38use criterion::{
39 measurement::{Measurement, ValueFormatter},
40 Throughput,
41};
42use std::cell::RefCell;
43
44use perfcnt::linux::PerfCounter;
45use perfcnt::linux::PerfCounterBuilderLinux;
46use perfcnt::AbstractPerfCounter;
47
48pub struct Perf {
51 counter: RefCell<PerfCounter>,
52}
53
54impl Perf {
55 pub fn new(mut builder: PerfCounterBuilderLinux) -> Perf {
65 Perf {
66 counter: RefCell::new(
67 builder
68 .for_pid(std::process::id() as i32)
69 .disable()
70 .finish()
71 .expect("Could not create counter"),
72 ),
73 }
74 }
75}
76
77impl Measurement for Perf {
78 type Intermediate = u64;
79 type Value = u64;
80
81 fn start(&self) -> Self::Intermediate {
82 self.counter
83 .borrow()
84 .start()
85 .expect("Could not read perf counter");
86 0
87 }
88
89 fn end(&self, _i: Self::Intermediate) -> Self::Value {
90 self.counter
91 .borrow()
92 .stop()
93 .expect("Could not stop perf counter");
94 let ret = self
95 .counter
96 .borrow_mut()
97 .read()
98 .expect("Could not read perf counter");
99 self.counter
100 .borrow_mut()
101 .reset()
102 .expect("Could not reset perf counter");
103 ret
104 }
105
106 fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value {
107 v1 + v2
108 }
109
110 fn zero(&self) -> Self::Value {
111 0
112 }
113
114 fn to_f64(&self, value: &Self::Value) -> f64 {
115 *value as f64
116 }
117
118 fn formatter(&self) -> &dyn ValueFormatter {
119 &PerfFormatter
120 }
121}
122
123struct PerfFormatter;
124
125impl ValueFormatter for PerfFormatter {
126 fn format_value(&self, value: f64) -> String {
127 format!("{value:.4} cycles")
128 }
129
130 fn format_throughput(&self, throughput: &Throughput, value: f64) -> String {
131 match throughput {
132 Throughput::Bytes(bytes) => format!("{:.4} events/byte", value / *bytes as f64),
133 Throughput::BytesDecimal(bytes) => {
134 let event_per_byte = value / *bytes as f64;
135
136 let (denominator, unit) = if *bytes < 1000 {
137 (1.0, "events/byte")
138 } else if *bytes < 1000 * 1000 {
139 (1000.0, "events/kilobyte")
140 } else if *bytes < 1000 * 1000 * 1000 {
141 (1000.0 * 1000.0, "events/megabyte")
142 } else {
143 (1000.0 * 1000.0 * 1000.0, "events/gigabyte")
144 };
145
146 format!("{:.4} {}", event_per_byte / denominator, unit)
147 }
148 Throughput::Elements(bytes) => format!("{:.4} events/element", value / *bytes as f64),
149 }
150 }
151
152 fn scale_values(&self, _typical_value: f64, _values: &mut [f64]) -> &'static str {
153 "events"
154 }
155
156 fn scale_throughputs(
157 &self,
158 _typical_value: f64,
159 throughput: &Throughput,
160 values: &mut [f64],
161 ) -> &'static str {
162 match throughput {
163 Throughput::Bytes(bytes) => {
164 for val in values {
165 *val /= *bytes as f64;
166 }
167 "events/byte"
168 }
169 Throughput::BytesDecimal(bytes) => {
170 let bytes_per_second = *bytes;
171 let (denominator, unit) = if bytes_per_second < 1000 {
172 (1.0, "events/byte")
173 } else if bytes_per_second < 1000 * 1000 {
174 (1000.0, "events/kilobyte")
175 } else if bytes_per_second < 1000 * 1000 * 1000 {
176 (1000.0 * 1000.0, "events/megabyte")
177 } else {
178 (1000.0 * 1000.0 * 1000.0, "events/gigabyte")
179 };
180
181 for val in values {
182 *val /= *bytes as f64;
183 *val /= denominator;
184 }
185
186 unit
187 }
188 Throughput::Elements(bytes) => {
189 for val in values {
190 *val /= *bytes as f64;
191 }
192 "events/element"
193 }
194 }
195 }
196
197 fn scale_for_machines(&self, _values: &mut [f64]) -> &'static str {
198 "events"
199 }
200}