-
Notifications
You must be signed in to change notification settings - Fork 637
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
feat: implement gas profiler. #3038
Conversation
@@ -22,16 +22,19 @@ pub struct GasCounter { | |||
prepaid_gas: Gas, | |||
is_view: bool, | |||
ext_costs_config: ExtCostsConfig, | |||
/// Where to store profile data, if needed. | |||
profile: Option<&'a mut Vec<u64>>, |
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.
Please hide it behind costs_counting
flag and consider Rc<RefCell<Vec<u64>>
also to avoid lifetime poisoning (when introduction of lifetime into one type leads to introduction of it in dependent types).
runtime/near-vm-logic/Cargo.toml
Outdated
@@ -19,6 +19,8 @@ base64 = "0.11" | |||
serde = { version = "1", features = ["derive"] } | |||
sha2 = "0.8" | |||
sha3 = "0.8" | |||
strum = "0.18" |
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.
Let's not add more dependencies, we are trying to keep near-vm-logic
minimal because we have plans of compiling it to Wasm. Also strum
is not a widely used library, let's implement enum iterator manually here.
|
||
near-vm-logic = { path = "../near-vm-logic", version = "1.0.0"} | ||
near-vm-runner = { path = "../near-vm-runner", version = "1.0.0" } | ||
near-vm-runner = { path = "../near-vm-runner", version = "1.0.0", features = ["wasmtime_vm"] } |
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.
Typo?
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.
Nope, intentional, so that we could run in standalone mode with both VMs.
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, maybe more helpful to output number as space seperate columns at each line begining, so unix sort
command can list cost from max to minimum easier
Codecov Report
@@ Coverage Diff @@
## master #3038 +/- ##
=======================================
Coverage 87.82% 87.82%
=======================================
Files 213 213
Lines 42377 42377
=======================================
Hits 37219 37219
Misses 5158 5158 Continue to review full report at Codecov.
|
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 overall. There are some comments.
@@ -64,29 +75,81 @@ impl GasCounter { | |||
|
|||
#[cfg(feature = "costs_counting")] | |||
#[inline] | |||
fn inc_ext_costs_counter(&self, cost: ExtCosts, value: u64) { | |||
fn inc_ext_costs_counter(&mut self, cost: ExtCosts, value: u64) { | |||
EXT_COSTS_COUNTER.with(|f| { |
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 thought you suggested to remove EXT_COSTS_COUNTER
because it is an expensive thread-local tool. If we have profile
that it looks like it should be easy to remove EXT_COSTS_COUNTER
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.
Well, they have different semantics with profile (profile sums costs, EXT_COSTS_COUNTER counts invocations) and EXT_COSTS_COUNTER id being used in the cost estimator, so I decided to keep it.
self.deduct_gas(burn_gas, use_gas) | ||
} | ||
|
||
/// A helper function to pay base cost gas fee for batching an action. | ||
/// # Args: | ||
/// * `base_fee`: base fee for the action; | ||
/// * `sir`: whether the receiver_id is same as the current account ID; | ||
pub fn pay_action_base(&mut self, base_fee: &Fee, sir: bool) -> Result<()> { | ||
pub fn pay_action_base(&mut self, base_fee: &Fee, sir: bool, action: Actions) -> 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.
We should just change pay_action_base
and pay_acion_per_byte
so that it takes action
but does not take base_fee
and then store RuntimeFeesConfig
in GasCounter
just like we store ExtCostsConfig
. Then we would need to implement value()
method for Actions
just like we did it here: https://github.com/nearprotocol/nearcore/blob/f024a28d9b0f1b2238f04d288ddebc8ac12ff4d1/runtime/near-vm-logic/src/config.rs#L490
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.
This is rather major change, do you believe we shall do that in this (somewhat unrelated) profiler change?
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.
Let's create a tracking issue for it and fix it later.
ExtCosts::validator_total_stake_base as usize + 1 | ||
} | ||
|
||
pub fn name_of(index: usize) -> &'static str { |
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.
Let's implement Display
trait instead.
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 shall implement Display
for which class? Note, that we have usize
, not enum as an input, and usize
-> ExtCosts
conversion is not straightforward.
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.
and usize -> ExtCosts conversion is not straightforward.
Why not? It should be a straightforward cast.
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.
int cannot be casted to enum, as far, as I could see.
runtime/near-vm-logic/src/config.rs
Outdated
@@ -541,4 +577,62 @@ impl ExtCosts { | |||
validator_total_stake_base => config.validator_total_stake_base, | |||
} | |||
} | |||
|
|||
pub fn count() -> usize { |
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.
Let's try to make it const
: https://doc.rust-lang.org/reference/items/functions.html#const-functions
runtime/near-vm-logic/src/types.rs
Outdated
@@ -35,3 +37,5 @@ pub enum PromiseResult { | |||
Successful(Vec<u8>), | |||
Failed, | |||
} | |||
|
|||
pub type ProfileData = Rc<RefCell<Vec<u64>>>; |
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.
Could we try Rc<RefCell<[u8; Actions::count() + ExtCosts::count() + 1]>>
once count()
is const
?
@@ -22,6 +22,8 @@ pub struct GasCounter { | |||
prepaid_gas: Gas, | |||
is_view: bool, | |||
ext_costs_config: ExtCostsConfig, | |||
/// Where to store profile data, if needed. |
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.
If I understood you correctly @olonho you said that it creates a negligible overhead if so then we can try to drop costs_counting
flag once we get rid of the thread local static variable.
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.
Not sure if we could, see comment above. It's used in https://github.com/nearprotocol/nearcore/blob/f024a28d9b0f1b2238f04d288ddebc8ac12ff4d1/runtime/runtime-params-estimator/src/stats.rs#L29.
"serde", | ||
"serde_json", | ||
"strum", |
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 does not look like strum is used anymore.
"serde", | ||
"serde_json", | ||
"strum", |
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 does not look like strum is used anymore.
If sorting is needed, we'd better do that in code, but I guess it's not too big so that we could see relevant parts w/o sorting. |
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.
Approving, let's create a refactoring cleanup issue for tracking.
ExtCosts::validator_total_stake_base as usize + 1 | ||
} | ||
|
||
pub fn name_of(index: usize) -> &'static str { |
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.
and usize -> ExtCosts conversion is not straightforward.
Why not? It should be a straightforward cast.
runtime/near-vm-logic/src/logic.rs
Outdated
@@ -864,7 +867,7 @@ impl<'a> VMLogic<'a> { | |||
.ok_or(HostError::IntegerOverflow)?; | |||
} | |||
use_gas = use_gas.checked_add(burn_gas).ok_or(HostError::IntegerOverflow)?; | |||
self.gas_counter.deduct_gas(burn_gas, use_gas) | |||
self.gas_counter.pay_action_accumulated(burn_gas, use_gas, Actions::new_receipt) |
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.
Hm, I might be wrong, but doesn't this has all the unused gas as well?
E.g. if we attach 100Tgas and use 20Tgas, this will be 80% usage because it will record 80 Tgas?
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 only records burnt gas in the profiler.
runtime/near-vm-logic/src/config.rs
Outdated
write!( | ||
f, | ||
"{}", | ||
vec![ |
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 it possible to reuse name_of
?
runtime/near-vm-logic/src/config.rs
Outdated
write!( | ||
f, | ||
"{}", | ||
vec![ |
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.
Maybe also reuse name_of
runtime/near-vm-logic/src/config.rs
Outdated
// Type of an action, used in fees logic. | ||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, PartialOrd, Ord)] | ||
#[allow(non_camel_case_types)] | ||
pub enum Actions { |
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 don't like the name of it, because it's confusing to Action
. Is it possible to rename it to ActionCosts
or ActionProfiler
or something similar to indicate it's not an Action
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.
Also it doesn't have to be public. Can be pub(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's exported to standalone runner, so not sure if could be crate pub.
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.
This type is used outside of the crate, namely in standalone launcher.
runtime/near-vm-logic/src/types.rs
Outdated
@@ -35,3 +38,6 @@ pub enum PromiseResult { | |||
Successful(Vec<u8>), | |||
Failed, | |||
} | |||
|
|||
/// Profile of gas consumption, +1 for Wasm bytecode execution cost. | |||
pub type ProfileData = Rc<RefCell<[u64; Actions::count() + ExtCosts::count() + 1]>>; |
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.
Can we hide it behind the feature as well? It seems like this introduces RefCell
and Rc
towards the vm-logic
from std
, so this creates the dependencies.
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.
As this type is used in signatures, it will clutter all code using it with the feature. Original code didn't use RefCell
/ Rc
but had explicit lifetimes, and Max requested to rewrite it. What would we consider least evil?
@@ -22,6 +22,8 @@ pub struct GasCounter { | |||
prepaid_gas: Gas, | |||
is_view: bool, | |||
ext_costs_config: ExtCostsConfig, | |||
/// Where to store profile data, if needed. | |||
profile: Option<ProfileData>, |
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.
Instead of put it into the constructor, can you put it using setProfile
method. This way it can be behind a feature and doesn't affect existing implementations.
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.
But is it really correct for the cost breakdown be behind the flag?
action_gas += profile_data[e as usize + ExtCosts::count()]; | ||
} | ||
|
||
let wasm_gas = profile_data[Actions::count() + ExtCosts::count()]; |
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.
After some thought it might be good if we had the option to write to JSON file.
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.
Well, maybe, but not sure what usecase would be.
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.
Well currently in near-sdk-as, it uses the rust binary and expects the output to be the JSON of the outcome. It is possible to work around this, but it would be a lot simpler if the only output is the outcome.
PTAL |
@@ -108,21 +172,41 @@ impl GasCounter { | |||
num_bytes.checked_mul(per_byte_fee.exec_fee()).ok_or(HostError::IntegerOverflow)?, | |||
) | |||
.ok_or(HostError::IntegerOverflow)?; | |||
|
|||
self.update_profile_action(action, burn_gas); | |||
self.deduct_gas(burn_gas, use_gas) | |||
} | |||
|
|||
/// A helper function to pay base cost gas fee for batching an action. | |||
/// # Args: | |||
/// * `base_fee`: base fee for the action; | |||
/// * `sir`: whether the receiver_id is same as the current account ID; |
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.
Please update comments
self.deduct_gas(burn_gas, use_gas) | ||
} | ||
|
||
pub fn pay_action_accumulated( |
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.
Add a comment
fn update_profile_wasm(&mut self, _value: u64) {} | ||
|
||
pub fn pay_wasm_gas(&mut self, value: u64) -> Result<()> { | ||
self.update_profile_wasm(value); |
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.
We should remove this and use difference from total
Implement profiler for gas consumption.
Command like
produces