reth_bench/bench/
output.rs1use eyre::OptionExt;
5use reth_primitives_traits::constants::GIGAGAS;
6use serde::{ser::SerializeStruct, Serialize};
7use std::time::Duration;
8
9pub(crate) const GAS_OUTPUT_SUFFIX: &str = "total_gas.csv";
11
12pub(crate) const COMBINED_OUTPUT_SUFFIX: &str = "combined_latency.csv";
14
15pub(crate) const NEW_PAYLOAD_OUTPUT_SUFFIX: &str = "new_payload_latency.csv";
17
18#[derive(Debug)]
21pub(crate) struct NewPayloadResult {
22 pub(crate) gas_used: u64,
24 pub(crate) latency: Duration,
26}
27
28impl NewPayloadResult {
29 pub(crate) fn gas_per_second(&self) -> f64 {
31 self.gas_used as f64 / self.latency.as_secs_f64()
32 }
33}
34
35impl std::fmt::Display for NewPayloadResult {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 write!(
38 f,
39 "New payload processed at {:.4} Ggas/s, used {} total gas. Latency: {:?}",
40 self.gas_per_second() / GIGAGAS as f64,
41 self.gas_used,
42 self.latency
43 )
44 }
45}
46
47impl Serialize for NewPayloadResult {
50 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51 where
52 S: serde::ser::Serializer,
53 {
54 let time = self.latency.as_micros();
56 let mut state = serializer.serialize_struct("NewPayloadResult", 2)?;
57 state.serialize_field("gas_used", &self.gas_used)?;
58 state.serialize_field("latency", &time)?;
59 state.end()
60 }
61}
62
63#[derive(Debug)]
67pub(crate) struct CombinedResult {
68 pub(crate) block_number: u64,
70 pub(crate) transaction_count: u64,
72 pub(crate) new_payload_result: NewPayloadResult,
74 pub(crate) fcu_latency: Duration,
76 pub(crate) total_latency: Duration,
78}
79
80impl CombinedResult {
81 pub(crate) fn combined_gas_per_second(&self) -> f64 {
83 self.new_payload_result.gas_used as f64 / self.total_latency.as_secs_f64()
84 }
85}
86
87impl std::fmt::Display for CombinedResult {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(
90 f,
91 "Payload {} processed at {:.4} Ggas/s, used {} total gas. Combined gas per second: {:.4} Ggas/s. fcu latency: {:?}, newPayload latency: {:?}",
92 self.block_number,
93 self.new_payload_result.gas_per_second() / GIGAGAS as f64,
94 self.new_payload_result.gas_used,
95 self.combined_gas_per_second() / GIGAGAS as f64,
96 self.fcu_latency,
97 self.new_payload_result.latency
98 )
99 }
100}
101
102impl Serialize for CombinedResult {
105 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
106 where
107 S: serde::ser::Serializer,
108 {
109 let fcu_latency = self.fcu_latency.as_micros();
111 let new_payload_latency = self.new_payload_result.latency.as_micros();
112 let total_latency = self.total_latency.as_micros();
113 let mut state = serializer.serialize_struct("CombinedResult", 6)?;
114
115 state.serialize_field("block_number", &self.block_number)?;
117 state.serialize_field("transaction_count", &self.transaction_count)?;
118 state.serialize_field("gas_used", &self.new_payload_result.gas_used)?;
119 state.serialize_field("new_payload_latency", &new_payload_latency)?;
120 state.serialize_field("fcu_latency", &fcu_latency)?;
121 state.serialize_field("total_latency", &total_latency)?;
122 state.end()
123 }
124}
125
126#[derive(Debug)]
128pub(crate) struct TotalGasRow {
129 pub(crate) block_number: u64,
131 pub(crate) transaction_count: u64,
133 pub(crate) gas_used: u64,
135 pub(crate) time: Duration,
137}
138
139#[derive(Debug)]
141pub(crate) struct TotalGasOutput {
142 pub(crate) total_gas_used: u64,
144 pub(crate) total_duration: Duration,
146 pub(crate) total_gas_per_second: f64,
148 pub(crate) blocks_processed: u64,
150}
151
152impl TotalGasOutput {
153 pub(crate) fn new(rows: Vec<TotalGasRow>) -> eyre::Result<Self> {
155 let total_duration = rows.last().map(|row| row.time).ok_or_eyre("empty results")?;
157 let blocks_processed = rows.len() as u64;
158 let total_gas_used: u64 = rows.into_iter().map(|row| row.gas_used).sum();
159 let total_gas_per_second = total_gas_used as f64 / total_duration.as_secs_f64();
160
161 Ok(Self { total_gas_used, total_duration, total_gas_per_second, blocks_processed })
162 }
163
164 pub(crate) fn total_gigagas_per_second(&self) -> f64 {
166 self.total_gas_per_second / GIGAGAS as f64
167 }
168}
169
170impl Serialize for TotalGasRow {
174 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
175 where
176 S: serde::ser::Serializer,
177 {
178 let time = self.time.as_micros();
180 let mut state = serializer.serialize_struct("TotalGasRow", 4)?;
181 state.serialize_field("block_number", &self.block_number)?;
182 state.serialize_field("transaction_count", &self.transaction_count)?;
183 state.serialize_field("gas_used", &self.gas_used)?;
184 state.serialize_field("time", &time)?;
185 state.end()
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use csv::Writer;
193 use std::io::BufRead;
194
195 #[test]
196 fn test_write_total_gas_row_csv() {
197 let row = TotalGasRow {
198 block_number: 1,
199 transaction_count: 10,
200 gas_used: 1_000,
201 time: Duration::from_secs(1),
202 };
203
204 let mut writer = Writer::from_writer(vec![]);
205 writer.serialize(row).unwrap();
206 let result = writer.into_inner().unwrap();
207
208 let mut result = result.as_slice().lines();
210
211 let expected_first_line = "block_number,transaction_count,gas_used,time";
213 let first_line = result.next().unwrap().unwrap();
214 assert_eq!(first_line, expected_first_line);
215
216 let expected_second_line = "1,10,1000,1000000";
217 let second_line = result.next().unwrap().unwrap();
218 assert_eq!(second_line, expected_second_line);
219 }
220}