Skip to content

Commit

Permalink
feat: implement gas profiler. (#3038)
Browse files Browse the repository at this point in the history
  • Loading branch information
olonho authored Aug 18, 2020
1 parent 70f4e8a commit 1db01be
Show file tree
Hide file tree
Showing 14 changed files with 449 additions and 57 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 108 additions & 0 deletions runtime/near-vm-logic/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::types::Gas;
use core::fmt;
use serde::{Deserialize, Serialize};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -486,6 +487,55 @@ pub enum ExtCosts {
validator_total_stake_base,
}

// Type of an action, used in fees logic.
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, PartialOrd, Ord)]
#[allow(non_camel_case_types)]
pub enum ActionCosts {
create_account,
delete_account,
deploy_contract,
function_call,
transfer,
stake,
add_key,
delete_key,
value_return,
new_receipt,
}

impl fmt::Display for ActionCosts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", ActionCosts::name_of(*self as usize))
}
}

impl ActionCosts {
pub const fn count() -> usize {
ActionCosts::new_receipt as usize + 1
}

pub fn name_of(index: usize) -> &'static str {
vec![
"create_account",
"delete_account",
"deploy_contract",
"function_call",
"transfer",
"stake",
"add_key",
"delete_key",
"value_return",
"new_receipt",
][index]
}
}

impl fmt::Display for ExtCosts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", ExtCosts::name_of(*self as usize))
}
}

impl ExtCosts {
pub fn value(self, config: &ExtCostsConfig) -> Gas {
use ExtCosts::*;
Expand Down Expand Up @@ -541,4 +591,62 @@ impl ExtCosts {
validator_total_stake_base => config.validator_total_stake_base,
}
}

pub const fn count() -> usize {
ExtCosts::validator_total_stake_base as usize + 1
}

pub fn name_of(index: usize) -> &'static str {
vec![
"base",
"contract_compile_base",
"contract_compile_bytes",
"read_memory_base",
"read_memory_byte",
"write_memory_base",
"write_memory_byte",
"read_register_base",
"read_register_byte",
"write_register_base",
"write_register_byte",
"utf8_decoding_base",
"utf8_decoding_byte",
"utf16_decoding_base",
"utf16_decoding_byte",
"sha256_base",
"sha256_byte",
"keccak256_base",
"keccak256_byte",
"keccak512_base",
"keccak512_byte",
"log_base",
"log_byte",
"storage_write_base",
"storage_write_key_byte",
"storage_write_value_byte",
"storage_write_evicted_byte",
"storage_read_base",
"storage_read_key_byte",
"storage_read_value_byte",
"storage_remove_base",
"storage_remove_key_byte",
"storage_remove_ret_value_byte",
"storage_has_key_base",
"storage_has_key_byte",
"storage_iter_create_prefix_base",
"storage_iter_create_prefix_byte",
"storage_iter_create_range_base",
"storage_iter_create_from_byte",
"storage_iter_create_to_byte",
"storage_iter_next_base",
"storage_iter_next_key_byte",
"storage_iter_next_value_byte",
"touching_trie_node",
"promise_and_base",
"promise_and_per_promise",
"promise_return",
"validator_stake_base",
"validator_total_stake_base",
][index]
}
}
98 changes: 86 additions & 12 deletions runtime/near-vm-logic/src/gas_counter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::config::{ExtCosts, ExtCostsConfig};
use crate::types::Gas;
use crate::config::{ActionCosts, ExtCosts, ExtCostsConfig};
use crate::types::{Gas, ProfileData};
use crate::{HostError, VMLogicError};
use near_runtime_fees::Fee;

Expand All @@ -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>,
}

