-
Notifications
You must be signed in to change notification settings - Fork 240
Add gas report logic #3841
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
base: master
Are you sure you want to change the base?
Add gas report logic #3841
Conversation
|
This change is part of the following stack:
Change managed by git-spice. |
d59ea90 to
e22f8aa
Compare
fc46288 to
8a9f925
Compare
8a9f925 to
8f3b1f6
Compare
| #[must_use] | ||
| pub fn new_with_report( | ||
| gas_used: GasVector, | ||
| call_trace: &CallTrace, | ||
| contracts_data: &ContractsDataStore, | ||
| ) -> Self { | ||
| Self::new(gas_used).collect_gas_data(call_trace, contracts_data) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wdyt about a little refactor here? Seems better to me:
#[must_use]
pub fn new_with_report(
gas_used: GasVector,
call_trace: &CallTrace,
contracts_data: &ContractsDataStore,
) -> Self {
let mut info = Self::new(gas_used);
info.collect_gas_data(call_trace, contracts_data);
info.finalize();
info
}and we don't call finalize in collect_gas_data
fn collect_gas_data(&mut self, trace: &CallTrace, contracts_data: &ContractsDataStore) {
let mut stack = trace.nested_calls.clone();
while let Some(call_trace_node) = stack.pop() {
if let CallTraceNode::EntryPointCall(call) = call_trace_node {
let call = call.borrow();
let class_hash = call.entry_point.class_hash.expect(
"class_hash should be set in `fn execute_call_entry_point` in cheatnet",
);
let contract_name = get_contract_name(contracts_data, class_hash);
let selector = get_selector(contracts_data, call.entry_point.entry_point_selector);
let gas = call
.gas_report_data
.as_ref()
.expect("Gas report data must be updated after test execution")
.get_gas();
self.update_entry(contract_name, selector, gas);
stack.extend(call.nested_calls.clone());
}
}
}| pub fn new(gas_used: GasVector) -> Self { | ||
| Self { | ||
| gas_used, | ||
| report_data: ReportData(BTreeMap::new()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bit weird to have a public constructor that doesn't have a gas report when collect_gas_data is private.
| } | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| pub struct ReportData(BTreeMap<ContractName, ContractInfo>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a point to this wrapper? It's used in a public field, but the inner BTreeMap<ContractName, ContractInfo>); is not public. So I understand it cannot be accessed by external modules anyway.
Why don't we just make the fields of GasSingleTestInfo not pub?
| fn finalize(&mut self) { | ||
| for contract_info in self.report_data.0.values_mut() { | ||
| for gas_info in contract_info.functions.values_mut() { | ||
| gas_info.gas_stats = GasStats::new(&gas_info.records); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd consider structuring this a bit different. Instead of having this struct technically accessible in non-final state, before finalized is called, I'd make it's internals private and changed the finalize method to return a new, different struct (some fields can probably be reused).
This way it is clear that this one is a "in progress" one and there's a separate struct representing a final result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can even do this on the same struct using type state pattern
pub struct NoReport;
pub struct WithReport;
#[derive(Debug, Clone)]
pub struct GasSingleTestInfo<State> {
pub gas_used: GasVector,
pub report_data: ReportData,
_state: PhantomData<State>,
}
pub fn finalize(self) -> GasSingleTestInfo<WithReport>There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd consider structuring this a bit different. Instead of having this struct technically accessible in non-final state, before finalized is called
What do you mean by technically accessible in a non-final state? It's possible to modify the fields since they are public, but there's a distinction between new and new_with_report, both return "finalized" versions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean it's bit weird for me to share the same struct for gas data with and without raport, where report_data is typed as non-optional, and finalize method is private, so depending how the struct was constructed, there's no way to move it to a state with gas report non private.
I'd either represent these different states more explicitly, use separate structs or follow the pattern @ksew1 shared.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do rest of review tomorrow as I am getting sleepy 😴
| fn finalize(&mut self) { | ||
| for contract_info in self.report_data.0.values_mut() { | ||
| for gas_info in contract_info.functions.values_mut() { | ||
| gas_info.gas_stats = GasStats::new(&gas_info.records); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can even do this on the same struct using type state pattern
pub struct NoReport;
pub struct WithReport;
#[derive(Debug, Clone)]
pub struct GasSingleTestInfo<State> {
pub gas_used: GasVector,
pub report_data: ReportData,
_state: PhantomData<State>,
}
pub fn finalize(self) -> GasSingleTestInfo<WithReport>| mod trace; | ||
| mod tree; | ||
|
|
||
| pub use contracts_data_store::ContractsDataStore; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not a biggie right now, but we are beginning to leak implementation details, and this crate is becoming entangled with something unrelated—in this case, the gas report. Maybe ContractsData should contain better information so that we can use it here, as it is already used as the base in ContractsDataStore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to move ContractsDataStore out of the debugging crate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is one of the options, but now we would have three structures: ContractsData, ForkData, and ContractsDataStore. ContractsDataStore is most complete so maybe we could delete other 2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, let's agree on and resolve the open comments
Towards #3660