Skip to content

Commit b96b8c4

Browse files
committed
perf: improve EIP-3155 tracer
- Buffer stderr - Allocate less
1 parent 4c77354 commit b96b8c4

File tree

2 files changed

+77
-69
lines changed

2 files changed

+77
-69
lines changed

bins/revme/src/cmd/statetest/runner.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ pub fn execute_test_suite(
427427

428428
let timer = Instant::now();
429429
let res = ctx.inspect_commit_previous(
430-
TracerEip3155::new(Box::new(stderr())).without_summary(),
430+
TracerEip3155::buffered(stderr()).without_summary(),
431431
);
432432
*elapsed.lock().unwrap() += timer.elapsed();
433433

@@ -497,9 +497,8 @@ pub fn execute_test_suite(
497497
.with_tx(&tx)
498498
.with_cfg(&cfg);
499499

500-
let _ = ctx.inspect_commit_previous(
501-
TracerEip3155::new(Box::new(stderr())).without_summary(),
502-
);
500+
let _ = ctx
501+
.inspect_commit_previous(TracerEip3155::buffered(stderr()).without_summary());
503502

504503
println!("\nExecution result: {exec_result:#?}");
505504
println!("\nExpected exception: {:?}", test.expect_exception);

crates/inspector/src/eip3155.rs

+74-65
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub struct TracerEip3155<CTX, INTR> {
3838
// The CUT MUST output a `json` object for EACH operation.
3939
#[derive(Serialize)]
4040
#[serde(rename_all = "camelCase")]
41-
struct Output {
41+
struct Output<'a> {
4242
// Required fields:
4343
/// Program counter
4444
pc: u64,
@@ -48,22 +48,26 @@ struct Output {
4848
/// OpCode
4949
op: u8,
5050
/// Gas left before executing this operation
51-
gas: String,
51+
#[serde(serialize_with = "serde_hex_u64")]
52+
gas: u64,
5253
/// Gas cost of this operation
53-
gas_cost: String,
54+
#[serde(serialize_with = "serde_hex_u64")]
55+
gas_cost: u64,
5456
/// Array of all values on the stack
55-
stack: Vec<String>,
57+
stack: &'a [U256],
5658
/// Depth of the call stack
5759
depth: u64,
5860
/// Depth of the EOF function call stack
5961
#[serde(default, skip_serializing_if = "Option::is_none")]
6062
function_depth: Option<u64>,
6163
/// Data returned by the function call
62-
return_data: String,
64+
return_data: &'static str,
6365
/// Amount of **global** gas refunded
64-
refund: String,
66+
#[serde(serialize_with = "serde_hex_u64")]
67+
refund: u64,
6568
/// Size of memory array
66-
mem_size: String,
69+
#[serde(serialize_with = "serde_hex_u64")]
70+
mem_size: u64,
6771

6872
// Optional fields:
6973
/// Name of the operation
@@ -93,7 +97,8 @@ struct Summary {
9397
/// Return values of the function
9498
output: String,
9599
/// All gas used by the transaction
96-
gas_used: String,
100+
#[serde(serialize_with = "serde_hex_u64")]
101+
gas_used: u64,
97102
/// Bool whether transaction was executed successfully
98103
pass: bool,
99104

@@ -111,35 +116,13 @@ where
111116
CTX: CfgGetter + TransactionGetter,
112117
INTR:,
113118
{
114-
/// Sets the writer to use for the output.
115-
pub fn set_writer(&mut self, writer: Box<dyn Write>) {
116-
self.output = writer;
117-
}
118-
119-
/// Resets the Tracer to its initial state of [Self::new].
120-
/// This makes the inspector ready to be used again.
121-
pub fn clear(&mut self) {
122-
let Self {
123-
gas_inspector,
124-
stack,
125-
pc,
126-
opcode,
127-
gas,
128-
refunded,
129-
mem_size,
130-
skip,
131-
..
132-
} = self;
133-
*gas_inspector = GasInspector::new();
134-
stack.clear();
135-
*pc = 0;
136-
*opcode = 0;
137-
*gas = 0;
138-
*refunded = 0;
139-
*mem_size = 0;
140-
*skip = false;
119+
/// Creates a new EIP-3155 tracer with the given output writer, by first wrapping it in a
120+
/// [`BufWriter`](std::io::BufWriter).
121+
pub fn buffered(output: impl Write + 'static) -> Self {
122+
Self::new(Box::new(std::io::BufWriter::new(output)))
141123
}
142124

125+
/// Creates a new EIP-3155 tracer with the given output writer.
143126
pub fn new(output: Box<dyn Write>) -> Self {
144127
Self {
145128
output,
@@ -160,6 +143,11 @@ where
160143
}
161144
}
162145

146+
/// Sets the writer to use for the output.
147+
pub fn set_writer(&mut self, writer: Box<dyn Write>) {
148+
self.output = writer;
149+
}
150+
163151
/// Don't include a summary at the end of the trace
164152
pub fn without_summary(mut self) -> Self {
165153
self.print_summary = false;
@@ -172,10 +160,29 @@ where
172160
self
173161
}
174162

175-
fn write_value(&mut self, value: &impl serde::Serialize) -> std::io::Result<()> {
176-
serde_json::to_writer(&mut *self.output, value)?;
177-
self.output.write_all(b"\n")?;
178-
self.output.flush()
163+
/// Resets the tracer to its initial state of [`Self::new`].
164+
///
165+
/// This makes the inspector ready to be used again.
166+
pub fn clear(&mut self) {
167+
let Self {
168+
gas_inspector,
169+
stack,
170+
pc,
171+
opcode,
172+
gas,
173+
refunded,
174+
mem_size,
175+
skip,
176+
..
177+
} = self;
178+
*gas_inspector = GasInspector::new();
179+
stack.clear();
180+
*pc = 0;
181+
*opcode = 0;
182+
*gas = 0;
183+
*refunded = 0;
184+
*mem_size = 0;
185+
*skip = false;
179186
}
180187

181188
fn print_summary(&mut self, result: &InterpreterResult, context: &mut CTX) {
@@ -185,23 +192,27 @@ where
185192
let value = Summary {
186193
state_root: B256::ZERO.to_string(),
187194
output: result.output.to_string(),
188-
gas_used: hex_number(gas_limit - self.gas_inspector.gas_remaining()),
195+
gas_used: gas_limit - self.gas_inspector.gas_remaining(),
189196
pass: result.is_ok(),
190197
time: None,
191198
fork: Some(spec.to_string()),
192199
};
193200
let _ = self.write_value(&value);
194201
}
195202
}
203+
204+
fn write_value(&mut self, value: &impl serde::Serialize) -> std::io::Result<()> {
205+
write_value(&mut *self.output, value)
206+
}
196207
}
197208

198209
pub trait CloneStack {
199-
fn clone_from(&self) -> Vec<U256>;
210+
fn clone_into(&self, stack: &mut Vec<U256>);
200211
}
201212

202213
impl CloneStack for Stack {
203-
fn clone_from(&self) -> Vec<U256> {
204-
self.data().to_vec()
214+
fn clone_into(&self, stack: &mut Vec<U256>) {
215+
stack.extend_from_slice(self.data());
205216
}
206217
}
207218

@@ -216,10 +227,11 @@ where
216227

217228
fn step(&mut self, interp: &mut Interpreter<INTR>, _: &mut CTX) {
218229
self.gas_inspector.step(interp.control.gas());
219-
self.stack = interp.stack.clone_from();
230+
self.stack.clear();
231+
interp.stack.clone_into(&mut self.stack);
220232
self.memory = if self.include_memory {
221233
Some(hex::encode_prefixed(
222-
interp.memory.slice(0..usize::MAX).as_ref(),
234+
interp.memory.slice(0..interp.memory.size()).as_ref(),
223235
))
224236
} else {
225237
None
@@ -252,14 +264,14 @@ where
252264
pc: self.pc,
253265
section: self.section,
254266
op: self.opcode,
255-
gas: hex_number(self.gas),
256-
gas_cost: hex_number(self.gas_inspector.last_gas_cost()),
257-
stack: self.stack.iter().map(hex_number_u256).collect(),
267+
gas: self.gas,
268+
gas_cost: self.gas_inspector.last_gas_cost(),
269+
stack: &self.stack,
258270
depth: context.journal().depth() as u64,
259271
function_depth: self.function_depth,
260-
return_data: "0x".to_string(),
261-
refund: hex_number(self.refunded as u64),
262-
mem_size: self.mem_size.to_string(),
272+
return_data: "0x",
273+
refund: self.refunded as u64,
274+
mem_size: self.mem_size as u64,
263275

264276
op_name: OpCode::new(self.opcode).map(|i| i.as_str()),
265277
error: if !interp.control.instruction_result().is_ok() {
@@ -271,15 +283,15 @@ where
271283
storage: None,
272284
return_stack: None,
273285
};
274-
let _ = self.write_value(&value);
286+
let _ = write_value(&mut self.output, &value);
275287
}
276288

277289
fn call_end(&mut self, context: &mut CTX, _: &CallInputs, outcome: &mut CallOutcome) {
278290
self.gas_inspector.call_end(outcome);
279291

280292
if context.journal().depth() == 0 {
281293
self.print_summary(&outcome.result, context);
282-
// Clear the state if we are at the top level
294+
// Clear the state if we are at the top level.
283295
self.clear();
284296
}
285297
}
@@ -289,23 +301,20 @@ where
289301

290302
if context.journal().depth() == 0 {
291303
self.print_summary(&outcome.result, context);
292-
293-
// Clear the state if we are at the top level
304+
// Clear the state if we are at the top level.
294305
self.clear();
295306
}
296307
}
297308
}
298309

299-
fn hex_number(uint: u64) -> String {
300-
format!("0x{uint:x}")
310+
fn write_value(
311+
output: &mut dyn std::io::Write,
312+
value: &impl serde::Serialize,
313+
) -> std::io::Result<()> {
314+
serde_json::to_writer(&mut *output, value)?;
315+
output.write_all(b"\n")
301316
}
302317

303-
fn hex_number_u256(b: &U256) -> String {
304-
let s = hex::encode(b.to_be_bytes::<32>());
305-
let s = s.trim_start_matches('0');
306-
if s.is_empty() {
307-
"0x0".to_string()
308-
} else {
309-
format!("0x{s}")
310-
}
318+
fn serde_hex_u64<S: serde::Serializer>(n: &u64, serializer: S) -> Result<S::Ok, S::Error> {
319+
serializer.serialize_str(&format!("{:#x}", *n))
311320
}

0 commit comments

Comments
 (0)