Skip to content

Commit

Permalink
More benchmarks & experiments, refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinonard committed Nov 2, 2023
1 parent c47d36e commit d36839c
Show file tree
Hide file tree
Showing 8 changed files with 490 additions and 307 deletions.
19 changes: 15 additions & 4 deletions pallets/dapp-staking-v3/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ pub(crate) fn advance_to_next_era<T: Config>() {
// }

// /// Advance blocks until next period type has been reached.
// pub(crate) fn advance_to_next_period_type<T: Config>() {
// let period_type = ActiveProtocolState::<T>::get().period_type();
// while ActiveProtocolState::<T>::get().period_type() == period_type {
// pub(crate) fn advance_to_next_subperiod<T: Config>() {
// let subperiod = ActiveProtocolState::<T>::get().subperiod();
// while ActiveProtocolState::<T>::get().subperiod() == subperiod {
// run_for_blocks::<T>(One::one());
// }
// }
Expand All @@ -99,7 +99,7 @@ pub fn initial_config<T: Config>() {
next_era_start: era_length.saturating_mul(voting_period_length_in_eras.into()) + One::one(),
period_info: PeriodInfo {
number: 1,
period_type: PeriodType::Voting,
subperiod: Subperiod::Voting,
ending_era: 2,
},
maintenance: false,
Expand Down Expand Up @@ -217,6 +217,17 @@ mod benchmarks {
}
}

#[benchmark]
fn experimental_read() {
// Prepare init config (protocol state, tier params & config, etc.)
initial_config::<T>();

#[block]
{
let _ = ExperimentalContractEntries::<T>::get(10);
}
}

impl_benchmark_test_suite!(
Pallet,
crate::benchmarking::tests::new_test_ext(),
Expand Down
55 changes: 38 additions & 17 deletions pallets/dapp-staking-v3/src/dsv3_weight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! Autogenerated weights for pallet_dapp_staking_v3
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-10-30, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2023-11-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `Dinos-MBP`, CPU: `<UNKNOWN>`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
Expand Down Expand Up @@ -50,6 +50,7 @@ use core::marker::PhantomData;
/// Weight functions needed for pallet_dapp_staking_v3.
pub trait WeightInfo {
fn dapp_tier_assignment(x: u32, ) -> Weight;
fn experimental_read() -> Weight;
}

/// Weights for pallet_dapp_staking_v3 using the Substrate node and recommended hardware.
Expand All @@ -62,19 +63,29 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// Storage: DappStaking IntegratedDApps (r:101 w:0)
/// Proof: DappStaking IntegratedDApps (max_values: None, max_size: Some(121), added: 2596, mode: MaxEncodedLen)
/// Storage: DappStaking ContractStake (r:100 w:0)
/// Proof: DappStaking ContractStake (max_values: None, max_size: Some(170), added: 2645, mode: MaxEncodedLen)
/// Proof: DappStaking ContractStake (max_values: None, max_size: Some(130), added: 2605, mode: MaxEncodedLen)
/// The range of component `x` is `[0, 100]`.
fn dapp_tier_assignment(x: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `823 + x * (164 ±0)`
// Estimated: `3586 + x * (2645 ±0)`
// Minimum execution time: 8_000_000 picoseconds.
Weight::from_parts(12_342_575, 3586)
// Standard Error: 16_840
.saturating_add(Weight::from_parts(7_051_078, 0).saturating_mul(x.into()))
// Measured: `836 + x * (169 ±0)`
// Estimated: `3586 + x * (2605 ±0)`
// Minimum execution time: 9_000_000 picoseconds.
Weight::from_parts(12_879_631, 3586)
// Standard Error: 18_480
.saturating_add(Weight::from_parts(7_315_677, 0).saturating_mul(x.into()))
.saturating_add(T::DbWeight::get().reads(3_u64))
.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(x.into())))
.saturating_add(Weight::from_parts(0, 2645).saturating_mul(x.into()))
.saturating_add(Weight::from_parts(0, 2605).saturating_mul(x.into()))
}
/// Storage: DappStaking ExperimentalContractEntries (r:1 w:0)
/// Proof: DappStaking ExperimentalContractEntries (max_values: None, max_size: Some(3483), added: 5958, mode: MaxEncodedLen)
fn experimental_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `224`
// Estimated: `6948`
// Minimum execution time: 5_000_000 picoseconds.
Weight::from_parts(5_000_000, 6948)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
}

