Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: rework MemoryCell for cache efficiency #1672

Merged
merged 9 commits into from
May 6, 2024
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## Cairo-VM Changelog

#### Upcoming Changes

* perf: use a more compact representation for `MemoryCell` [#1672](https://github.com/lambdaclass/cairo-vm/pull/1672)
* BREAKING: `Memory::get_value` will now always return `Cow::Owned` variants, code that relied on `Cow::Borrowed` may break

* feat: add a `--tracer` option which hosts a web server that shows the line by line execution of cairo code along with memory registers [#1265](https://github.com/lambdaclass/cairo-vm/pull/1265)

* feat: Fix error handling in `initialize_state`[#1657](https://github.com/lambdaclass/cairo-vm/pull/1657)
Expand Down
2 changes: 1 addition & 1 deletion vm/src/hint_processor/builtin_hint_processor/hint_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ mod tests {

assert_matches!(
get_integer_from_var_name("value", &vm, &ids_data, &ApTracking::new()),
Ok(Cow::Borrowed(x)) if x == &Felt252::from(1)
Ok(Cow::Owned(x)) if x == Felt252::from(1)
);
}

Expand Down
23 changes: 11 additions & 12 deletions vm/src/vm/runners/builtin_runner/ec_op.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::air_private_input::{PrivateInput, PrivateInputEcOp};
use crate::stdlib::{borrow::Cow, prelude::*};
use crate::stdlib::prelude::*;
use crate::stdlib::{cell::RefCell, collections::HashMap};
use crate::types::instance_definitions::ec_op_instance_def::{
EcOpInstanceDef, CELLS_PER_EC_OP, INPUT_CELLS_PER_EC_OP,
Expand Down Expand Up @@ -136,14 +136,13 @@ impl EcOpBuiltinRunner {

//All input cells should be filled, and be integer values
//If an input cell is not filled, return None
let mut input_cells = Vec::<&Felt252>::with_capacity(self.n_input_cells as usize);
let mut input_cells = Vec::<Felt252>::with_capacity(self.n_input_cells as usize);
for i in 0..self.n_input_cells as usize {
match memory.get(&(instance + i)?) {
None => return Ok(None),
Some(addr) => {
input_cells.push(match addr {
// Only relocatable values can be owned
Cow::Borrowed(MaybeRelocatable::Int(ref num)) => num,
input_cells.push(match addr.as_ref() {
MaybeRelocatable::Int(num) => *num,
_ => {
return Err(RunnerError::Memory(MemoryError::ExpectedInteger(
Box::new((instance + i)?),
Expand All @@ -163,21 +162,21 @@ impl EcOpBuiltinRunner {
// Assert that if the current address is part of a point, the point is on the curve
for pair in &EC_POINT_INDICES[0..2] {
if !EcOpBuiltinRunner::point_on_curve(
input_cells[pair.0],
input_cells[pair.1],
&input_cells[pair.0],
&input_cells[pair.1],
&alpha,
&beta,
) {
return Err(RunnerError::PointNotOnCurve(Box::new((
*input_cells[pair.0],
*input_cells[pair.1],
input_cells[pair.0],
input_cells[pair.1],
))));
};
}
let result = EcOpBuiltinRunner::ec_op_impl(
(input_cells[0].to_owned(), input_cells[1].to_owned()),
(input_cells[2].to_owned(), input_cells[3].to_owned()),
input_cells[4],
(input_cells[0], input_cells[1]),
(input_cells[2], input_cells[3]),
&input_cells[4],
self.ec_op_builtin.scalar_height,
)?;
self.cache.borrow_mut().insert(x_addr, result.0);
Expand Down
15 changes: 12 additions & 3 deletions vm/src/vm/runners/builtin_runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,11 @@ impl BuiltinRunner {
for i in 0..n {
for j in 0..n_input_cells {
let offset = cells_per_instance * i + j;
if let None | Some(None) = builtin_segment.get(offset) {
if builtin_segment
.get(offset)
.filter(|x| x.is_some())
.is_none()
{
missing_offsets.push(offset)
}
}
Expand All @@ -439,7 +443,11 @@ impl BuiltinRunner {
for i in 0..n {
for j in n_input_cells..cells_per_instance {
let offset = cells_per_instance * i + j;
if let None | Some(None) = builtin_segment.get(offset) {
if builtin_segment
.get(offset)
.filter(|x| x.is_some())
.is_none()
{
vm.verify_auto_deductions_for_addr(
Relocatable::from((builtin_segment_index as isize, offset)),
self,
Expand Down Expand Up @@ -587,6 +595,7 @@ mod tests {
},
utils::test_utils::*,
vm::vm_core::VirtualMachine,
vm::vm_memory::memory::MemoryCell,
};
use assert_matches::assert_matches;

Expand Down Expand Up @@ -1367,7 +1376,7 @@ mod tests {

let mut vm = vm!();

vm.segments.memory.data = vec![vec![None, None, None]];
vm.segments.memory.data = vec![vec![MemoryCell::NONE, MemoryCell::NONE, MemoryCell::NONE]];

assert_matches!(builtin.run_security_checks(&vm), Ok(()));
}
Expand Down
7 changes: 3 additions & 4 deletions vm/src/vm/runners/builtin_runner/range_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@ impl RangeCheckBuiltinRunner {
// Split value into n_parts parts of less than _INNER_RC_BOUND size.
for value in range_check_segment {
rc_bounds = value
.as_ref()?
.get_value()
.get_value()?
.get_int_ref()?
.to_le_digits()
// TODO: maybe skip leading zeros
Expand Down Expand Up @@ -199,8 +198,8 @@ impl RangeCheckBuiltinRunner {
pub fn air_private_input(&self, memory: &Memory) -> Vec<PrivateInput> {
let mut private_inputs = vec![];
if let Some(segment) = memory.data.get(self.base) {
for (index, val) in segment.iter().enumerate() {
if let Some(value) = val.as_ref().and_then(|cell| cell.get_value().get_int()) {
for (index, cell) in segment.iter().enumerate() {
if let Some(value) = cell.get_value().and_then(|value| value.get_int()) {
private_inputs.push(PrivateInput::Value(PrivateInputValue { index, value }))
}
}
Expand Down
40 changes: 20 additions & 20 deletions vm/src/vm/runners/cairo_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,13 +912,13 @@ impl CairoRunner {
self.relocated_memory.push(None);
for (index, segment) in vm.segments.memory.data.iter().enumerate() {
for (seg_offset, cell) in segment.iter().enumerate() {
match cell {
match cell.get_value() {
Some(cell) => {
let relocated_addr = relocate_address(
Relocatable::from((index as isize, seg_offset)),
relocation_table,
)?;
let value = relocate_value(cell.get_value().clone(), relocation_table)?;
let value = relocate_value(cell, relocation_table)?;
if self.relocated_memory.len() <= relocated_addr {
self.relocated_memory.resize(relocated_addr + 1, None);
}
Expand Down Expand Up @@ -4061,11 +4061,11 @@ mod tests {

vm.segments.memory.data = vec![
vec![
Some(MemoryCell::new(Felt252::from(0x8000_8023_8012u64).into())),
Some(MemoryCell::new(Felt252::from(0xBFFF_8000_0620u64).into())),
Some(MemoryCell::new(Felt252::from(0x8FFF_8000_0750u64).into())),
MemoryCell::new(Felt252::from(0x8000_8023_8012u64).into()),
MemoryCell::new(Felt252::from(0xBFFF_8000_0620u64).into()),
MemoryCell::new(Felt252::from(0x8FFF_8000_0750u64).into()),
],
vec![Some(MemoryCell::new((0isize, 0usize).into())); 128 * 1024],
vec![MemoryCell::new((0isize, 0usize).into()); 128 * 1024],
];

cairo_runner
Expand All @@ -4088,9 +4088,9 @@ mod tests {
let cairo_runner = cairo_runner!(program);
let mut vm = vm!();

vm.segments.memory.data = vec![vec![Some(MemoryCell::new(mayberelocatable!(
vm.segments.memory.data = vec![vec![MemoryCell::new(mayberelocatable!(
0x80FF_8000_0530u64
)))]];
))]];
vm.builtin_runners = vec![RangeCheckBuiltinRunner::new(Some(12), 5, true).into()];

assert_matches!(
Expand Down Expand Up @@ -4124,9 +4124,9 @@ mod tests {
let mut vm = vm!();
vm.builtin_runners = vec![];
vm.current_step = 10000;
vm.segments.memory.data = vec![vec![Some(MemoryCell::new(mayberelocatable!(
vm.segments.memory.data = vec![vec![MemoryCell::new(mayberelocatable!(
0x80FF_8000_0530u64
)))]];
))]];
vm.trace = Some(vec![TraceEntry {
pc: (0, 0).into(),
ap: 0,
Expand All @@ -4146,9 +4146,9 @@ mod tests {
let cairo_runner = cairo_runner!(program);
let mut vm = vm!();
vm.builtin_runners = vec![RangeCheckBuiltinRunner::new(Some(8), 8, true).into()];
vm.segments.memory.data = vec![vec![Some(MemoryCell::new(mayberelocatable!(
vm.segments.memory.data = vec![vec![MemoryCell::new(mayberelocatable!(
0x80FF_8000_0530u64
)))]];
))]];
vm.trace = Some(vec![TraceEntry {
pc: (0, 0).into(),
ap: 0,
Expand Down Expand Up @@ -4213,9 +4213,9 @@ mod tests {
let cairo_runner = cairo_runner!(program);
let mut vm = vm!();
vm.builtin_runners = vec![RangeCheckBuiltinRunner::new(Some(8), 8, true).into()];
vm.segments.memory.data = vec![vec![Some(MemoryCell::new(mayberelocatable!(
vm.segments.memory.data = vec![vec![MemoryCell::new(mayberelocatable!(
0x80FF_8000_0530u64
)))]];
))]];
vm.trace = Some(vec![TraceEntry {
pc: (0, 0).into(),
ap: 0,
Expand Down Expand Up @@ -4710,7 +4710,7 @@ mod tests {
vm.builtin_runners.push(output_builtin.into());
vm.segments.memory.data = vec![
vec![],
vec![Some(MemoryCell::new(MaybeRelocatable::from((0, 0))))],
vec![MemoryCell::new(MaybeRelocatable::from((0, 0)))],
vec![],
];
vm.set_ap(1);
Expand Down Expand Up @@ -4740,8 +4740,8 @@ mod tests {
let output_builtin = OutputBuiltinRunner::new(true);
vm.builtin_runners.push(output_builtin.into());
vm.segments.memory.data = vec![
vec![Some(MemoryCell::new(MaybeRelocatable::from((0, 0))))],
vec![Some(MemoryCell::new(MaybeRelocatable::from((0, 1))))],
vec![MemoryCell::new(MaybeRelocatable::from((0, 0)))],
vec![MemoryCell::new(MaybeRelocatable::from((0, 1)))],
vec![],
];
vm.set_ap(1);
Expand Down Expand Up @@ -4774,10 +4774,10 @@ mod tests {
vm.builtin_runners.push(bitwise_builtin.into());
cairo_runner.initialize_segments(&mut vm, None);
vm.segments.memory.data = vec![
vec![Some(MemoryCell::new(MaybeRelocatable::from((0, 0))))],
vec![MemoryCell::new(MaybeRelocatable::from((0, 0)))],
vec![
Some(MemoryCell::new(MaybeRelocatable::from((2, 0)))),
Some(MemoryCell::new(MaybeRelocatable::from((3, 5)))),
MemoryCell::new(MaybeRelocatable::from((2, 0))),
MemoryCell::new(MaybeRelocatable::from((3, 5))),
],
vec![],
];
Expand Down
4 changes: 2 additions & 2 deletions vm/src/vm/security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ pub fn verify_secure_runner(
// Asumption: If temporary memory is empty, this means no temporary memory addresses were generated and all addresses in memory are real
if !vm.segments.memory.temp_data.is_empty() {
for value in vm.segments.memory.data.iter().flatten() {
match value.as_ref().map(|x| x.get_value()) {
match value.get_value() {
Some(MaybeRelocatable::RelocatableValue(addr)) if addr.segment_index < 0 => {
return Err(VirtualMachineError::InvalidMemoryValueTemporaryAddress(
Box::new(*addr),
Box::new(addr),
))
}
_ => {}
Expand Down
60 changes: 30 additions & 30 deletions vm/src/vm/vm_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,12 +691,12 @@ impl VirtualMachine {
)
.map_err(VirtualMachineError::RunnerError)?
{
let value = value.as_ref().map(|x| x.get_value());
if Some(&deduced_memory_cell) != value && value.is_some() {
let value = value.get_value();
if Some(&deduced_memory_cell) != value.as_ref() && value.is_some() {
return Err(VirtualMachineError::InconsistentAutoDeduction(Box::new((
builtin.name(),
deduced_memory_cell,
value.cloned(),
value,
))));
}
}
Expand Down Expand Up @@ -3000,8 +3000,8 @@ mod tests {
//Check that the following addresses have been accessed:
// Addresses have been copied from python execution:
let mem = vm.segments.memory.data;
assert!(mem[1][0].as_ref().unwrap().is_accessed());
assert!(mem[1][1].as_ref().unwrap().is_accessed());
assert!(mem[1][0].is_accessed());
assert!(mem[1][1].is_accessed());
}

#[test]
Expand Down Expand Up @@ -3098,15 +3098,15 @@ mod tests {
//Check that the following addresses have been accessed:
// Addresses have been copied from python execution:
let mem = &vm.segments.memory.data;
assert!(mem[0][1].as_ref().unwrap().is_accessed());
assert!(mem[0][4].as_ref().unwrap().is_accessed());
assert!(mem[0][6].as_ref().unwrap().is_accessed());
assert!(mem[1][0].as_ref().unwrap().is_accessed());
assert!(mem[1][1].as_ref().unwrap().is_accessed());
assert!(mem[1][2].as_ref().unwrap().is_accessed());
assert!(mem[1][3].as_ref().unwrap().is_accessed());
assert!(mem[1][4].as_ref().unwrap().is_accessed());
assert!(mem[1][5].as_ref().unwrap().is_accessed());
assert!(mem[0][1].is_accessed());
assert!(mem[0][4].is_accessed());
assert!(mem[0][6].is_accessed());
assert!(mem[1][0].is_accessed());
assert!(mem[1][1].is_accessed());
assert!(mem[1][2].is_accessed());
assert!(mem[1][3].is_accessed());
assert!(mem[1][4].is_accessed());
assert!(mem[1][5].is_accessed());
assert_eq!(
vm.segments
.memory
Expand Down Expand Up @@ -4219,11 +4219,11 @@ mod tests {
//Check that the following addresses have been accessed:
// Addresses have been copied from python execution:
let mem = &vm.segments.memory.data;
assert!(mem[0][0].as_ref().unwrap().is_accessed());
assert!(mem[0][1].as_ref().unwrap().is_accessed());
assert!(mem[0][2].as_ref().unwrap().is_accessed());
assert!(mem[0][10].as_ref().unwrap().is_accessed());
assert!(mem[1][1].as_ref().unwrap().is_accessed());
assert!(mem[0][0].is_accessed());
assert!(mem[0][1].is_accessed());
assert!(mem[0][2].is_accessed());
assert!(mem[0][10].is_accessed());
assert!(mem[1][1].is_accessed());
assert_eq!(
vm.segments
.memory
Expand Down Expand Up @@ -4446,8 +4446,8 @@ mod tests {
//Check that the following addresses have been accessed:
// Addresses have been copied from python execution:
let mem = vm.segments.memory.data;
assert!(mem[1][0].as_ref().unwrap().is_accessed());
assert!(mem[1][1].as_ref().unwrap().is_accessed());
assert!(mem[1][0].is_accessed());
assert!(mem[1][1].is_accessed());
}

#[test]
Expand Down Expand Up @@ -4547,15 +4547,15 @@ mod tests {
//Check that the following addresses have been accessed:
// Addresses have been copied from python execution:
let mem = &vm.segments.memory.data;
assert!(mem[4][1].as_ref().unwrap().is_accessed());
assert!(mem[4][4].as_ref().unwrap().is_accessed());
assert!(mem[4][6].as_ref().unwrap().is_accessed());
assert!(mem[1][0].as_ref().unwrap().is_accessed());
assert!(mem[1][1].as_ref().unwrap().is_accessed());
assert!(mem[1][2].as_ref().unwrap().is_accessed());
assert!(mem[1][3].as_ref().unwrap().is_accessed());
assert!(mem[1][4].as_ref().unwrap().is_accessed());
assert!(mem[1][5].as_ref().unwrap().is_accessed());
assert!(mem[4][1].is_accessed());
assert!(mem[4][4].is_accessed());
assert!(mem[4][6].is_accessed());
assert!(mem[1][0].is_accessed());
assert!(mem[1][1].is_accessed());
assert!(mem[1][2].is_accessed());
assert!(mem[1][3].is_accessed());
assert!(mem[1][4].is_accessed());
assert!(mem[1][5].is_accessed());
assert_eq!(
vm.segments
.memory
Expand Down
Loading
Loading