impl GasCounter {
Expand All @@ -30,11 +32,20 @@ impl GasCounter {
max_gas_burnt: Gas,
prepaid_gas: Gas,
is_view: bool,
profile: Option<ProfileData>,
) -> Self {
Self { ext_costs_config, burnt_gas: 0, used_gas: 0, max_gas_burnt, prepaid_gas, is_view }
Self {
ext_costs_config,
burnt_gas: 0,
used_gas: 0,
max_gas_burnt,
prepaid_gas,
is_view,
profile,
}
}

pub fn deduct_gas(&mut self, burn_gas: Gas, use_gas: Gas) -> Result<()> {
fn deduct_gas(&mut self, burn_gas: Gas, use_gas: Gas) -> Result<()> {
assert!(burn_gas <= use_gas);
let new_burnt_gas =
self.burnt_gas.checked_add(burn_gas).ok_or(HostError::IntegerOverflow)?;
Expand Down Expand Up @@ -64,29 +75,64 @@ 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| {
*f.borrow_mut().entry(cost).or_default() += value;
});
}

#[cfg(not(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) {}

#[cfg(feature = "costs_counting")]
#[inline]
fn update_profile_host(&mut self, cost: ExtCosts, value: u64) {
match &self.profile {
Some(profile) => *profile.borrow_mut().get_mut(cost as usize).unwrap() += value,
None => {}
};
}

#[cfg(not(feature = "costs_counting"))]
#[inline]
fn update_profile_host(&mut self, cost: ExtCosts, _value: u64) {}

#[cfg(feature = "costs_counting")]
#[inline]
fn update_profile_action(&mut self, action: ActionCosts, value: u64) {
match &self.profile {
Some(profile) => {
*profile.borrow_mut().get_mut(action as usize + ExtCosts::count()).unwrap() += value
}
None => {}
};
}

#[cfg(not(feature = "costs_counting"))]
#[inline]
fn update_profile_action(&mut self, action: ActionCosts, _value: u64) {}

pub fn pay_wasm_gas(&mut self, value: u64) -> Result<()> {
self.deduct_gas(value, value)
}

/// A helper function to pay per byte gas
pub fn pay_per_byte(&mut self, cost: ExtCosts, num_bytes: u64) -> Result<()> {
self.inc_ext_costs_counter(cost, num_bytes);
let use_gas = num_bytes
.checked_mul(cost.value(&self.ext_costs_config))
.ok_or(HostError::IntegerOverflow)?;

self.inc_ext_costs_counter(cost, num_bytes);
self.update_profile_host(cost, use_gas);
self.deduct_gas(use_gas, use_gas)
}

/// A helper function to pay base cost gas
pub fn pay_base(&mut self, cost: ExtCosts) -> Result<()> {
self.inc_ext_costs_counter(cost, 1);
let base_fee = cost.value(&self.ext_costs_config);
self.inc_ext_costs_counter(cost, 1);
self.update_profile_host(cost, base_fee);
self.deduct_gas(base_fee, base_fee)
}

Expand All @@ -95,11 +141,13 @@ impl GasCounter {
/// * `per_byte_fee`: the fee per byte;
/// * `num_bytes`: the number of bytes;
/// * `sir`: whether the receiver_id is same as the current account ID;
/// * `action`: what kind of action is charged for;
pub fn pay_action_per_byte(
&mut self,
per_byte_fee: &Fee,
num_bytes: u64,
sir: bool,
action: ActionCosts,
) -> Result<()> {
let burn_gas =
num_bytes.checked_mul(per_byte_fee.send_fee(sir)).ok_or(HostError::IntegerOverflow)?;
Expand All @@ -108,21 +156,47 @@ 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;
pub fn pay_action_base(&mut self, base_fee: &Fee, sir: bool) -> Result<()> {
/// * `action`: what kind of action is charged for;
pub fn pay_action_base(
&mut self,
base_fee: &Fee,
sir: bool,
action: ActionCosts,
) -> Result<()> {
let burn_gas = base_fee.send_fee(sir);
let use_gas =
burn_gas.checked_add(base_fee.exec_fee()).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:
/// * `burn_gas`: amount of gas to burn;
/// * `use_gas`: amount of gas to reserve;
/// * `action`: what kind of action is charged for;
pub fn pay_action_accumulated(
&mut self,
burn_gas: Gas,
use_gas: Gas,
action: ActionCosts,
) -> Result<()> {
self.update_profile_action(action, burn_gas);
self.deduct_gas(burn_gas, use_gas)
}

pub fn prepay_gas(&mut self, use_gas: Gas) -> Result<()> {
self.deduct_gas(0, use_gas)
}

pub fn burnt_gas(&self) -> Gas {
self.burnt_gas
}
Expand All @@ -136,7 +210,7 @@ mod tests {
use super::*;
#[test]
fn test_deduct_gas() {
let mut counter = GasCounter::new(ExtCostsConfig::default(), 10, 10, false);
let mut counter = GasCounter::new(ExtCostsConfig::default(), 10, 10, false, None);
counter.deduct_gas(5, 10).expect("deduct_gas should work");
assert_eq!(counter.burnt_gas(), 5);
assert_eq!(counter.used_gas(), 10);
Expand All @@ -145,7 +219,7 @@ mod tests {
#[test]
#[should_panic]
fn test_prepaid_gas_min() {
let mut counter = GasCounter::new(ExtCostsConfig::default(), 100, 10, false);
let mut counter = GasCounter::new(ExtCostsConfig::default(), 100, 10, false, None);
counter.deduct_gas(10, 5).unwrap();
}
}
2 changes: 1 addition & 1 deletion runtime/near-vm-logic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub mod serde_with;
pub mod types;
mod utils;

pub use config::{ExtCosts, ExtCostsConfig, VMConfig, VMKind, VMLimitConfig};
pub use config::{ActionCosts, ExtCosts, ExtCostsConfig, VMConfig, VMKind, VMLimitConfig};
pub use context::VMContext;
pub use dependencies::{External, MemoryLike, ValuePtr};
pub use logic::{VMLogic, VMOutcome};
Expand Down
Loading

0 comments on commit 1db01be

Please sign in to comment.