From ae75a371bfdf7a4ec678b2f2263a7096a2aaa0e1 Mon Sep 17 00:00:00 2001 From: Georges <georges.dib@gmail.com> Date: Fri, 15 Apr 2022 11:15:01 +0100 Subject: [PATCH] Adding benchmarking for new `frame_election_provider_support` (#11149) * First stab at adding benchmarking for `election-provider-support` onchain * Adding `BoundedPhragMMS` and fixing stuff * Fixing node runtime * Fixing tests * Finalising all benchmarking stuff * better comments * Better benchmarking config * Better `WeightInfo` and benchmarking * Fixing tests * Adding some documentation * Fixing some typos * Incorporating review feedback * cleanup of rustdocs * rustdoc changes * changes after code review * Fixing some errors. * Fixing dependencies post merge * Bringing back `UnboundedExecution` * Better rustdoc and naming * Cargo.toml formatting --- substrate/Cargo.lock | 14 +++ substrate/Cargo.toml | 1 + substrate/bin/node/runtime/Cargo.toml | 2 + substrate/bin/node/runtime/src/lib.rs | 11 +- substrate/frame/babe/src/mock.rs | 3 +- .../election-provider-multi-phase/Cargo.toml | 10 +- .../election-provider-multi-phase/src/mock.rs | 5 +- .../benchmarking/Cargo.toml | 38 ++++++ .../benchmarking/src/lib.rs | 91 ++++++++++++++ .../election-provider-support/src/lib.rs | 19 ++- .../election-provider-support/src/onchain.rs | 115 ++++++++++++------ .../election-provider-support/src/weights.rs | 94 ++++++++++++++ substrate/frame/grandpa/src/mock.rs | 5 +- .../frame/offences/benchmarking/src/mock.rs | 3 +- .../frame/session/benchmarking/src/mock.rs | 3 +- substrate/frame/staking/src/mock.rs | 3 +- .../npos-elections/src/balancing.rs | 2 +- .../primitives/npos-elections/src/phragmms.rs | 2 +- 18 files changed, 366 insertions(+), 55 deletions(-) create mode 100644 substrate/frame/election-provider-support/benchmarking/Cargo.toml create mode 100644 substrate/frame/election-provider-support/benchmarking/src/lib.rs create mode 100644 substrate/frame/election-provider-support/src/weights.rs diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 440d549af1a..2fa73811824 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -4998,6 +4998,7 @@ dependencies = [ "pallet-conviction-voting", "pallet-democracy", "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-gilt", "pallet-grandpa", @@ -5851,6 +5852,7 @@ dependencies = [ "frame-system", "log 0.4.14", "pallet-balances", + "pallet-election-provider-support-benchmarking", "parity-scale-codec", "parking_lot 0.12.0", "rand 0.7.3", @@ -5866,6 +5868,18 @@ dependencies = [ "strum", ] +[[package]] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", +] + [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 45df7258ef2..c281913cd55 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -87,6 +87,7 @@ members = [ "frame/try-runtime", "frame/election-provider-multi-phase", "frame/election-provider-support", + "frame/election-provider-support/benchmarking", "frame/election-provider-support/solution-type", "frame/election-provider-support/solution-type/fuzzer", "frame/examples/basic", diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 686508b47db..41b2402d33a 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -65,6 +65,7 @@ pallet-contracts-rpc-runtime-api = { version = "4.0.0-dev", default-features = f pallet-conviction-voting = { version = "4.0.0-dev", default-features = false, path = "../../../frame/conviction-voting" } pallet-democracy = { version = "4.0.0-dev", default-features = false, path = "../../../frame/democracy" } pallet-election-provider-multi-phase = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-multi-phase" } +pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-support/benchmarking", optional = true } pallet-elections-phragmen = { version = "5.0.0-dev", default-features = false, path = "../../../frame/elections-phragmen" } pallet-gilt = { version = "4.0.0-dev", default-features = false, path = "../../../frame/gilt" } pallet-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../frame/grandpa" } @@ -196,6 +197,7 @@ runtime-benchmarks = [ "pallet-conviction-voting/runtime-benchmarks", "pallet-democracy/runtime-benchmarks", "pallet-election-provider-multi-phase/runtime-benchmarks", + "pallet-election-provider-support-benchmarking/runtime-benchmarks", "pallet-elections-phragmen/runtime-benchmarks", "pallet-gilt/runtime-benchmarks", "pallet-grandpa/runtime-benchmarks", diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index f422d1ffc45..f37345014f3 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -643,17 +643,18 @@ impl Get<Option<(usize, ExtendedBalance)>> for OffchainRandomBalancing { } pub struct OnChainSeqPhragmen; -impl onchain::ExecutionConfig for OnChainSeqPhragmen { +impl onchain::Config for OnChainSeqPhragmen { type System = Runtime; type Solver = SequentialPhragmen< AccountId, pallet_election_provider_multi_phase::SolutionAccuracyOf<Runtime>, >; type DataProvider = <Runtime as pallet_election_provider_multi_phase::Config>::DataProvider; + type WeightInfo = frame_election_provider_support::weights::SubstrateWeight<Runtime>; } -impl onchain::BoundedExecutionConfig for OnChainSeqPhragmen { - type VotersBound = ConstU32<20_000>; +impl onchain::BoundedConfig for OnChainSeqPhragmen { + type VotersBound = MaxElectingVoters; type TargetsBound = ConstU32<2_000>; } @@ -1531,6 +1532,7 @@ mod benches { [pallet_contracts, Contracts] [pallet_democracy, Democracy] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] + [pallet_election_provider_support_benchmarking, EPSBench::<Runtime>] [pallet_elections_phragmen, Elections] [pallet_gilt, Gilt] [pallet_grandpa, Grandpa] @@ -1851,6 +1853,7 @@ impl_runtime_apis! { // which is why we need these two lines below. use pallet_session_benchmarking::Pallet as SessionBench; use pallet_offences_benchmarking::Pallet as OffencesBench; + use pallet_election_provider_support_benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; @@ -1872,11 +1875,13 @@ impl_runtime_apis! { // which is why we need these two lines below. use pallet_session_benchmarking::Pallet as SessionBench; use pallet_offences_benchmarking::Pallet as OffencesBench; + use pallet_election_provider_support_benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; impl pallet_session_benchmarking::Config for Runtime {} impl pallet_offences_benchmarking::Config for Runtime {} + impl pallet_election_provider_support_benchmarking::Config for Runtime {} impl frame_system_benchmarking::Config for Runtime {} impl baseline::Config for Runtime {} diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index 37d8e9e37a5..15f53e7da08 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -173,10 +173,11 @@ parameter_types! { } pub struct OnChainSeqPhragmen; -impl onchain::ExecutionConfig for OnChainSeqPhragmen { +impl onchain::Config for OnChainSeqPhragmen { type System = Test; type Solver = SequentialPhragmen<DummyValidatorId, Perbill>; type DataProvider = Staking; + type WeightInfo = (); } impl pallet_staking::Config for Test { diff --git a/substrate/frame/election-provider-multi-phase/Cargo.toml b/substrate/frame/election-provider-multi-phase/Cargo.toml index 25f98d965d8..48134966a92 100644 --- a/substrate/frame/election-provider-multi-phase/Cargo.toml +++ b/substrate/frame/election-provider-multi-phase/Cargo.toml @@ -17,7 +17,9 @@ static_assertions = "1.1.0" codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = [ + "derive", +] } log = { version = "0.4.14", default-features = false } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } @@ -33,11 +35,14 @@ frame-election-provider-support = { version = "4.0.0-dev", default-features = fa # Optional imports for benchmarking frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } +pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../election-provider-support/benchmarking", optional = true } rand = { version = "0.7.3", default-features = false, optional = true, features = [ "alloc", "small_rng", ] } -strum = { optional = true, default-features = false, version = "0.23.0", features = ["derive"] } +strum = { optional = true, default-features = false, version = "0.23.0", features = [ + "derive", +] } [dev-dependencies] parking_lot = "0.12.0" @@ -46,7 +51,6 @@ sp-core = { version = "6.0.0", default-features = false, path = "../../primitive sp-io = { version = "6.0.0", path = "../../primitives/io" } sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../primitives/npos-elections" } sp-tracing = { version = "5.0.0", path = "../../primitives/tracing" } -frame-election-provider-support = { version = "4.0.0-dev", path = "../election-provider-support" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } frame-benchmarking = { version = "4.0.0-dev", path = "../benchmarking" } diff --git a/substrate/frame/election-provider-multi-phase/src/mock.rs b/substrate/frame/election-provider-multi-phase/src/mock.rs index d6f040363db..7c06ff6bee5 100644 --- a/substrate/frame/election-provider-multi-phase/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/src/mock.rs @@ -276,10 +276,11 @@ parameter_types! { } pub struct OnChainSeqPhragmen; -impl onchain::ExecutionConfig for OnChainSeqPhragmen { +impl onchain::Config for OnChainSeqPhragmen { type System = Runtime; type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>; type DataProvider = StakingMock; + type WeightInfo = (); } pub struct MockFallback; @@ -304,7 +305,7 @@ impl InstantElectionProvider for MockFallback { max_voters, max_targets, ) - .map_err(|_| "UnboundedExecution failed") + .map_err(|_| "onchain::UnboundedExecution failed.") } else { super::NoFallback::<Runtime>::elect_with_bounds(max_voters, max_targets) } diff --git a/substrate/frame/election-provider-support/benchmarking/Cargo.toml b/substrate/frame/election-provider-support/benchmarking/Cargo.toml new file mode 100644 index 00000000000..e9719be139a --- /dev/null +++ b/substrate/frame/election-provider-support/benchmarking/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +authors = ["Parity Technologies <admin@parity.io>"] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Benchmarking for election provider support onchain config trait" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ + "derive", +] } +sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/npos-elections" } +sp-runtime = { version = "6.0.0", default-features = false, path = "../../../primitives/runtime" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" } +frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = ".." } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking", optional = true } + + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-npos-elections/std", + "sp-runtime/std", + "frame-benchmarking/std", + "frame-system/std", +] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-election-provider-support/runtime-benchmarks", +] diff --git a/substrate/frame/election-provider-support/benchmarking/src/lib.rs b/substrate/frame/election-provider-support/benchmarking/src/lib.rs new file mode 100644 index 00000000000..547e35bed36 --- /dev/null +++ b/substrate/frame/election-provider-support/benchmarking/src/lib.rs @@ -0,0 +1,91 @@ +// This file is part of Substrate. + +// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Election provider support pallet benchmarking. +//! This is separated into its own crate to avoid bloating the size of the runtime. + +#![cfg(feature = "runtime-benchmarks")] +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::Decode; +use frame_benchmarking::{benchmarks, Vec}; +use frame_election_provider_support::{NposSolver, PhragMMS, SequentialPhragmen}; + +pub struct Pallet<T: Config>(frame_system::Pallet<T>); +pub trait Config: frame_system::Config {} + +const VOTERS: [u32; 2] = [1_000, 2_000]; +const TARGETS: [u32; 2] = [500, 1_000]; +const VOTES_PER_VOTER: [u32; 2] = [5, 16]; + +const SEED: u32 = 999; +fn set_up_voters_targets<AccountId: Decode + Clone>( + voters_len: u32, + targets_len: u32, + degree: usize, +) -> (Vec<(AccountId, u64, impl IntoIterator<Item = AccountId>)>, Vec<AccountId>) { + // fill targets. + let mut targets = (0..targets_len) + .map(|i| frame_benchmarking::account::<AccountId>("Target", i, SEED)) + .collect::<Vec<_>>(); + assert!(targets.len() > degree, "we should always have enough voters to fill"); + targets.truncate(degree); + + // fill voters. + let voters = (0..voters_len) + .map(|i| { + let voter = frame_benchmarking::account::<AccountId>("Voter", i, SEED); + (voter, 1_000, targets.clone()) + }) + .collect::<Vec<_>>(); + + (voters, targets) +} + +benchmarks! { + phragmen { + // number of votes in snapshot. + let v in (VOTERS[0]) .. VOTERS[1]; + // number of targets in snapshot. + let t in (TARGETS[0]) .. TARGETS[1]; + // number of votes per voter (ie the degree). + let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1]; + + let (voters, targets) = set_up_voters_targets::<T::AccountId>(v, t, d as usize); + }: { + assert!( + SequentialPhragmen::<T::AccountId, sp_runtime::Perbill> + ::solve(d as usize, targets, voters).is_ok() + ); + } + + phragmms { + // number of votes in snapshot. + let v in (VOTERS[0]) .. VOTERS[1]; + // number of targets in snapshot. + let t in (TARGETS[0]) .. TARGETS[1]; + // number of votes per voter (ie the degree). + let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1]; + + let (voters, targets) = set_up_voters_targets::<T::AccountId>(v, t, d as usize); + }: { + assert!( + PhragMMS::<T::AccountId, sp_runtime::Perbill> + ::solve(d as usize, targets, voters).is_ok() + ); + } +} diff --git a/substrate/frame/election-provider-support/src/lib.rs b/substrate/frame/election-provider-support/src/lib.rs index 19735cf6035..37ea2fcc590 100644 --- a/substrate/frame/election-provider-support/src/lib.rs +++ b/substrate/frame/election-provider-support/src/lib.rs @@ -170,7 +170,7 @@ pub mod onchain; pub mod traits; #[cfg(feature = "std")] use codec::{Decode, Encode}; -use frame_support::{BoundedVec, RuntimeDebug}; +use frame_support::{weights::Weight, BoundedVec, RuntimeDebug}; use sp_runtime::traits::Bounded; use sp_std::{fmt::Debug, prelude::*}; @@ -195,6 +195,9 @@ pub use sp_arithmetic; #[doc(hidden)] pub use sp_std; +pub mod weights; +pub use weights::WeightInfo; + #[cfg(test)] mod mock; #[cfg(test)] @@ -523,6 +526,12 @@ pub trait NposSolver { targets: Vec<Self::AccountId>, voters: Vec<(Self::AccountId, VoteWeight, impl IntoIterator<Item = Self::AccountId>)>, ) -> Result<ElectionResult<Self::AccountId, Self::Accuracy>, Self::Error>; + + /// Measure the weight used in the calculation of the solver. + /// - `voters` is the number of voters. + /// - `targets` is the number of targets. + /// - `vote_degree` is the degree ie the maximum numbers of votes per voter. + fn weight<T: WeightInfo>(voters: u32, targets: u32, vote_degree: u32) -> Weight; } /// A wrapper for [`sp_npos_elections::seq_phragmen`] that implements [`NposSolver`]. See the @@ -547,6 +556,10 @@ impl< ) -> Result<ElectionResult<Self::AccountId, Self::Accuracy>, Self::Error> { sp_npos_elections::seq_phragmen(winners, targets, voters, Balancing::get()) } + + fn weight<T: WeightInfo>(voters: u32, targets: u32, vote_degree: u32) -> Weight { + T::phragmen(voters, targets, vote_degree) + } } /// A wrapper for [`sp_npos_elections::phragmms()`] that implements [`NposSolver`]. See the @@ -571,6 +584,10 @@ impl< ) -> Result<ElectionResult<Self::AccountId, Self::Accuracy>, Self::Error> { sp_npos_elections::phragmms(winners, targets, voters, Balancing::get()) } + + fn weight<T: WeightInfo>(voters: u32, targets: u32, vote_degree: u32) -> Weight { + T::phragmms(voters, targets, vote_degree) + } } /// A voter, at the level of abstraction of this crate. diff --git a/substrate/frame/election-provider-support/src/onchain.rs b/substrate/frame/election-provider-support/src/onchain.rs index 57fd931a467..62e76c38888 100644 --- a/substrate/frame/election-provider-support/src/onchain.rs +++ b/substrate/frame/election-provider-support/src/onchain.rs @@ -15,9 +15,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! An implementation of [`ElectionProvider`] that uses an `NposSolver` to do the election. +//! An implementation of [`ElectionProvider`] that uses an `NposSolver` to do the election. As the +//! name suggests, this is meant to be used onchain. Given how heavy the calculations are, please be +//! careful when using it onchain. -use crate::{ElectionDataProvider, ElectionProvider, InstantElectionProvider, NposSolver}; +use crate::{ + Debug, ElectionDataProvider, ElectionProvider, InstantElectionProvider, NposSolver, WeightInfo, +}; use frame_support::{traits::Get, weights::DispatchClass}; use sp_npos_elections::*; use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, prelude::*}; @@ -41,39 +45,32 @@ impl From<sp_npos_elections::Error> for Error { /// /// This will accept voting data on the fly and produce the results immediately. /// -/// Finally, the [`ElectionProvider`] implementation of this type does not impose any limits on the +/// The [`ElectionProvider`] implementation of this type does not impose any dynamic limits on the /// number of voters and targets that are fetched. This could potentially make this unsuitable for -/// execution onchain. One could, however, impose bounds on it by using for example -/// `BoundedExecution` which will the bounds provided in the configuration. +/// execution onchain. One could, however, impose bounds on it by using `BoundedExecution` using the +/// `MaxVoters` and `MaxTargets` bonds in the `BoundedConfig` trait. /// -/// On the other hand, the [`InstantElectionProvider`] implementation does limit these inputs, -/// either via using `BoundedExecution` and imposing the bounds there, or dynamically via calling -/// `elect_with_bounds` providing these bounds. If you use `elect_with_bounds` along with -/// `InstantElectionProvider`, the bound that would be used is the minimum of the 2 bounds. -/// -/// It is advisable to use the former ([`ElectionProvider::elect`]) only at genesis, or for testing, -/// the latter [`InstantElectionProvider::elect_with_bounds`] for onchain operations, with -/// thoughtful bounds. +/// On the other hand, the [`InstantElectionProvider`] implementation does limit these inputs +/// dynamically. If you use `elect_with_bounds` along with `InstantElectionProvider`, the bound that +/// would be used is the minimum of the dynamic bounds given as arguments to `elect_with_bounds` and +/// the trait bounds (`MaxVoters` and `MaxTargets`). /// /// Please use `BoundedExecution` at all times except at genesis or for testing, with thoughtful /// bounds in order to bound the potential execution time. Limit the use `UnboundedExecution` at /// genesis or for testing, as it does not bound the inputs. However, this can be used with /// `[InstantElectionProvider::elect_with_bounds`] that dynamically imposes limits. -pub struct BoundedExecution<T: BoundedExecutionConfig>(PhantomData<T>); +pub struct BoundedExecution<T: BoundedConfig>(PhantomData<T>); /// An unbounded variant of [`BoundedExecution`]. /// /// ### Warning /// -/// This can be very expensive to run frequently on-chain. Use with care. Moreover, this -/// implementation ignores the additional data of the election data provider and gives no insight on -/// how much weight was consumed. -pub struct UnboundedExecution<T: ExecutionConfig>(PhantomData<T>); - -/// Configuration trait of [`UnboundedExecution`]. -pub trait ExecutionConfig { - /// Something that implements the system pallet configs. This is to enable to register extra - /// weight. +/// This can be very expensive to run frequently on-chain. Use with care. +pub struct UnboundedExecution<T: Config>(PhantomData<T>); + +/// Configuration trait for an onchain election execution. +pub trait Config { + /// Needed for weight registration. type System: frame_system::Config; /// `NposSolver` that should be used, an example would be `PhragMMS`. type Solver: NposSolver< @@ -85,17 +82,18 @@ pub trait ExecutionConfig { AccountId = <Self::System as frame_system::Config>::AccountId, BlockNumber = <Self::System as frame_system::Config>::BlockNumber, >; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; } -/// Configuration trait of [`BoundedExecution`]. -pub trait BoundedExecutionConfig: ExecutionConfig { +pub trait BoundedConfig: Config { /// Bounds the number of voters. type VotersBound: Get<u32>; /// Bounds the number of targets. type TargetsBound: Get<u32>; } -fn elect_with<T: ExecutionConfig>( +fn elect_with<T: Config>( maybe_max_voters: Option<usize>, maybe_max_targets: Option<usize>, ) -> Result<Supports<<T::System as frame_system::Config>::AccountId>, Error> { @@ -104,6 +102,9 @@ fn elect_with<T: ExecutionConfig>( T::DataProvider::electable_targets(maybe_max_targets).map_err(Error::DataProvider)?; let desired_targets = T::DataProvider::desired_targets().map_err(Error::DataProvider)?; + let voters_len = voters.len() as u32; + let targets_len = targets.len() as u32; + let stake_map: BTreeMap<_, _> = voters .iter() .map(|(validator, vote_weight, _)| (validator.clone(), *vote_weight)) @@ -118,7 +119,11 @@ fn elect_with<T: ExecutionConfig>( let staked = assignment_ratio_to_staked_normalized(assignments, &stake_of)?; - let weight = <T::System as frame_system::Config>::BlockWeights::get().max_block; + let weight = T::Solver::weight::<T::WeightInfo>( + voters_len, + targets_len, + <T::DataProvider as ElectionDataProvider>::MaxVotesPerVoter::get(), + ); frame_system::Pallet::<T::System>::register_extra_weight_unchecked( weight, DispatchClass::Mandatory, @@ -127,13 +132,13 @@ fn elect_with<T: ExecutionConfig>( Ok(to_supports(&staked)) } -impl<T: ExecutionConfig> ElectionProvider for UnboundedExecution<T> { +impl<T: Config> ElectionProvider for UnboundedExecution<T> { type AccountId = <T::System as frame_system::Config>::AccountId; type BlockNumber = <T::System as frame_system::Config>::BlockNumber; type Error = Error; type DataProvider = T::DataProvider; - fn elect() -> Result<Supports<<T::System as frame_system::Config>::AccountId>, Self::Error> { + fn elect() -> Result<Supports<Self::AccountId>, Self::Error> { // This should not be called if not in `std` mode (and therefore neither in genesis nor in // testing) if cfg!(not(feature = "std")) { @@ -147,7 +152,7 @@ impl<T: ExecutionConfig> ElectionProvider for UnboundedExecution<T> { } } -impl<T: ExecutionConfig> InstantElectionProvider for UnboundedExecution<T> { +impl<T: Config> InstantElectionProvider for UnboundedExecution<T> { fn elect_with_bounds( max_voters: usize, max_targets: usize, @@ -156,18 +161,18 @@ impl<T: ExecutionConfig> InstantElectionProvider for UnboundedExecution<T> { } } -impl<T: BoundedExecutionConfig> ElectionProvider for BoundedExecution<T> { +impl<T: BoundedConfig> ElectionProvider for BoundedExecution<T> { type AccountId = <T::System as frame_system::Config>::AccountId; type BlockNumber = <T::System as frame_system::Config>::BlockNumber; type Error = Error; type DataProvider = T::DataProvider; - fn elect() -> Result<Supports<<T::System as frame_system::Config>::AccountId>, Self::Error> { + fn elect() -> Result<Supports<Self::AccountId>, Self::Error> { elect_with::<T>(Some(T::VotersBound::get() as usize), Some(T::TargetsBound::get() as usize)) } } -impl<T: BoundedExecutionConfig> InstantElectionProvider for BoundedExecution<T> { +impl<T: BoundedConfig> InstantElectionProvider for BoundedExecution<T> { fn elect_with_bounds( max_voters: usize, max_targets: usize, @@ -182,6 +187,8 @@ impl<T: BoundedExecutionConfig> InstantElectionProvider for BoundedExecution<T> #[cfg(test)] mod tests { use super::*; + use crate::{PhragMMS, SequentialPhragmen}; + use frame_support::traits::ConstU32; use sp_npos_elections::Support; use sp_runtime::Perbill; type AccountId = u64; @@ -228,13 +235,32 @@ mod tests { type MaxConsumers = frame_support::traits::ConstU32<16>; } - impl ExecutionConfig for Runtime { - type System = Self; - type Solver = crate::SequentialPhragmen<AccountId, Perbill>; + struct PhragmenParams; + struct PhragMMSParams; + + impl Config for PhragmenParams { + type System = Runtime; + type Solver = SequentialPhragmen<AccountId, Perbill>; type DataProvider = mock_data_provider::DataProvider; + type WeightInfo = (); } - type OnChainPhragmen = UnboundedExecution<Runtime>; + impl BoundedConfig for PhragmenParams { + type VotersBound = ConstU32<600>; + type TargetsBound = ConstU32<400>; + } + + impl Config for PhragMMSParams { + type System = Runtime; + type Solver = PhragMMS<AccountId, Perbill>; + type DataProvider = mock_data_provider::DataProvider; + type WeightInfo = (); + } + + impl BoundedConfig for PhragMMSParams { + type VotersBound = ConstU32<600>; + type TargetsBound = ConstU32<400>; + } mod mock_data_provider { use frame_support::{bounded_vec, traits::ConstU32}; @@ -273,7 +299,20 @@ mod tests { fn onchain_seq_phragmen_works() { sp_io::TestExternalities::new_empty().execute_with(|| { assert_eq!( - OnChainPhragmen::elect().unwrap(), + BoundedExecution::<PhragmenParams>::elect().unwrap(), + vec![ + (10, Support { total: 25, voters: vec![(1, 10), (3, 15)] }), + (30, Support { total: 35, voters: vec![(2, 20), (3, 15)] }) + ] + ); + }) + } + + #[test] + fn onchain_phragmms_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + assert_eq!( + BoundedExecution::<PhragMMSParams>::elect().unwrap(), vec![ (10, Support { total: 25, voters: vec![(1, 10), (3, 15)] }), (30, Support { total: 35, voters: vec![(2, 20), (3, 15)] }) diff --git a/substrate/frame/election-provider-support/src/weights.rs b/substrate/frame/election-provider-support/src/weights.rs new file mode 100644 index 00000000000..f288ae63bb5 --- /dev/null +++ b/substrate/frame/election-provider-support/src/weights.rs @@ -0,0 +1,94 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_election_provider_support_onchain_benchmarking +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-04-04, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/substrate +// benchmark +// --chain=dev +// --steps=1 +// --repeat=1 +// --pallet=pallet_election_provider_support_benchmarking +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=frame/election-provider-support/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_election_provider_support_benchmarking. +pub trait WeightInfo { + fn phragmen(v: u32, t: u32, d: u32, ) -> Weight; + fn phragmms(v: u32, t: u32, d: u32, ) -> Weight; +} + +/// Weights for pallet_election_provider_support_benchmarking using the Substrate node and recommended hardware. +pub struct SubstrateWeight<T>(PhantomData<T>); +impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { + fn phragmen(v: u32, t: u32, d: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 667_000 + .saturating_add((32_973_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 1_334_000 + .saturating_add((1_334_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 60_644_000 + .saturating_add((2_636_364_000 as Weight).saturating_mul(d as Weight)) + } + fn phragmms(v: u32, t: u32, d: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 73_000 + .saturating_add((21_073_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 146_000 + .saturating_add((65_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 6_649_000 + .saturating_add((1_711_424_000 as Weight).saturating_mul(d as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn phragmen(v: u32, t: u32, d: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 667_000 + .saturating_add((32_973_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 1_334_000 + .saturating_add((1_334_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 60_644_000 + .saturating_add((2_636_364_000 as Weight).saturating_mul(d as Weight)) + } + fn phragmms(v: u32, t: u32, d: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 73_000 + .saturating_add((21_073_000 as Weight).saturating_mul(v as Weight)) + // Standard Error: 146_000 + .saturating_add((65_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 6_649_000 + .saturating_add((1_711_424_000 as Weight).saturating_mul(d as Weight)) + } +} diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index 0296cd2e28d..67d5a3d7fd3 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -181,10 +181,11 @@ parameter_types! { } pub struct OnChainSeqPhragmen; -impl onchain::ExecutionConfig for OnChainSeqPhragmen { +impl onchain::Config for OnChainSeqPhragmen { type System = Test; - type Solver = SequentialPhragmen<<Test as frame_system::Config>::AccountId, Perbill>; + type Solver = SequentialPhragmen<u64, Perbill>; type DataProvider = Staking; + type WeightInfo = (); } impl pallet_staking::Config for Test { diff --git a/substrate/frame/offences/benchmarking/src/mock.rs b/substrate/frame/offences/benchmarking/src/mock.rs index 1a4414de0b0..74cc2958692 100644 --- a/substrate/frame/offences/benchmarking/src/mock.rs +++ b/substrate/frame/offences/benchmarking/src/mock.rs @@ -151,10 +151,11 @@ parameter_types! { pub type Extrinsic = sp_runtime::testing::TestXt<Call, ()>; pub struct OnChainSeqPhragmen; -impl onchain::ExecutionConfig for OnChainSeqPhragmen { +impl onchain::Config for OnChainSeqPhragmen { type System = Test; type Solver = SequentialPhragmen<AccountId, Perbill>; type DataProvider = Staking; + type WeightInfo = (); } impl pallet_staking::Config for Test { diff --git a/substrate/frame/session/benchmarking/src/mock.rs b/substrate/frame/session/benchmarking/src/mock.rs index 24b42b3e9f4..5acc484f9ba 100644 --- a/substrate/frame/session/benchmarking/src/mock.rs +++ b/substrate/frame/session/benchmarking/src/mock.rs @@ -157,10 +157,11 @@ where } pub struct OnChainSeqPhragmen; -impl onchain::ExecutionConfig for OnChainSeqPhragmen { +impl onchain::Config for OnChainSeqPhragmen { type System = Test; type Solver = SequentialPhragmen<AccountId, sp_runtime::Perbill>; type DataProvider = Staking; + type WeightInfo = (); } impl pallet_staking::Config for Test { diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index bb90aded852..09809483ec1 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -248,10 +248,11 @@ impl pallet_bags_list::Config for Test { } pub struct OnChainSeqPhragmen; -impl onchain::ExecutionConfig for OnChainSeqPhragmen { +impl onchain::Config for OnChainSeqPhragmen { type System = Test; type Solver = SequentialPhragmen<AccountId, Perbill>; type DataProvider = Staking; + type WeightInfo = (); } impl crate::pallet::pallet::Config for Test { diff --git a/substrate/primitives/npos-elections/src/balancing.rs b/substrate/primitives/npos-elections/src/balancing.rs index 98f193e9e61..54b8ee4bf24 100644 --- a/substrate/primitives/npos-elections/src/balancing.rs +++ b/substrate/primitives/npos-elections/src/balancing.rs @@ -37,7 +37,7 @@ use sp_std::prelude::*; /// /// In almost all cases, a balanced solution will have a better score than an unbalanced solution, /// yet this is not 100% guaranteed because the first element of a [`crate::ElectionScore`] does not -/// directly related to balancing. +/// directly relate to balancing. /// /// Note that some reference implementation adopt an approach in which voters are balanced randomly /// per round. To advocate determinism, we don't do this. In each round, all voters are exactly diff --git a/substrate/primitives/npos-elections/src/phragmms.rs b/substrate/primitives/npos-elections/src/phragmms.rs index 6220cacd157..aa4c558bea1 100644 --- a/substrate/primitives/npos-elections/src/phragmms.rs +++ b/substrate/primitives/npos-elections/src/phragmms.rs @@ -37,7 +37,7 @@ use sp_std::{prelude::*, rc::Rc}; /// - The algorithm is a _best-effort_ to elect `to_elect`. If less candidates are provided, less /// winners are returned, without an error. /// -/// This can only fail of the normalization fails. This can happen if for any of the resulting +/// This can only fail if the normalization fails. This can happen if for any of the resulting /// assignments, `assignment.distribution.map(|p| p.deconstruct()).sum()` fails to fit inside /// `UpperOf<P>`. A user of this crate may statically assert that this can never happen and safely /// `expect` this to return `Ok`. -- GitLab