diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index c29a3ebc176a4..6a25a278f2c78 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -554,6 +554,19 @@ sp_npos_elections::generate_solution_type!( pub const MAX_NOMINATIONS: u32 = ::LIMIT as u32; +/// The numbers configured here should always be more than the the maximum limits of staking pallet +/// to ensure election snapshot will not run out of memory. +pub struct BenchmarkConfig; +impl pallet_election_provider_multi_phase::BenchmarkingConfig for BenchmarkConfig { + const VOTERS: [u32; 2] = [5_000, 10_000]; + const TARGETS: [u32; 2] = [1_000, 2_000]; + const ACTIVE_VOTERS: [u32; 2] = [1000, 4_000]; + const DESIRED_TARGETS: [u32; 2] = [400, 800]; + const SNAPSHOT_MAXIMUM_VOTERS: u32 = 25_000; + const MINER_MAXIMUM_VOTERS: u32 = 15_000; + const MAXIMUM_TARGETS: u32 = 2000; +} + impl pallet_election_provider_multi_phase::Config for Runtime { type Event = Event; type Currency = Balances; @@ -579,7 +592,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime { type Fallback = Fallback; type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight; type ForceOrigin = EnsureRootOrHalfCouncil; - type BenchmarkingConfig = (); + type BenchmarkingConfig = BenchmarkConfig; } parameter_types! { @@ -1578,7 +1591,6 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_uniques, Uniques); add_benchmark!(params, batches, pallet_utility, Utility); add_benchmark!(params, batches, pallet_vesting, Vesting); - add_benchmark!(params, batches, pallet_election_provider_multi_phase, ElectionProviderMultiPhase); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok((batches, storage_info)) diff --git a/client/allocator/src/freeing_bump.rs b/client/allocator/src/freeing_bump.rs index 0f3639803f1b3..7f83576aedfa6 100644 --- a/client/allocator/src/freeing_bump.rs +++ b/client/allocator/src/freeing_bump.rs @@ -38,9 +38,9 @@ //! allocation size is capped, therefore the number of orders and thus the linked lists is as well //! limited. Currently, the maximum size of an allocation is 32 MiB. //! -//! When the allocator serves an allocation request it first checks the linked list for the respective -//! order. If it doesn't have any free chunks, the allocator requests memory from the bump allocator. -//! In any case the order is stored in the header of the allocation. +//! When the allocator serves an allocation request it first checks the linked list for the +//! respective order. If it doesn't have any free chunks, the allocator requests memory from the +//! bump allocator. In any case the order is stored in the header of the allocation. //! //! Upon deallocation we get the order of the allocation from its header and then add that //! allocation to the linked list for the respective order. @@ -59,12 +59,13 @@ //! allocator was consumed by the 32 MiB allocation, allocations of all sizes except 32 MiB will //! fail. //! -//! - Sizes of allocations are rounded up to the nearest order. That is, an allocation of 2,00001 MiB -//! will be put into the bucket of 4 MiB. Therefore, any allocation of size `(N, 2N]` will take -//! up to `2N`, thus assuming a uniform distribution of allocation sizes, the average amount in use -//! of a `2N` space on the heap will be `(3N + ε) / 2`. So average utilisation is going to be around -//! 75% (`(3N + ε) / 2 / 2N`) meaning that around 25% of the space in allocation will be wasted. -//! This is more pronounced (in terms of absolute heap amounts) with larger allocation sizes. +//! - Sizes of allocations are rounded up to the nearest order. That is, an allocation of 2,00001 +//! MiB will be put into the bucket of 4 MiB. Therefore, any allocation of size `(N, 2N]` will +//! take up to `2N`, thus assuming a uniform distribution of allocation sizes, the average amount +//! in use of a `2N` space on the heap will be `(3N + ε) / 2`. So average utilization is going to +//! be around 75% (`(3N + ε) / 2 / 2N`) meaning that around 25% of the space in allocation will be +//! wasted. This is more pronounced (in terms of absolute heap amounts) with larger allocation +//! sizes. use crate::Error; use std::{mem, convert::{TryFrom, TryInto}, ops::{Range, Index, IndexMut}}; diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 7988163e98f65..f73ead376d5e2 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -20,10 +20,9 @@ use super::*; use crate::{Pallet as MultiPhase, unsigned::IndexAssignmentOf}; use frame_benchmarking::{account, impl_benchmark_test_suite}; -use frame_support::{assert_ok, traits::OnInitialize}; +use frame_support::{assert_ok, traits::Hooks}; use frame_system::RawOrigin; use rand::{prelude::SliceRandom, rngs::SmallRng, SeedableRng}; -use frame_election_provider_support::Assignment; use sp_arithmetic::{per_things::Percent, traits::One}; use sp_npos_elections::IndexAssignment; use sp_runtime::InnerOf; @@ -38,14 +37,14 @@ fn solution_with_size( size: SolutionOrSnapshotSize, active_voters_count: u32, desired_targets: u32, -) -> RawSolution> { - assert!(size.targets >= desired_targets, "must have enough targets"); - assert!( +) -> Result>, &'static str> { + ensure!(size.targets >= desired_targets, "must have enough targets"); + ensure!( size.targets >= (>::LIMIT * 2) as u32, "must have enough targets for unique votes." ); - assert!(size.voters >= active_voters_count, "must have enough voters"); - assert!( + ensure!(size.voters >= active_voters_count, "must have enough voters"); + ensure!( (>::LIMIT as u32) < desired_targets, "must have enough winners to give them votes." ); @@ -125,7 +124,7 @@ fn solution_with_size( .map(|(voter, _stake, votes)| { let percent_per_edge: InnerOf> = (100 / votes.len()).try_into().unwrap_or_else(|_| panic!("failed to convert")); - Assignment { + crate::unsigned::Assignment:: { who: voter.clone(), distribution: votes .iter() @@ -141,7 +140,31 @@ fn solution_with_size( let round = >::round(); assert!(score[0] > 0, "score is zero, this probably means that the stakes are not set."); - RawSolution { compact, score, round } + Ok(RawSolution { compact, score, round }) +} + +fn set_up_data_provider(v: u32, t: u32) { + // number of votes in snapshot. + + T::DataProvider::clear(); + log!(info, "setting up with voters = {} [degree = {}], targets = {}", v, T::DataProvider::MAXIMUM_VOTES_PER_VOTER, t); + + // fill targets. + let mut targets = (0..t).map(|i| { + let target = frame_benchmarking::account::("Target", i, SEED); + T::DataProvider::add_target(target.clone()); + target + }).collect::>(); + // we should always have enough voters to fill. + assert!(targets.len() > T::DataProvider::MAXIMUM_VOTES_PER_VOTER as usize); + targets.truncate(T::DataProvider::MAXIMUM_VOTES_PER_VOTER as usize); + + // fill voters. + (0..v).for_each(|i| { + let voter = frame_benchmarking::account::("Voter", i, SEED); + let weight = T::Currency::minimum_balance().saturated_into::() * 1000; + T::DataProvider::add_voter(voter, weight, targets.clone()); + }); } frame_benchmarking::benchmarks! { @@ -223,14 +246,18 @@ frame_benchmarking::benchmarks! { // a call to `::elect` where we only return the queued solution. elect_queued { - // assume largest values for the election status. These will merely affect the decoding. - let v = T::BenchmarkingConfig::VOTERS[1]; - let t = T::BenchmarkingConfig::TARGETS[1]; - let a = T::BenchmarkingConfig::ACTIVE_VOTERS[1]; - let d = T::BenchmarkingConfig::DESIRED_TARGETS[1]; + // number of votes in snapshot. + let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; + // number of targets in snapshot. + let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; + // number of assignments, i.e. compact.len(). This means the active nominators, thus must be + // a subset of `v` component. + let a in (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; + // number of desired targets. Must be a subset of `t` component. + let d in (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. T::BenchmarkingConfig::DESIRED_TARGETS[1]; let witness = SolutionOrSnapshotSize { voters: v, targets: t }; - let raw_solution = solution_with_size::(witness, a, d); + let raw_solution = solution_with_size::(witness, a, d)?; let ready_solution = >::feasibility_check(raw_solution, ElectionCompute::Signed).unwrap(); @@ -251,15 +278,6 @@ frame_benchmarking::benchmarks! { assert_eq!(>::get(), >::Off); } - #[extra] - create_snapshot { - assert!(>::snapshot().is_none()); - }: { - >::create_snapshot().unwrap() - } verify { - assert!(>::snapshot().is_some()); - } - submit { let c in 1 .. (T::SignedMaxSubmissions::get() - 1); @@ -307,7 +325,7 @@ frame_benchmarking::benchmarks! { T::BenchmarkingConfig::DESIRED_TARGETS[1]; let witness = SolutionOrSnapshotSize { voters: v, targets: t }; - let raw_solution = solution_with_size::(witness, a, d); + let raw_solution = solution_with_size::(witness, a, d)?; assert!(>::queued_solution().is_none()); >::put(Phase::Unsigned((true, 1u32.into()))); @@ -324,6 +342,84 @@ frame_benchmarking::benchmarks! { assert!(>::queued_solution().is_some()); } + // This is checking a valid solution. The worse case is indeed a valid solution. + feasibility_check { + // number of votes in snapshot. + let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; + // number of targets in snapshot. + let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; + // number of assignments, i.e. compact.len(). This means the active nominators, thus must be + // a subset of `v` component. + let a in (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; + // number of desired targets. Must be a subset of `t` component. + let d in (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. T::BenchmarkingConfig::DESIRED_TARGETS[1]; + + let size = SolutionOrSnapshotSize { voters: v, targets: t }; + let raw_solution = solution_with_size::(size, a, d)?; + + assert_eq!(raw_solution.compact.voter_count() as u32, a); + assert_eq!(raw_solution.compact.unique_targets().len() as u32, d); + + // encode the most significant storage item that needs to be decoded in the dispatch. + let encoded_snapshot = >::snapshot().unwrap().encode(); + }: { + assert_ok!(>::feasibility_check(raw_solution, ElectionCompute::Unsigned)); + let _decoded_snap = as Decode>::decode(&mut &*encoded_snapshot).unwrap(); + } + + // NOTE: this weight is not used anywhere, but the fact that it should succeed when execution in + // isolation is vital to ensure memory-safety. For the same reason, we don't care about the + // components iterating, we merely check that this operation will work with the "maximum" + // numbers. + // + // ONLY run this benchmark in isolation, and pass the `--extra` flag to enable it. + // + // NOTE: If this benchmark does not run out of memory with a given heap pages, it means that the + // OCW process can SURELY succeed with the given configuration, but the opposite is not true. + // This benchmark is doing more work than a raw call to `OffchainWorker_offchain_worker` runtime + // api call, since it is also setting up some mock data, which will itself exhaust the heap to + // some extent. + #[extra] + mine_solution_offchain_memory { + // number of votes in snapshot. Fixed to maximum. + let v = T::BenchmarkingConfig::MINER_MAXIMUM_VOTERS; + // number of targets in snapshot. Fixed to maximum. + let t = T::BenchmarkingConfig::MAXIMUM_TARGETS; + + T::DataProvider::clear(); + set_up_data_provider::(v, t); + let now = frame_system::Pallet::::block_number(); + >::put(Phase::Unsigned((true, now))); + >::create_snapshot().unwrap(); + }: { + // we can't really verify this as it won't write anything to state, check logs. + >::offchain_worker(now) + } + + // NOTE: this weight is not used anywhere, but the fact that it should succeed when execution in + // isolation is vital to ensure memory-safety. For the same reason, we don't care about the + // components iterating, we merely check that this operation will work with the "maximum" + // numbers. + // + // ONLY run this benchmark in isolation, and pass the `--extra` flag to enable it. + #[extra] + create_snapshot_memory { + // number of votes in snapshot. Fixed to maximum. + let v = T::BenchmarkingConfig::SNAPSHOT_MAXIMUM_VOTERS; + // number of targets in snapshot. Fixed to maximum. + let t = T::BenchmarkingConfig::MAXIMUM_TARGETS; + + T::DataProvider::clear(); + set_up_data_provider::(v, t); + assert!(>::snapshot().is_none()); + }: { + >::create_snapshot().unwrap() + } verify { + assert!(>::snapshot().is_some()); + assert_eq!(>::snapshot_metadata().unwrap().voters, v + t); + assert_eq!(>::snapshot_metadata().unwrap().targets, t); + } + #[extra] trim_assignments_length { // number of votes in snapshot. @@ -344,7 +440,7 @@ frame_benchmarking::benchmarks! { // Compute a random solution, then work backwards to get the lists of voters, targets, and // assignments let witness = SolutionOrSnapshotSize { voters: v, targets: t }; - let RawSolution { compact, .. } = solution_with_size::(witness, a, d); + let RawSolution { compact, .. } = solution_with_size::(witness, a, d)?; let RoundSnapshot { voters, targets } = MultiPhase::::snapshot().unwrap(); let voter_at = helpers::voter_at_fn::(&voters); let target_at = helpers::target_at_fn::(&targets); @@ -394,39 +490,10 @@ frame_benchmarking::benchmarks! { log!(trace, "actual encoded size = {}", encoding.len()); assert!(encoding.len() <= desired_size); } - - // This is checking a valid solution. The worse case is indeed a valid solution. - feasibility_check { - // number of votes in snapshot. - let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; - // number of targets in snapshot. - let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; - // number of assignments, i.e. compact.len(). This means the active nominators, thus must be - // a subset of `v` component. - let a in - (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; - // number of desired targets. Must be a subset of `t` component. - let d in - (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. - T::BenchmarkingConfig::DESIRED_TARGETS[1]; - - let size = SolutionOrSnapshotSize { voters: v, targets: t }; - let raw_solution = solution_with_size::(size, a, d); - - assert_eq!(raw_solution.compact.voter_count() as u32, a); - assert_eq!(raw_solution.compact.unique_targets().len() as u32, d); - - // encode the most significant storage item that needs to be decoded in the dispatch. - let encoded_snapshot = >::snapshot().unwrap().encode(); - }: { - assert_ok!(>::feasibility_check(raw_solution, ElectionCompute::Unsigned)); - let _decoded_snap = as Decode>::decode(&mut &*encoded_snapshot) - .unwrap(); - } } impl_benchmark_test_suite!( MultiPhase, - crate::mock::ExtBuilder::default().build(), + crate::mock::ExtBuilder::default().build_offchainify(10).0, crate::mock::Runtime, ); diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index f1d01a248111e..b41db2a42c607 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -191,8 +191,8 @@ //! portion of the bond). //! //! **Conditionally open unsigned phase**: Currently, the unsigned phase is always opened. This is -//! useful because an honest validator will run substrate OCW code, which should be good enough to trump -//! a mediocre or malicious signed submission (assuming in the absence of honest signed bots). +//! useful because an honest validator will run substrate OCW code, which should be good enough to +//! trump a mediocre or malicious signed submission (assuming in the absence of honest signed bots). //! If there are signed submissions, they can be checked against an absolute measure (e.g. PJR), //! then we can only open the unsigned phase in extreme conditions (i.e. "no good signed solution //! received") to spare some work for the active validators. @@ -308,6 +308,12 @@ pub trait BenchmarkingConfig { const ACTIVE_VOTERS: [u32; 2]; /// Range of desired targets. const DESIRED_TARGETS: [u32; 2]; + /// Maximum number of voters expected. This is used only for memory-benchmarking of snapshot. + const SNAPSHOT_MAXIMUM_VOTERS: u32; + /// Maximum number of voters expected. This is used only for memory-benchmarking of miner. + const MINER_MAXIMUM_VOTERS: u32; + /// Maximum number of targets expected. This is used only for memory-benchmarking. + const MAXIMUM_TARGETS: u32; } impl BenchmarkingConfig for () { @@ -315,6 +321,9 @@ impl BenchmarkingConfig for () { const TARGETS: [u32; 2] = [1000, 1600]; const ACTIVE_VOTERS: [u32; 2] = [1000, 3000]; const DESIRED_TARGETS: [u32; 2] = [400, 800]; + const SNAPSHOT_MAXIMUM_VOTERS: u32 = 10_000; + const MINER_MAXIMUM_VOTERS: u32 = 10_000; + const MAXIMUM_TARGETS: u32 = 2_000; } /// Current phase of the pallet. @@ -1063,7 +1072,7 @@ pub mod pallet { let _ = Self::unsigned_pre_dispatch_checks(solution) .map_err(|err| { - log!(error, "unsigned transaction validation failed due to {:?}", err); + log!(debug, "unsigned transaction validation failed due to {:?}", err); err }) .map_err(dispatch_error_to_invalid)?; @@ -1201,8 +1210,9 @@ impl Pallet { /// Internal logic of the offchain worker, to be executed only when the offchain lock is /// acquired with success. fn do_synchronized_offchain_worker(now: T::BlockNumber) { - log!(trace, "lock for offchain worker acquired."); - match Self::current_phase() { + let current_phase = Self::current_phase(); + log!(trace, "lock for offchain worker acquired. Phase = {:?}", current_phase); + match current_phase { Phase::Unsigned((true, opened)) if opened == now => { // Mine a new solution, cache it, and attempt to submit it let initial_output = Self::ensure_offchain_repeat_frequency(now).and_then(|_| { @@ -1453,11 +1463,20 @@ impl Pallet { .map_err(Into::into), FallbackStrategy::Nothing => Err(ElectionError::NoFallbackConfigured), }, - |ReadySolution { supports, compute, .. }| Ok(( - supports, - T::WeightInfo::elect_queued(), - compute - )), + |ReadySolution { supports, compute, .. }| { + // defensive-only: snapshot must always exist by this point. + let metadata = Self::snapshot_metadata().unwrap_or_default(); + let desired = supports.len() as u32; + let active_voters = supports + .iter() + .map(|(_, x)| x) + .fold(Zero::zero(), |acc, next| acc + next.voters.len() as u32); + Ok(( + supports, + T::WeightInfo::elect_queued(metadata.voters, metadata.targets, active_voters, desired), + compute + )) + }, ) .map(|(supports, weight, compute)| { Self::deposit_event(Event::ElectionFinalized(Some(compute))); diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 8840e2b935d35..1b8ee14345852 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -330,11 +330,11 @@ impl multi_phase::weights::WeightInfo for DualMockWeightInfo { <() as multi_phase::weights::WeightInfo>::submit(c) } } - fn elect_queued() -> Weight { + fn elect_queued(v: u32, t: u32, a: u32, d: u32) -> Weight { if MockWeightInfo::get() { Zero::zero() } else { - <() as multi_phase::weights::WeightInfo>::elect_queued() + <() as multi_phase::weights::WeightInfo>::elect_queued(v, t, a, d) } } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32) -> Weight { @@ -438,6 +438,32 @@ impl ElectionDataProvider for StakingMock { Targets::set(targets); Voters::set(voters); } + + #[cfg(any(feature = "runtime-benchmarks", test))] + fn clear() { + Targets::set(vec![]); + Voters::set(vec![]); + } + + #[cfg(any(feature = "runtime-benchmarks", test))] + fn add_voter(voter: AccountId, weight: VoteWeight, targets: Vec) { + let mut current = Voters::get(); + current.push((voter, weight, targets)); + Voters::set(current); + } + + #[cfg(any(feature = "runtime-benchmarks", test))] + fn add_target(target: AccountId) { + let mut current = Targets::get(); + current.push(target); + Targets::set(current); + + // to be on-par with staking, we add a self vote as well. the stake is really not that + // important. + let mut current = Voters::get(); + current.push((target, ExistentialDeposit::get() as u64, vec![target])); + Voters::set(current); + } } impl ExtBuilder { diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 52ecae7afa5fc..aaeb5e4c0c9e4 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -679,7 +679,7 @@ mod max_weight { fn on_initialize_open_unsigned_with_snapshot() -> Weight { unreachable!() } - fn elect_queued() -> Weight { + fn elect_queued(_v: u32, _t: u32, _a: u32, _d: u32) -> Weight { 0 } fn on_initialize_open_unsigned_without_snapshot() -> Weight { diff --git a/frame/election-provider-multi-phase/src/weights.rs b/frame/election-provider-multi-phase/src/weights.rs index 6a245ebb51254..0f732784c62c4 100644 --- a/frame/election-provider-multi-phase/src/weights.rs +++ b/frame/election-provider-multi-phase/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_election_provider_multi_phase //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-06-20, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-07-07, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -50,7 +50,7 @@ pub trait WeightInfo { fn finalize_signed_phase_accept_solution() -> Weight; fn finalize_signed_phase_reject_solution() -> Weight; fn on_initialize_open_unsigned_without_snapshot() -> Weight; - fn elect_queued() -> Weight; + fn elect_queued(v: u32, t: u32, a: u32, d: u32, ) -> Weight; fn submit(c: u32, ) -> Weight; fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight; fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight; @@ -60,69 +60,75 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn on_initialize_nothing() -> Weight { - (33_392_000 as Weight) + (33_170_000 as Weight) .saturating_add(T::DbWeight::get().reads(8 as Weight)) } fn on_initialize_open_signed() -> Weight { - (115_659_000 as Weight) + (113_680_000 as Weight) .saturating_add(T::DbWeight::get().reads(10 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (114_970_000 as Weight) + (113_619_000 as Weight) .saturating_add(T::DbWeight::get().reads(10 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn finalize_signed_phase_accept_solution() -> Weight { - (51_442_000 as Weight) + (60_184_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn finalize_signed_phase_reject_solution() -> Weight { - (23_160_000 as Weight) + (40_151_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (24_101_000 as Weight) + (23_833_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn elect_queued() -> Weight { - (6_153_604_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + fn elect_queued(v: u32, _t: u32, a: u32, d: u32, ) -> Weight { + (51_573_000 as Weight) + // Standard Error: 1_000 + .saturating_add((9_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 2_000 + .saturating_add((1_957_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 18_000 + .saturating_add((588_000 as Weight).saturating_mul(d as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) } fn submit(c: u32, ) -> Weight { - (78_972_000 as Weight) - // Standard Error: 16_000 - .saturating_add((308_000 as Weight).saturating_mul(c as Weight)) + (77_469_000 as Weight) + // Standard Error: 17_000 + .saturating_add((281_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 12_000 - .saturating_add((3_572_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 42_000 - .saturating_add((23_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 12_000 - .saturating_add((11_529_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 63_000 - .saturating_add((3_333_000 as Weight).saturating_mul(d as Weight)) + // Standard Error: 5_000 + .saturating_add((3_667_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 29_000 + .saturating_add((497_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 9_000 + .saturating_add((11_228_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 73_000 + .saturating_add((4_432_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 7_000 - .saturating_add((3_647_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 4_000 + .saturating_add((3_613_000 as Weight).saturating_mul(v as Weight)) // Standard Error: 23_000 - .saturating_add((390_000 as Weight).saturating_mul(t as Weight)) + .saturating_add((286_000 as Weight).saturating_mul(t as Weight)) // Standard Error: 7_000 - .saturating_add((9_614_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 35_000 - .saturating_add((3_405_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((9_677_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 58_000 + .saturating_add((4_178_000 as Weight).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) } } @@ -130,69 +136,73 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { fn on_initialize_nothing() -> Weight { - (33_392_000 as Weight) + (33_564_000 as Weight) .saturating_add(RocksDbWeight::get().reads(8 as Weight)) } fn on_initialize_open_signed() -> Weight { - (115_659_000 as Weight) + (114_561_000 as Weight) .saturating_add(RocksDbWeight::get().reads(10 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn on_initialize_open_unsigned_with_snapshot() -> Weight { - (114_970_000 as Weight) + (114_070_000 as Weight) .saturating_add(RocksDbWeight::get().reads(10 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn finalize_signed_phase_accept_solution() -> Weight { - (51_442_000 as Weight) + (59_765_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn finalize_signed_phase_reject_solution() -> Weight { - (23_160_000 as Weight) + (39_894_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn on_initialize_open_unsigned_without_snapshot() -> Weight { - (24_101_000 as Weight) + (23_591_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn elect_queued() -> Weight { - (6_153_604_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + fn elect_queued(v: u32, _t: u32, a: u32, d: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 1_000 + .saturating_add((19_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 1_000 + .saturating_add((1_959_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 14_000 + .saturating_add((392_000 as Weight).saturating_mul(d as Weight)) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) } fn submit(c: u32, ) -> Weight { - (78_972_000 as Weight) - // Standard Error: 16_000 - .saturating_add((308_000 as Weight).saturating_mul(c as Weight)) + (77_616_000 as Weight) + // Standard Error: 18_000 + .saturating_add((213_000 as Weight).saturating_mul(c as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn submit_unsigned(v: u32, t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) - // Standard Error: 12_000 - .saturating_add((3_572_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 8_000 + .saturating_add((3_701_000 as Weight).saturating_mul(v as Weight)) // Standard Error: 42_000 - .saturating_add((23_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 12_000 - .saturating_add((11_529_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 63_000 - .saturating_add((3_333_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((75_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 14_000 + .saturating_add((11_268_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 107_000 + .saturating_add((5_019_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight { + fn feasibility_check(v: u32, _t: u32, a: u32, d: u32, ) -> Weight { (0 as Weight) // Standard Error: 7_000 - .saturating_add((3_647_000 as Weight).saturating_mul(v as Weight)) - // Standard Error: 23_000 - .saturating_add((390_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 7_000 - .saturating_add((9_614_000 as Weight).saturating_mul(a as Weight)) - // Standard Error: 35_000 - .saturating_add((3_405_000 as Weight).saturating_mul(d as Weight)) + .saturating_add((3_632_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 12_000 + .saturating_add((9_664_000 as Weight).saturating_mul(a as Weight)) + // Standard Error: 95_000 + .saturating_add((4_264_000 as Weight).saturating_mul(d as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) } } diff --git a/frame/election-provider-support/src/lib.rs b/frame/election-provider-support/src/lib.rs index b846460e71f8a..1d1ebf02a2635 100644 --- a/frame/election-provider-support/src/lib.rs +++ b/frame/election-provider-support/src/lib.rs @@ -224,8 +224,25 @@ pub trait ElectionDataProvider { _voters: Vec<(AccountId, VoteWeight, Vec)>, _targets: Vec, _target_stake: Option, - ) { - } + ) {} + + /// Utility function only to be used in benchmarking scenarios, to be implemented optionally, + /// else a noop. + /// + /// Same as `put_snapshot`, but can add a single voter one by one. + #[cfg(any(feature = "runtime-benchmarks", test))] + fn add_voter(_voter: AccountId, _weight: VoteWeight, _targets: Vec) {} + + /// Utility function only to be used in benchmarking scenarios, to be implemented optionally, + /// else a noop. + /// + /// Same as `put_snapshot`, but can add a single voter one by one. + #[cfg(any(feature = "runtime-benchmarks", test))] + fn add_target(_target: AccountId) {} + + /// Clear all voters and targets. + #[cfg(any(feature = "runtime-benchmarks", test))] + fn clear() {} } #[cfg(feature = "std")] diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 5fe02212c650c..98db60d1b599a 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -3049,6 +3049,57 @@ impl frame_election_provider_support::ElectionDataProvider) { + use sp_std::convert::TryFrom; + let stake = >::try_from(weight).unwrap_or_else(|_| { + panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.") + }); + >::insert(voter.clone(), voter.clone()); + >::insert( + voter.clone(), + StakingLedger { + stash: voter.clone(), + active: stake, + total: stake, + unlocking: vec![], + claimed_rewards: vec![], + }, + ); + Self::do_add_nominator( + &voter, + Nominations { targets: targets, submitted_in: 0, suppressed: false }, + ); + } + + #[cfg(any(feature = "runtime-benchmarks", test))] + fn add_target(target: T::AccountId) { + let stake = MinValidatorBond::::get() * 100u32.into(); + >::insert(target.clone(), target.clone()); + >::insert( + target.clone(), + StakingLedger { + stash: target.clone(), + active: stake, + total: stake, + unlocking: vec![], + claimed_rewards: vec![], + }, + ); + Self::do_add_validator( + &target, + ValidatorPrefs { commission: Perbill::zero(), blocked: false }, + ); + } + + #[cfg(any(feature = "runtime-benchmarks", test))] + fn clear() { + >::remove_all(None); + >::remove_all(None); + >::remove_all(None); + >::remove_all(None); + } + #[cfg(any(feature = "runtime-benchmarks", test))] fn put_snapshot( voters: Vec<(T::AccountId, VoteWeight, Vec)>, diff --git a/utils/frame/benchmarking-cli/src/command.rs b/utils/frame/benchmarking-cli/src/command.rs index e1803a2c56ead..3bfb639dd9eb7 100644 --- a/utils/frame/benchmarking-cli/src/command.rs +++ b/utils/frame/benchmarking-cli/src/command.rs @@ -22,15 +22,15 @@ use frame_support::traits::StorageInfo; use sc_cli::{SharedParams, CliConfiguration, ExecutionStrategy, Result}; use sc_client_db::BenchmarkingState; use sc_executor::NativeExecutor; -use sp_state_machine::StateMachine; -use sp_externalities::Extensions; use sc_service::{Configuration, NativeExecutionDispatch}; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; -use sp_core::offchain::{OffchainWorkerExt, testing::TestOffchainExt}; -use sp_keystore::{ - SyncCryptoStorePtr, KeystoreExt, - testing::KeyStore, +use sp_core::offchain::{ + testing::{TestOffchainExt, TestTransactionPoolExt}, + OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, }; +use sp_externalities::Extensions; +use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStorePtr}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; +use sp_state_machine::StateMachine; use std::{fmt::Debug, sync::Arc}; impl BenchmarkCmd { @@ -73,7 +73,10 @@ impl BenchmarkCmd { let mut extensions = Extensions::default(); extensions.register(KeystoreExt(Arc::new(KeyStore::new()) as SyncCryptoStorePtr)); let (offchain, _) = TestOffchainExt::new(); - extensions.register(OffchainWorkerExt::new(offchain)); + let (pool, _) = TestTransactionPoolExt::new(); + extensions.register(OffchainWorkerExt::new(offchain.clone())); + extensions.register(OffchainDbExt::new(offchain)); + extensions.register(TransactionPoolExt::new(pool)); let result = StateMachine::<_, _, NumberFor, _>::new( &state, diff --git a/utils/frame/benchmarking-cli/src/lib.rs b/utils/frame/benchmarking-cli/src/lib.rs index 38dabd8c9415d..0642ddabc1374 100644 --- a/utils/frame/benchmarking-cli/src/lib.rs +++ b/utils/frame/benchmarking-cli/src/lib.rs @@ -85,7 +85,8 @@ pub struct BenchmarkCmd { #[structopt(long)] pub output_analysis: Option, - /// Set the heap pages while running benchmarks. + /// Set the heap pages while running benchmarks. If not set, the default value from the client + /// is used. #[structopt(long)] pub heap_pages: Option, @@ -93,7 +94,8 @@ pub struct BenchmarkCmd { #[structopt(long)] pub no_verify: bool, - /// Display and run extra benchmarks that would otherwise not be needed for weight construction. + /// Display and run extra benchmarks that would otherwise not be needed for weight + /// construction. #[structopt(long)] pub extra: bool, @@ -120,7 +122,7 @@ pub struct BenchmarkCmd { value_name = "METHOD", possible_values = &WasmExecutionMethod::variants(), case_insensitive = true, - default_value = "Interpreted" + default_value = "compiled" )] pub wasm_method: WasmExecutionMethod,