Skip to content

Commit c035797

Browse files
authored
perf: cache decoded instructions (#944)
Decoding instructions (including fetching from memory, converting them to `u64` and later on parsing them) takes up a significant portion of time spent executing the main loop. Caching them as they get decoded alleviates it, reducing runtime in proof mode benchmarks up to 9%.
1 parent 06f4c19 commit c035797

File tree

3 files changed

+37
-18
lines changed

3 files changed

+37
-18
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@
199199
%{ ids.full_word = int(ids.n_bytes >= 8) %}
200200
```
201201

202+
* perf: cache decoded instructions [#944](https://github.com/lambdaclass/cairo-rs/pull/944)
203+
* Creates a new cache field in `VirtualMachine` that stores the `Instruction` instances as they get decoded from memory, significantly reducing decoding overhead, with gains up to 9% in runtime according to benchmarks in the performance server
204+
202205
* Add alternative hint code for nondet_bigint3 hint [#1071](https://github.com/lambdaclass/cairo-rs/pull/1071)
203206

204207
`BuiltinHintProcessor` now supports the following hint:

src/types/instruction.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ use serde::{Deserialize, Serialize};
44

55
use crate::vm::decoding::decoder::decode_instruction;
66

7-
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
7+
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
88
pub enum Register {
99
AP,
1010
FP,
1111
}
1212

13-
#[derive(Debug, PartialEq, Eq)]
13+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1414
pub struct Instruction {
1515
pub off0: isize,
1616
pub off1: isize,
@@ -25,46 +25,46 @@ pub struct Instruction {
2525
pub opcode: Opcode,
2626
}
2727

28-
#[derive(Debug, PartialEq, Eq)]
28+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2929
pub enum Op1Addr {
3030
Imm,
3131
AP,
3232
FP,
3333
Op0,
3434
}
3535

36-
#[derive(Debug, PartialEq, Eq)]
36+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
3737
pub enum Res {
3838
Op1,
3939
Add,
4040
Mul,
4141
Unconstrained,
4242
}
4343

44-
#[derive(Debug, PartialEq, Eq)]
44+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
4545
pub enum PcUpdate {
4646
Regular,
4747
Jump,
4848
JumpRel,
4949
Jnz,
5050
}
5151

52-
#[derive(Debug, PartialEq, Eq)]
52+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5353
pub enum ApUpdate {
5454
Regular,
5555
Add,
5656
Add1,
5757
Add2,
5858
}
5959

60-
#[derive(Debug, PartialEq, Eq)]
60+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6161
pub enum FpUpdate {
6262
Regular,
6363
APPlus2,
6464
Dst,
6565
}
6666

67-
#[derive(Debug, PartialEq, Eq)]
67+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6868
pub enum Opcode {
6969
NOp,
7070
AssertEq,

src/vm/vm_core.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub struct VirtualMachine {
8181
trace_relocated: bool,
8282
skip_instruction_execution: bool,
8383
run_finished: bool,
84+
instruction_cache: Vec<Option<Instruction>>,
8485
#[cfg(feature = "hooks")]
8586
pub(crate) hooks: crate::vm::hooks::Hooks,
8687
}
@@ -108,6 +109,7 @@ impl VirtualMachine {
108109
segments: MemorySegmentManager::new(),
109110
run_finished: false,
110111
trace_relocated: false,
112+
instruction_cache: Vec::new(),
111113
#[cfg(feature = "hooks")]
112114
hooks: Default::default(),
113115
}
@@ -183,12 +185,12 @@ impl VirtualMachine {
183185

184186
fn update_registers(
185187
&mut self,
186-
instruction: Instruction,
188+
instruction: &Instruction,
187189
operands: Operands,
188190
) -> Result<(), VirtualMachineError> {
189-
self.update_fp(&instruction, &operands)?;
190-
self.update_ap(&instruction, &operands)?;
191-
self.update_pc(&instruction, &operands)?;
191+
self.update_fp(instruction, &operands)?;
192+
self.update_ap(instruction, &operands)?;
193+
self.update_pc(instruction, &operands)?;
192194
Ok(())
193195
}
194196

@@ -381,11 +383,11 @@ impl VirtualMachine {
381383
Ok(())
382384
}
383385

384-
fn run_instruction(&mut self, instruction: Instruction) -> Result<(), VirtualMachineError> {
386+
fn run_instruction(&mut self, instruction: &Instruction) -> Result<(), VirtualMachineError> {
385387
let (operands, operands_addresses, deduced_operands) =
386-
self.compute_operands(&instruction)?;
388+
self.compute_operands(instruction)?;
387389
self.insert_deduced_operands(deduced_operands, &operands, &operands_addresses)?;
388-
self.opcode_assertions(&instruction, &operands)?;
390+
self.opcode_assertions(instruction, &operands)?;
389391

390392
if let Some(ref mut trace) = &mut self.trace {
391393
trace.push(TraceEntry {
@@ -438,13 +440,23 @@ impl VirtualMachine {
438440
}
439441

440442
pub fn step_instruction(&mut self) -> Result<(), VirtualMachineError> {
441-
let instruction = self.decode_current_instruction()?;
443+
let pc = self.run_context.pc.offset;
444+
445+
let mut inst_cache = core::mem::take(&mut self.instruction_cache);
446+
inst_cache.resize((pc + 1).max(inst_cache.len()), None);
447+
448+
let instruction = inst_cache.get_mut(pc).unwrap();
449+
if instruction.is_none() {
450+
*instruction = Some(self.decode_current_instruction()?);
451+
}
452+
let instruction = instruction.as_ref().unwrap();
442453
if !self.skip_instruction_execution {
443454
self.run_instruction(instruction)?;
444455
} else {
445456
self.run_context.pc += instruction.size();
446457
self.skip_instruction_execution = false;
447458
}
459+
self.instruction_cache = inst_cache;
448460
Ok(())
449461
}
450462

@@ -792,6 +804,9 @@ impl VirtualMachine {
792804
ptr: Relocatable,
793805
data: &Vec<MaybeRelocatable>,
794806
) -> Result<Relocatable, MemoryError> {
807+
if ptr.segment_index == 0 {
808+
self.instruction_cache.resize(data.len(), None);
809+
}
795810
self.segments.load_data(ptr, data)
796811
}
797812

@@ -1067,6 +1082,7 @@ impl VirtualMachineBuilder {
10671082
segments: self.segments,
10681083
run_finished: self.run_finished,
10691084
trace_relocated: false,
1085+
instruction_cache: Vec::new(),
10701086
#[cfg(feature = "hooks")]
10711087
hooks: self.hooks,
10721088
}
@@ -1750,7 +1766,7 @@ mod tests {
17501766
vm.run_context.fp = 6;
17511767

17521768
assert_matches!(
1753-
vm.update_registers(instruction, operands),
1769+
vm.update_registers(&instruction, operands),
17541770
Ok::<(), VirtualMachineError>(())
17551771
);
17561772
assert_eq!(vm.run_context.pc, Relocatable::from((0, 5)));
@@ -1786,7 +1802,7 @@ mod tests {
17861802
run_context!(vm, 4, 5, 6);
17871803

17881804
assert_matches!(
1789-
vm.update_registers(instruction, operands),
1805+
vm.update_registers(&instruction, operands),
17901806
Ok::<(), VirtualMachineError>(())
17911807
);
17921808
assert_eq!(vm.run_context.pc, Relocatable::from((0, 12)));

0 commit comments

Comments
 (0)