Expand All @@ -87,18 +98,28 @@ impl WeightInfo for () {
/// Storage: DappStaking IntegratedDApps (r:101 w:0)
/// Proof: DappStaking IntegratedDApps (max_values: None, max_size: Some(121), added: 2596, mode: MaxEncodedLen)
/// Storage: DappStaking ContractStake (r:100 w:0)
/// Proof: DappStaking ContractStake (max_values: None, max_size: Some(170), added: 2645, mode: MaxEncodedLen)
/// Proof: DappStaking ContractStake (max_values: None, max_size: Some(130), added: 2605, mode: MaxEncodedLen)
/// The range of component `x` is `[0, 100]`.
fn dapp_tier_assignment(x: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `823 + x * (164 ±0)`
// Estimated: `3586 + x * (2645 ±0)`
// Minimum execution time: 8_000_000 picoseconds.
Weight::from_parts(12_342_575, 3586)
// Standard Error: 16_840
.saturating_add(Weight::from_parts(7_051_078, 0).saturating_mul(x.into()))
// Measured: `836 + x * (169 ±0)`
// Estimated: `3586 + x * (2605 ±0)`
// Minimum execution time: 9_000_000 picoseconds.
Weight::from_parts(12_879_631, 3586)
// Standard Error: 18_480
.saturating_add(Weight::from_parts(7_315_677, 0).saturating_mul(x.into()))
.saturating_add(RocksDbWeight::get().reads(3_u64))
.saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(x.into())))
.saturating_add(Weight::from_parts(0, 2645).saturating_mul(x.into()))
.saturating_add(Weight::from_parts(0, 2605).saturating_mul(x.into()))
}
/// Storage: DappStaking ExperimentalContractEntries (r:1 w:0)
/// Proof: DappStaking ExperimentalContractEntries (max_values: None, max_size: Some(3483), added: 5958, mode: MaxEncodedLen)
fn experimental_read() -> Weight {
// Proof Size summary in bytes:
// Measured: `224`
// Estimated: `6948`
// Minimum execution time: 5_000_000 picoseconds.
Weight::from_parts(5_000_000, 6948)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
}
56 changes: 35 additions & 21 deletions pallets/dapp-staking-v3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ pub mod pallet {
NewEra { era: EraNumber },
/// New period has started.
NewPeriod {
period_type: PeriodType,
subperiod: Subperiod,
number: PeriodNumber,
},
/// A smart contract has been registered for dApp staking
Expand Down Expand Up @@ -416,6 +416,11 @@ pub mod pallet {
pub type DAppTiers<T: Config> =
StorageMap<_, Twox64Concat, EraNumber, DAppTierRewardsFor<T>, OptionQuery>;

// TODO: this is experimental, please don't review
#[pallet::storage]
pub type ExperimentalContractEntries<T: Config> =
StorageMap<_, Twox64Concat, EraNumber, ContractEntriesFor<T>, OptionQuery>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig {
Expand Down Expand Up @@ -473,7 +478,7 @@ pub mod pallet {
next_era_start: Pallet::<T>::blocks_per_voting_period() + 1_u32.into(),
period_info: PeriodInfo {
number: 1,
period_type: PeriodType::Voting,
subperiod: Subperiod::Voting,
ending_era: 2,
},
maintenance: false,
Expand Down Expand Up @@ -510,8 +515,8 @@ pub mod pallet {

let current_era = protocol_state.era;
let next_era = current_era.saturating_add(1);
let (maybe_period_event, era_reward) = match protocol_state.period_type() {
PeriodType::Voting => {
let (maybe_period_event, era_reward) = match protocol_state.subperiod() {
Subperiod::Voting => {
// For the sake of consistency, we put zero reward into storage
let era_reward = EraReward {
staker_reward_pool: Balance::zero(),
Expand All @@ -523,23 +528,23 @@ pub mod pallet {
next_era.saturating_add(T::StandardErasPerBuildAndEarnPeriod::get());
let build_and_earn_start_block =
now.saturating_add(T::StandardEraLength::get());
protocol_state.into_next_period_type(ending_era, build_and_earn_start_block);
protocol_state.into_next_subperiod(ending_era, build_and_earn_start_block);

era_info.migrate_to_next_era(Some(protocol_state.period_type()));
era_info.migrate_to_next_era(Some(protocol_state.subperiod()));

// Update tier configuration to be used when calculating rewards for the upcoming eras
let next_tier_config = NextTierConfig::<T>::take();
TierConfig::<T>::put(next_tier_config);

(
Some(Event::<T>::NewPeriod {
period_type: protocol_state.period_type(),
subperiod: protocol_state.subperiod(),
number: protocol_state.period_number(),
}),
era_reward,
)
}
PeriodType::BuildAndEarn => {
Subperiod::BuildAndEarn => {
let (staker_reward_pool, dapp_reward_pool) =
T::RewardPoolProvider::normal_reward_pools();
let era_reward = EraReward {
Expand All @@ -564,7 +569,7 @@ pub mod pallet {
&protocol_state.period_number(),
PeriodEndInfo {
bonus_reward_pool,
total_vp_stake: era_info.staked_amount(PeriodType::Voting),
total_vp_stake: era_info.staked_amount(Subperiod::Voting),
final_era: current_era,
},
);
Expand All @@ -575,9 +580,9 @@ pub mod pallet {
let voting_period_length = Self::blocks_per_voting_period();
let next_era_start_block = now.saturating_add(voting_period_length);

protocol_state.into_next_period_type(ending_era, next_era_start_block);
protocol_state.into_next_subperiod(ending_era, next_era_start_block);

era_info.migrate_to_next_era(Some(protocol_state.period_type()));
era_info.migrate_to_next_era(Some(protocol_state.subperiod()));

// Re-calculate tier configuration for the upcoming new period
let tier_params = StaticTierParams::<T>::get();
Expand All @@ -588,7 +593,7 @@ pub mod pallet {

(
Some(Event::<T>::NewPeriod {
period_type: protocol_state.period_type(),
subperiod: protocol_state.subperiod(),
number: protocol_state.period_number(),
}),
era_reward,
Expand Down Expand Up @@ -1045,12 +1050,12 @@ pub mod pallet {
None => (
SingularStakingInfo::new(
protocol_state.period_number(),
protocol_state.period_type(),
protocol_state.subperiod(),
),
true,
),
};
new_staking_info.stake(amount, protocol_state.period_type());
new_staking_info.stake(amount, protocol_state.subperiod());
ensure!(
new_staking_info.total_staked_amount() >= T::MinimumStakeAmount::get(),
Error::<T>::InsufficientStakeAmount
Expand All @@ -1072,7 +1077,7 @@ pub mod pallet {
// 4.
// Update total staked amount for the next era.
CurrentEraInfo::<T>::mutate(|era_info| {
era_info.add_stake_amount(amount, protocol_state.period_type());
era_info.add_stake_amount(amount, protocol_state.subperiod());
});

// 5.
Expand Down Expand Up @@ -1139,7 +1144,7 @@ pub mod pallet {
amount
};

staking_info.unstake(amount, protocol_state.period_type());
staking_info.unstake(amount, protocol_state.subperiod());
(staking_info, amount)
}
None => {
Expand Down Expand Up @@ -1170,7 +1175,7 @@ pub mod pallet {
// 4.
// Update total staked amount for the next era.
CurrentEraInfo::<T>::mutate(|era_info| {
era_info.unstake_amount(amount, protocol_state.period_type());
era_info.unstake_amount(amount, protocol_state.subperiod());
});

// 5.
Expand Down Expand Up @@ -1323,7 +1328,7 @@ pub mod pallet {
Error::<T>::InternalClaimBonusError
);

let eligible_amount = staker_info.staked_amount(PeriodType::Voting);
let eligible_amount = staker_info.staked_amount(Subperiod::Voting);
let bonus_reward =
Perbill::from_rational(eligible_amount, period_end_info.total_vp_stake)
* period_end_info.bonus_reward_pool;
Expand Down Expand Up @@ -1380,7 +1385,6 @@ pub mod pallet {
})?;

// Get reward destination, and deposit the reward.
// TODO: should we check reward is greater than zero, or even more precise, it's greater than the existential deposit? Seems redundant but still...
let beneficiary = dapp_info.reward_beneficiary();
T::Currency::deposit_creating(beneficiary, amount);

Expand Down Expand Up @@ -1447,7 +1451,7 @@ pub mod pallet {
// Update total staked amount for the next era.
// This means 'fake' stake total amount has been kept until now, even though contract was unregistered.
CurrentEraInfo::<T>::mutate(|era_info| {
era_info.unstake_amount(amount, protocol_state.period_type());
era_info.unstake_amount(amount, protocol_state.subperiod());
});

// Update remaining storage entries
Expand Down Expand Up @@ -1517,7 +1521,7 @@ pub mod pallet {

match force_type {
ForcingType::Era => (),
ForcingType::PeriodType => {
ForcingType::Subperiod => {
state.period_info.ending_era = state.era.saturating_add(1);
}
}
Expand Down Expand Up @@ -1608,6 +1612,13 @@ pub mod pallet {
// Even without async backing though, we should have enough capacity to handle this.
// UPDATE: might work with async backing, but right now we could handle up to 150 dApps before exceeding the PoV size.

// UPDATE2: instead of taking the approach of reading an ever increasing amount of entries from storage, we can instead adopt an approach
// of eficiently storing composite information into `BTreeMap`. The approach is essentially the same as the one used below to store rewards.
// Each time `stake` or `unstake` are called, corresponding entries are updated. This way we can keep track of all the contract stake in a single DB entry.
// To make the solution more scalable, we could 'split' stake entries into spans, similar as rewards are handled now.
//
// Experiment with an 'experimental' entry shows PoV size of ~7kB induced for entry that can hold up to 100 entries.

let tier_config = TierConfig::<T>::get();
let mut dapp_stakes = Vec::with_capacity(IntegratedDApps::<T>::count() as usize);

Expand Down Expand Up @@ -1676,6 +1687,9 @@ pub mod pallet {

// 4.
// Sort by dApp ID, in ascending order (unstable sort should be faster, and stability is guaranteed due to lack of duplicated Ids).
// TODO & Idea: perhaps use BTreeMap instead? It will "sort" automatically based on dApp Id, and we can efficiently remove entries,
// reducing PoV size step by step.
// It's a trade-off between speed and PoV size. Although both are quite minor, so maybe it doesn't matter that much.
dapp_tiers.sort_unstable_by(|first, second| first.dapp_id.cmp(&second.dapp_id));

// 5. Calculate rewards.
Expand Down
8 changes: 4 additions & 4 deletions pallets/dapp-staking-v3/src/test/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl ExtBuilder {
next_era_start: era_length.saturating_mul(voting_period_length_in_eras.into()) + 1,
period_info: PeriodInfo {
number: 1,
period_type: PeriodType::Voting,
subperiod: Subperiod::Voting,
ending_era: 2,
},
maintenance: false,
Expand Down Expand Up @@ -304,9 +304,9 @@ pub(crate) fn advance_to_next_period() {
}

/// Advance blocks until next period type has been reached.
pub(crate) fn advance_to_into_next_period_type() {
let period_type = ActiveProtocolState::<Test>::get().period_type();
while ActiveProtocolState::<Test>::get().period_type() == period_type {
pub(crate) fn advance_to_into_next_subperiod() {
let subperiod = ActiveProtocolState::<Test>::get().subperiod();
while ActiveProtocolState::<Test>::get().subperiod() == subperiod {
run_for_blocks(1);
}
}
Expand Down
Loading

0 comments on commit d36839c

Please sign in to comment.