Skip to content

Commit 591ad2b

Browse files
Oppenpefontana
authored andcommitted
perf: reference management optimizations (lambdaclass#1214)
* wip * test passing * more accurate bench * fix * perf: optimizations for reference management - Store directly the reference list rather than the whole `ReferenceManager` structures - Only process them once, when creating the `Program` - Move them to the `SharedProgramData` member - Convert to `Vec<HintReference>` and adapt the methods using it --------- Co-authored-by: Pedro Fontana <[email protected]>
1 parent 9809b48 commit 591ad2b

File tree

11 files changed

+78
-81
lines changed

11 files changed

+78
-81
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
* perf: accumulate `min` and `max` instruction offsets during run to speed up range check [#1080](https://github.com/lambdaclass/cairo-rs/pull/)
66
BREAKING: `Cairo_runner::get_perm_range_check_limits` no longer returns an error when called without trace enabled, as it no longer depends on it
77

8+
* perf: process reference list on `Program` creation only [#1214](https://github.com/lambdaclass/cairo-rs/pull/1214)
9+
Also keep them in a `Vec<_>` instead of a `HashMap<_, _>` since it will be continuous anyway.
10+
BREAKING:
11+
* `HintProcessor::compile_hint` now receies a `&[HintReference]` rather than `&HashMap<usize, HintReference>`
12+
* Public `CairoRunner::get_reference_list` has been removed
13+
814
#### [0.5.2] - 2023-6-12
915

1016
* BREAKING: Compute `ExecutionResources.n_steps` without requiring trace [#1222](https://github.com/lambdaclass/cairo-rs/pull/1222)

bench/criterion_benchmark.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ fn parse_program(c: &mut Criterion) {
1717
//Picked the biggest one at the time of writing
1818
let program = include_bytes!("../cairo_programs/benchmarks/keccak_integration_benchmark.json");
1919
c.bench_function("parse program", |b| {
20-
b.iter(|| {
20+
b.iter_with_large_drop(|| {
2121
_ = Program::from_bytes(black_box(program.as_slice()), black_box(Some("main")))
2222
.unwrap();
2323
})
@@ -29,7 +29,7 @@ fn build_many_runners(c: &mut Criterion) {
2929
let program = include_bytes!("../cairo_programs/benchmarks/keccak_integration_benchmark.json");
3030
let program = Program::from_bytes(program.as_slice(), Some("main")).unwrap();
3131
c.bench_function("build runner", |b| {
32-
b.iter(|| {
32+
b.iter_with_large_drop(|| {
3333
_ = black_box(
3434
CairoRunner::new(
3535
black_box(&program),

bench/iai_benchmark.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use iai_callgrind::{black_box, main};
1+
use core::hint::black_box;
2+
use iai_callgrind::main;
23

34
use cairo_vm::{
45
types::program::Program,
@@ -18,7 +19,7 @@ fn parse_program() {
1819
let program = include_bytes!("../cairo_programs/benchmarks/keccak_integration_benchmark.json");
1920
let program =
2021
Program::from_bytes(black_box(program.as_slice()), black_box(Some("main"))).unwrap();
21-
let _ = black_box(program);
22+
core::mem::drop(black_box(program));
2223
}
2324

2425
#[export_name = "helper::parse_program"]
@@ -33,7 +34,7 @@ fn parse_program_helper() -> Program {
3334
fn build_runner() {
3435
let program = parse_program_helper();
3536
let runner = CairoRunner::new(black_box(&program), "starknet_with_keccak", false).unwrap();
36-
let _ = black_box(runner);
37+
core::mem::drop(black_box(runner));
3738
}
3839

3940
#[export_name = "helper::build_runner"]
@@ -54,6 +55,6 @@ fn load_program_data() {
5455
}
5556

5657
main!(
57-
callgrind_args = "toggle-collect=helper::*";
58+
callgrind_args = "toggle-collect=helper::*,core::mem::drop";
5859
functions = parse_program, build_runner, load_program_data
5960
);

hint_accountant/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn run() {
5050
let (ap_tracking_data, reference_ids, references, mut exec_scopes, constants) = (
5151
ApTracking::default(),
5252
HashMap::new(),
53-
HashMap::new(),
53+
Vec::new(),
5454
ExecutionScopes::new(),
5555
HashMap::new(),
5656
);

vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,7 @@ impl HintProcessor for Cairo1HintProcessor {
11061106
//(may contain other variables aside from those used by the hint)
11071107
_reference_ids: &HashMap<String, usize>,
11081108
//List of all references (key corresponds to element of the previous dictionary)
1109-
_references: &HashMap<usize, HintReference>,
1109+
_references: &[HintReference],
11101110
) -> Result<Box<dyn Any>, VirtualMachineError> {
11111111
let data = hint_code.parse().ok().and_then(|x: usize| self.hints.get(&x).cloned())
11121112
.ok_or(VirtualMachineError::CompileHintFail(

vm/src/hint_processor/hint_processor_definition.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub trait HintProcessor {
4040
//(may contain other variables aside from those used by the hint)
4141
reference_ids: &HashMap<String, usize>,
4242
//List of all references (key corresponds to element of the previous dictionary)
43-
references: &HashMap<usize, HintReference>,
43+
references: &[HintReference],
4444
) -> Result<Box<dyn Any>, VirtualMachineError> {
4545
Ok(any_box!(HintProcessorData {
4646
code: hint_code.to_string(),
@@ -52,7 +52,7 @@ pub trait HintProcessor {
5252

5353
fn get_ids_data(
5454
reference_ids: &HashMap<String, usize>,
55-
references: &HashMap<usize, HintReference>,
55+
references: &[HintReference],
5656
) -> Result<HashMap<String, HintReference>, VirtualMachineError> {
5757
let mut ids_data = HashMap::<String, HintReference>::new();
5858
for (path, ref_id) in reference_ids {
@@ -63,7 +63,7 @@ fn get_ids_data(
6363
ids_data.insert(
6464
name.to_string(),
6565
references
66-
.get(ref_id)
66+
.get(*ref_id)
6767
.ok_or(VirtualMachineError::Unexpected)?
6868
.clone(),
6969
);

vm/src/serde/deserialize_program.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ fn deserialize_scientific_notation(n: Number) -> Option<Felt252> {
200200
}
201201
}
202202

203-
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
203+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
204204
pub struct ReferenceManager {
205205
pub references: Vec<Reference>,
206206
}
@@ -452,11 +452,11 @@ pub fn parse_program_json(
452452
.debug_info
453453
.map(|debug_info| debug_info.instruction_locations),
454454
identifiers: program_json.identifiers,
455+
reference_manager: Program::get_reference_list(&program_json.reference_manager),
455456
};
456457
Ok(Program {
457458
shared_program_data: Arc::new(shared_program_data),
458459
constants,
459-
reference_manager: program_json.reference_manager,
460460
builtins: program_json.builtins,
461461
})
462462
}

vm/src/types/program.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ use crate::stdlib::{collections::HashMap, prelude::*, sync::Arc};
33
#[cfg(feature = "cairo-1-hints")]
44
use crate::serde::deserialize_program::{ApTracking, FlowTrackingData};
55
use crate::{
6+
hint_processor::hint_processor_definition::HintReference,
67
serde::deserialize_program::{
78
deserialize_and_parse_program, Attribute, BuiltinName, HintParams, Identifier,
8-
InstructionLocation, ReferenceManager,
9+
InstructionLocation, OffsetValue, ReferenceManager,
10+
},
11+
types::{
12+
errors::program_errors::ProgramError, instruction::Register, relocatable::MaybeRelocatable,
913
},
10-
types::{errors::program_errors::ProgramError, relocatable::MaybeRelocatable},
1114
};
1215
#[cfg(feature = "cairo-1-hints")]
1316
use cairo_lang_starknet::casm_contract_class::CasmContractClass;
@@ -48,14 +51,14 @@ pub(crate) struct SharedProgramData {
4851
pub(crate) error_message_attributes: Vec<Attribute>,
4952
pub(crate) instruction_locations: Option<HashMap<usize, InstructionLocation>>,
5053
pub(crate) identifiers: HashMap<String, Identifier>,
54+
pub(crate) reference_manager: Vec<HintReference>,
5155
}
5256

5357
#[derive(Clone, Debug, PartialEq, Eq)]
5458
pub struct Program {
5559
pub(crate) shared_program_data: Arc<SharedProgramData>,
5660
pub(crate) constants: HashMap<String, Felt252>,
5761
pub(crate) builtins: Vec<BuiltinName>,
58-
pub(crate) reference_manager: ReferenceManager,
5962
}
6063

6164
impl Program {
@@ -89,11 +92,11 @@ impl Program {
8992
error_message_attributes,
9093
instruction_locations,
9194
identifiers,
95+
reference_manager: Self::get_reference_list(&reference_manager),
9296
};
9397
Ok(Self {
9498
shared_program_data: Arc::new(shared_program_data),
9599
constants,
96-
reference_manager,
97100
builtins,
98101
})
99102
}
@@ -139,16 +142,36 @@ impl Program {
139142
.iter()
140143
.map(|(cairo_type, identifier)| (cairo_type.as_str(), identifier))
141144
}
145+
146+
pub(crate) fn get_reference_list(reference_manager: &ReferenceManager) -> Vec<HintReference> {
147+
reference_manager
148+
.references
149+
.iter()
150+
.map(|r| {
151+
HintReference {
152+
offset1: r.value_address.offset1.clone(),
153+
offset2: r.value_address.offset2.clone(),
154+
dereference: r.value_address.dereference,
155+
// only store `ap` tracking data if the reference is referred to it
156+
ap_tracking_data: match (&r.value_address.offset1, &r.value_address.offset2) {
157+
(OffsetValue::Reference(Register::AP, _, _), _)
158+
| (_, OffsetValue::Reference(Register::AP, _, _)) => {
159+
Some(r.ap_tracking_data.clone())
160+
}
161+
_ => None,
162+
},
163+
cairo_type: Some(r.value_address.value_type.clone()),
164+
}
165+
})
166+
.collect()
167+
}
142168
}
143169

144170
impl Default for Program {
145171
fn default() -> Self {
146172
Self {
147173
shared_program_data: Arc::new(SharedProgramData::default()),
148174
constants: HashMap::new(),
149-
reference_manager: ReferenceManager {
150-
references: Vec::new(),
151-
},
152175
builtins: Vec::new(),
153176
}
154177
}
@@ -854,13 +877,13 @@ mod tests {
854877
error_message_attributes: Vec::new(),
855878
instruction_locations: None,
856879
identifiers: HashMap::new(),
880+
reference_manager: Program::get_reference_list(&ReferenceManager {
881+
references: Vec::new(),
882+
}),
857883
};
858884
let program = Program {
859885
shared_program_data: Arc::new(shared_program_data),
860886
constants: HashMap::new(),
861-
reference_manager: ReferenceManager {
862-
references: Vec::new(),
863-
},
864887
builtins: Vec::new(),
865888
};
866889

vm/src/utils.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,14 @@ pub mod test_utils {
265265
error_message_attributes: crate::stdlib::vec::Vec::new(),
266266
instruction_locations: None,
267267
identifiers: crate::stdlib::collections::HashMap::new(),
268+
reference_manager: Program::get_reference_list(&ReferenceManager {
269+
references: crate::stdlib::vec::Vec::new(),
270+
}),
268271
};
269272
Program {
270273
shared_program_data: Arc::new(shared_program_data),
271274
constants: crate::stdlib::collections::HashMap::new(),
272275
builtins: vec![$( $builtin_name ),*],
273-
reference_manager: ReferenceManager {
274-
references: crate::stdlib::vec::Vec::new(),
275-
},
276276
}
277277
}};
278278
($($field:ident = $value:expr),* $(,)?) => {{
@@ -352,10 +352,10 @@ pub mod test_utils {
352352
error_message_attributes: val.error_message_attributes,
353353
instruction_locations: val.instruction_locations,
354354
identifiers: val.identifiers,
355+
reference_manager: Program::get_reference_list(&val.reference_manager),
355356
}),
356357
constants: val.constants,
357358
builtins: val.builtins,
358-
reference_manager: val.reference_manager,
359359
}
360360
}
361361
}
@@ -926,13 +926,13 @@ mod test {
926926
error_message_attributes: Vec::new(),
927927
instruction_locations: None,
928928
identifiers: HashMap::new(),
929+
reference_manager: Program::get_reference_list(&ReferenceManager {
930+
references: Vec::new(),
931+
}),
929932
};
930933
let program = Program {
931934
shared_program_data: Arc::new(shared_data),
932935
constants: HashMap::new(),
933-
reference_manager: ReferenceManager {
934-
references: Vec::new(),
935-
},
936936
builtins: Vec::new(),
937937
};
938938
assert_eq!(program, program!())
@@ -950,13 +950,13 @@ mod test {
950950
error_message_attributes: Vec::new(),
951951
instruction_locations: None,
952952
identifiers: HashMap::new(),
953+
reference_manager: Program::get_reference_list(&ReferenceManager {
954+
references: Vec::new(),
955+
}),
953956
};
954957
let program = Program {
955958
shared_program_data: Arc::new(shared_data),
956959
constants: HashMap::new(),
957-
reference_manager: ReferenceManager {
958-
references: Vec::new(),
959-
},
960960
builtins: vec![BuiltinName::range_check],
961961
};
962962

@@ -975,13 +975,13 @@ mod test {
975975
error_message_attributes: Vec::new(),
976976
instruction_locations: None,
977977
identifiers: HashMap::new(),
978+
reference_manager: Program::get_reference_list(&ReferenceManager {
979+
references: Vec::new(),
980+
}),
978981
};
979982
let program = Program {
980983
shared_program_data: Arc::new(shared_data),
981984
constants: HashMap::new(),
982-
reference_manager: ReferenceManager {
983-
references: Vec::new(),
984-
},
985985
builtins: vec![BuiltinName::range_check],
986986
};
987987

vm/src/vm/errors/vm_exception.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ use thiserror::Error;
1010
use thiserror_no_std::Error;
1111

1212
use crate::{
13-
hint_processor::{
14-
hint_processor_definition::HintReference,
15-
hint_processor_utils::get_maybe_relocatable_from_reference,
16-
},
13+
hint_processor::hint_processor_utils::get_maybe_relocatable_from_reference,
1714
serde::deserialize_program::{ApTracking, Attribute, Location, OffsetValue},
1815
types::{instruction::Register, relocatable::MaybeRelocatable},
1916
vm::{runners::cairo_runner::CairoRunner, vm_core::VirtualMachine},
@@ -177,21 +174,19 @@ fn get_value_from_simple_reference(
177174
runner: &CairoRunner,
178175
vm: &VirtualMachine,
179176
) -> Option<MaybeRelocatable> {
180-
let reference: HintReference = runner
177+
let reference = runner
181178
.program
179+
.shared_program_data
182180
.reference_manager
183-
.references
184-
.get(ref_id)?
185-
.clone()
186-
.into();
181+
.get(ref_id)?;
187182
// Filter ap-based references
188183
match reference.offset1 {
189184
OffsetValue::Reference(Register::AP, _, _) => None,
190185
_ => {
191186
// Filer complex types (only felt/felt pointers)
192187
match reference.cairo_type {
193188
Some(ref cairo_type) if cairo_type.contains("felt") => Some(
194-
get_maybe_relocatable_from_reference(vm, &reference, ap_tracking)?,
189+
get_maybe_relocatable_from_reference(vm, reference, ap_tracking)?,
195190
),
196191
_ => None,
197192
}

0 commit comments

Comments
 (0)