Benchmark macro (#4962)

* MAcro benchamrks

* Iterative macro

* Tidying it up.

* Macro improvements

* Bits..

* Last benchmaks.

* Repo benchmark macro

* Add the possibility of evaluating arbitrary expressions in a
benchmaark

* Better syntax and docs

* Update `BenchmarkParameter`

* Add `ignore` to sudo-code in docs

* First try of timestamp implementation.

* Fix macro docs, remove warnings.

* Use macro in balances pallet.

* Make some space in frame benchmarking.

* Remove _benchmarks_seed variable.

* Bump impl_version.

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Marcio Diaz <marcio@parity.io>
This commit is contained in:
Gavin Wood
2020-02-20 17:20:16 +01:00
committed by GitHub
parent 504914b0a6
commit f5176ba377
9 changed files with 5098 additions and 5266 deletions
+4416 -4417
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -82,7 +82,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 224,
impl_version: 0,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
};
+66 -270
View File
@@ -19,305 +19,101 @@
use super::*;
use frame_system::RawOrigin;
use sp_io::hashing::blake2_256;
use frame_benchmarking::{
BenchmarkResults, BenchmarkParameter, Benchmarking, BenchmarkingSetup, benchmarking,
};
use frame_benchmarking::{benchmarks, account};
use sp_runtime::traits::{Bounded, Dispatchable};
use crate::Module as Balances;
// Support Functions
fn account<T: Trait>(name: &'static str, index: u32) -> T::AccountId {
let entropy = (name, index).using_encoded(blake2_256);
T::AccountId::decode(&mut &entropy[..]).unwrap_or_default()
}
const SEED: u32 = 0;
const MAX_EXISTENTIAL_DEPOSIT: u32 = 1000;
const MAX_USER_INDEX: u32 = 1000;
// Benchmark `transfer` extrinsic with the worst possible conditions:
// * Transfer will kill the sender account.
// * Transfer will create the recipient account.
struct Transfer;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for Transfer {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Existential Deposit Multiplier
(BenchmarkParameter::E, 2, 1000),
// User Seed
(BenchmarkParameter::U, 1, 1000),
]
benchmarks! {
_ {
let e in 2 .. MAX_EXISTENTIAL_DEPOSIT => ();
let u in 1 .. MAX_USER_INDEX => ();
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Constants
let ed = T::ExistentialDeposit::get();
// Benchmark `transfer` extrinsic with the worst possible conditions:
// * Transfer will kill the sender account.
// * Transfer will create the recipient account.
transfer {
let u in ...;
let e in ...;
// Select an account
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
let user = account::<T>("user", u);
let user_origin = RawOrigin::Signed(user.clone());
let existential_deposit = T::ExistentialDeposit::get();
let caller = account("caller", u, SEED);
// Give some multiple of the existential deposit + creation fee + transfer fee
let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1;
let balance = ed.saturating_mul(e.into());
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&user, balance);
let balance = existential_deposit.saturating_mul(e.into());
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, balance);
// Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user.
let recipient = account::<T>("recipient", u);
let recipient = account("recipient", u, SEED);
let recipient_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(recipient);
let transfer_amt = ed.saturating_mul((e - 1).into()) + 1.into();
let transfer_amount = existential_deposit.saturating_mul((e - 1).into()) + 1.into();
}: _(RawOrigin::Signed(caller), recipient_lookup, transfer_amount)
// Return the `transfer` call
Ok((crate::Call::<T>::transfer(recipient_lookup, transfer_amt), user_origin))
}
}
// Benchmark `transfer` with the best possible condition:
// * Both accounts exist and will continue to exist.
transfer_best_case {
let u in ...;
let e in ...;
// Benchmark `transfer` with the best possible condition:
// * Both accounts exist and will continue to exist.
struct TransferBestCase;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for TransferBestCase {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Existential Deposit Multiplier
(BenchmarkParameter::E, 2, 1000),
// User Seed
(BenchmarkParameter::U, 1, 1000),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Constants
let ed = T::ExistentialDeposit::get();
// Select a sender
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
let user = account::<T>("user", u);
let user_origin = RawOrigin::Signed(user.clone());
// Select a recipient
let recipient = account::<T>("recipient", u);
let caller = account("caller", u, SEED);
let recipient: T::AccountId = account("recipient", u, SEED);
let recipient_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(recipient.clone());
// Get the existential deposit multiplier
let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1;
// Give the sender account max funds for transfer (their account will never reasonably be killed).
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&user, T::Balance::max_value());
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value());
// Give the recipient account existential deposit (thus their account already exists).
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&recipient, ed);
let existential_deposit = T::ExistentialDeposit::get();
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&recipient, existential_deposit);
let transfer_amount = existential_deposit.saturating_mul(e.into());
}: transfer(RawOrigin::Signed(caller), recipient_lookup, transfer_amount)
// Transfer e * existential deposit.
let transfer_amt = ed.saturating_mul(e.into());
// Benchmark `transfer_keep_alive` with the worst possible condition:
// * The recipient account is created.
transfer_keep_alive {
let u in ...;
let e in ...;
// Return the `transfer` call
Ok((crate::Call::<T>::transfer(recipient_lookup, transfer_amt), user_origin))
}
}
// Benchmark `transfer_keep_alive` with the worst possible condition:
// * The recipient account is created.
struct TransferKeepAlive;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for TransferKeepAlive {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Existential Deposit Multiplier
(BenchmarkParameter::E, 2, 1000),
// User Seed
(BenchmarkParameter::U, 1, 1000),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Constants
let ed = T::ExistentialDeposit::get();
// Select a sender
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
let user = account::<T>("user", u);
let user_origin = RawOrigin::Signed(user.clone());
// Select a recipient
let recipient = account::<T>("recipient", u);
let recipient_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(recipient.clone());
// Get the existential deposit multiplier
let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1;
let caller = account("caller", u, SEED);
let recipient = account("recipient", u, SEED);
let recipient_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(recipient);
// Give the sender account max funds, thus a transfer will not kill account.
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&user, T::Balance::max_value());
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value());
let existential_deposit = T::ExistentialDeposit::get();
let transfer_amount = existential_deposit.saturating_mul(e.into());
}: _(RawOrigin::Signed(caller), recipient_lookup, transfer_amount)
// Transfer e * existential deposit.
let transfer_amt = ed.saturating_mul(e.into());
// Benchmark `set_balance` coming from ROOT account. This always creates an account.
set_balance {
let u in ...;
let e in ...;
// Return the `transfer_keep_alive` call
Ok((crate::Call::<T>::transfer_keep_alive(recipient_lookup, transfer_amt), user_origin))
}
}
// Benchmark `set_balance` coming from ROOT account. This always creates an account.
struct SetBalance;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for SetBalance {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Existential Deposit Multiplier
(BenchmarkParameter::E, 2, 1000),
// User Seed
(BenchmarkParameter::U, 1, 1000),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Constants
let ed = T::ExistentialDeposit::get();
// Select a sender
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
let user = account::<T>("user", u);
let user: T::AccountId = account("user", u, SEED);
let user_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(user.clone());
// Get the existential deposit multiplier for free and reserved
let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1;
let balance_amt = ed.saturating_mul(e.into());
// Give the user some initial balance.
let existential_deposit = T::ExistentialDeposit::get();
let balance_amount = existential_deposit.saturating_mul(e.into());
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&user, balance_amount);
}: _(RawOrigin::Root, user_lookup, balance_amount, balance_amount)
// Return the `set_balance` call
Ok((crate::Call::<T>::set_balance(user_lookup, balance_amt, balance_amt), RawOrigin::Root))
}
}
// Benchmark `set_balance` coming from ROOT account. This always kills an account.
set_balance_killing {
let u in ...;
let e in ...;
// Benchmark `set_balance` coming from ROOT account. This always kills an account.
struct SetBalanceKilling;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for SetBalanceKilling {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Existential Deposit Multiplier
(BenchmarkParameter::E, 2, 1000),
// User Seed
(BenchmarkParameter::U, 1, 1000),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Constants
let ed = T::ExistentialDeposit::get();
// Select a sender
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
let user = account::<T>("user", u);
let user: T::AccountId = account("user", u, SEED);
let user_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(user.clone());
// Get the existential deposit multiplier for free and reserved
let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1;
// Give the user some initial balance
let balance_amt = ed.saturating_mul(e.into());
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&user, balance_amt);
// Return the `set_balance` call that will kill the account
Ok((crate::Call::<T>::set_balance(user_lookup, 0.into(), 0.into()), RawOrigin::Root))
}
}
// The list of available benchmarks for this pallet.
enum SelectedBenchmark {
Transfer,
TransferBestCase,
TransferKeepAlive,
SetBalance,
SetBalanceKilling,
}
// Allow us to select a benchmark from the list of available benchmarks.
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for SelectedBenchmark {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
match self {
Self::Transfer => <Transfer as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&Transfer),
Self::TransferBestCase => <TransferBestCase as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&TransferBestCase),
Self::TransferKeepAlive => <TransferKeepAlive as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&TransferKeepAlive),
Self::SetBalance => <SetBalance as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&SetBalance),
Self::SetBalanceKilling => <SetBalanceKilling as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&SetBalanceKilling),
}
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
match self {
Self::Transfer => <Transfer as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::instance(&Transfer, components),
Self::TransferBestCase => <TransferBestCase as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::instance(&TransferBestCase, components),
Self::TransferKeepAlive => <TransferKeepAlive as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::instance(&TransferKeepAlive, components),
Self::SetBalance => <SetBalance as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::instance(&SetBalance, components),
Self::SetBalanceKilling => <SetBalanceKilling as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::instance(&SetBalanceKilling, components),
}
}
}
impl<T: Trait> Benchmarking<BenchmarkResults> for Module<T> {
fn run_benchmark(extrinsic: Vec<u8>, steps: u32, repeat: u32) -> Result<Vec<BenchmarkResults>, &'static str> {
// Map the input to the selected benchmark.
let selected_benchmark = match extrinsic.as_slice() {
b"transfer" => SelectedBenchmark::Transfer,
b"transfer_best_case" => SelectedBenchmark::TransferBestCase,
b"transfer_keep_alive" => SelectedBenchmark::TransferKeepAlive,
b"set_balance" => SelectedBenchmark::SetBalance,
b"set_balance_killing" => SelectedBenchmark::SetBalanceKilling,
_ => return Err("Could not find extrinsic."),
};
// Warm up the DB
benchmarking::commit_db();
benchmarking::wipe_db();
let components = <SelectedBenchmark as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&selected_benchmark);
// results go here
let mut results: Vec<BenchmarkResults> = Vec::new();
// Select the component we will be benchmarking. Each component will be benchmarked.
for (name, low, high) in components.iter() {
// Create up to `STEPS` steps for that component between high and low.
let step_size = ((high - low) / steps).max(1);
let num_of_steps = (high - low) / step_size;
for s in 0..num_of_steps {
// This is the value we will be testing for component `name`
let component_value = low + step_size * s;
// Select the mid value for all the other components.
let c: Vec<(BenchmarkParameter, u32)> = components.iter()
.map(|(n, l, h)|
(*n, if n == name { component_value } else { (h - l) / 2 + l })
).collect();
// Run the benchmark `repeat` times.
for _r in 0..repeat {
// Set up the externalities environment for the setup we want to benchmark.
let (call, caller) = <SelectedBenchmark as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::instance(&selected_benchmark, &c)?;
// Commit the externalities to the database, flushing the DB cache.
// This will enable worst case scenario for reading from the database.
benchmarking::commit_db();
// Run the benchmark.
let start = benchmarking::current_time();
call.dispatch(caller.clone().into())?;
let finish = benchmarking::current_time();
let elapsed = finish - start;
sp_std::if_std!{
if let RawOrigin::Signed(who) = caller.clone() {
let balance = Account::<T>::get(&who).free;
println!("Free Balance {:?}", balance);
}
}
results.push((c.clone(), elapsed));
// Wipe the DB back to the genesis state.
benchmarking::wipe_db();
}
}
}
return Ok(results);
}
}
// Give the user some initial balance.
let existential_deposit = T::ExistentialDeposit::get();
let balance_amount = existential_deposit.saturating_mul(e.into());
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&user, balance_amount);
}: set_balance(RawOrigin::Root, user_lookup, 0.into(), 0.into())
}
+1
View File
@@ -10,6 +10,7 @@ codec = { package = "parity-scale-codec", version = "1.1.2", default-features =
sp-api = { version = "2.0.0", path = "../../primitives/api", default-features = false }
sp-runtime-interface = { version = "2.0.0", path = "../../primitives/runtime-interface", default-features = false }
sp-std = { version = "2.0.0", path = "../../primitives/std", default-features = false }
sp-io ={ path = "../../primitives/io", default-features = false }
[features]
default = [ "std" ]
+382 -64
View File
@@ -14,79 +14,396 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Interfaces and types for benchmarking a FRAME runtime.
//! Macro for benchmarking a FRAME runtime.
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::vec::Vec;
mod utils;
pub use utils::*;
/// An alphabet of possible parameters to use for benchmarking.
#[derive(codec::Encode, codec::Decode, Clone, Copy, PartialEq, Debug)]
/// Construct pallet benchmarks for weighing dispatchables.
///
/// Works around the idea of complexity parameters, named by a single letter (which is usually
/// upper cased in complexity notation but is lower-cased for use in this macro).
///
/// Complexity parameters ("parameters") have a range which is a `u32` pair. Every time a benchmark
/// is prepared and run, this parameter takes a concrete value within the range. There is an
/// associated instancing block, which is a single expression that is evaluated during
/// preparation. It may use `?` (`i.e. `return Err(...)`) to bail with a string error. Here's a
/// few examples:
///
/// ```ignore
/// // These two are equivalent:
/// let x in 0 .. 10;
/// let x in 0 .. 10 => ();
/// // This one calls a setup function and might return an error (which would be terminal).
/// let y in 0 .. 10 => setup(y)?;
/// // This one uses a code block to do lots of stuff:
/// let z in 0 .. 10 => {
/// let a = z * z / 5;
/// let b = do_something(a)?;
/// combine_into(z, b);
/// }
/// ```
///
/// Note that due to parsing restrictions, if the `from` expression is not a single token (i.e. a
/// literal or constant), then it must be parenthesised.
///
/// The macro allows for a number of "arms", each representing an individual benchmark. Using the
/// simple syntax, the associated dispatchable function maps 1:1 with the benchmark and the name of
/// the benchmark is the same as that of the associated function. However, extended syntax allows
/// for arbitrary expresions to be evaluated in a benchmark (including for example,
/// `on_initialize`).
///
/// The macro allows for common parameters whose ranges and instancing expressions may be drawn upon
/// (or not) by each arm. Syntax is available to allow for only the range to be drawn upon if
/// desired, allowing an alternative instancing expression to be given.
///
/// Each arm may also have a block of code which is run prior to any instancing and a block of code
/// which is run afterwards. All code blocks may draw upon the specific value of each parameter
/// at any time. Local variables are shared between the two pre- and post- code blocks, but do not
/// leak from the interior of any instancing expressions.
///
/// Any common parameters that are unused in an arm do not have their instancing expressions
/// evaluated.
///
/// Example:
/// ```ignore
/// benchmarks! {
/// // common parameter; just one for this example.
/// _ {
/// let l in 1 .. MAX_LENGTH => initialize_l(l);
/// }
///
/// // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of
/// // size `l`, which we allow to be initialised as usual.
/// foo {
/// let caller = account::<T>(b"caller", 0, benchmarks_seed);
/// let l = ...;
/// } _(Origin::Signed(caller), vec![0u8; l])
///
/// // second dispatchable: bar; this is a root dispatchable and accepts a `u8` vector of size
/// // `l`. We don't want it preininitialised like before so we override using the `=> ()`
/// // notation.
/// // In this case, we explicitly name the call using `bar` instead of `_`.
/// bar {
/// let l = _ .. _ => ();
/// } bar(Origin::Root, vec![0u8; l])
///
/// // third dispatchable: baz; this is a user dispatchable. It isn't dependent on length like the
/// // other two but has its own complexity `c` that needs setting up. It uses `caller` (in the
/// // pre-instancing block) within the code block. This is only allowed in the param instancers
/// // of arms. Instancers of common params cannot optimistically draw upon hypothetical variables
/// // that the arm's pre-instancing code block might have declared.
/// baz1 {
/// let caller = account::<T>(b"caller", 0, benchmarks_seed);
/// let c = 0 .. 10 => setup_c(&caller, c);
/// } baz(Origin::Signed(caller))
///
/// // this is a second benchmark of the baz dispatchable with a different setup.
/// baz2 {
/// let caller = account::<T>(b"caller", 0, benchmarks_seed);
/// let c = 0 .. 10 => setup_c_in_some_other_way(&caller, c);
/// } baz(Origin::Signed(caller))
///
/// // this is benchmarking some code that is not a dispatchable.
/// populate_a_set {
/// let x in 0 .. 10_000;
/// let mut m = Vec::<u32>::new();
/// for i in 0..x {
/// m.insert(i);
/// }
/// } { m.into_iter().collect::<BTreeSet>() }
/// }
/// ```
#[macro_export]
macro_rules! benchmarks {
(
_ {
$(
let $common:ident in $common_from:tt .. $common_to:expr => $common_instancer:expr;
)*
}
$( $rest:tt )*
) => {
$crate::benchmarks_iter!({
$( { $common , $common_from , $common_to , $common_instancer } )*
} ( ) $( $rest )* );
}
}
#[macro_export]
macro_rules! impl_benchmark {
(
$( $name:ident ),*
) => {
impl<T: Trait> $crate::Benchmarking<$crate::BenchmarkResults> for Module<T> {
fn run_benchmark(extrinsic: Vec<u8>, steps: u32, repeat: u32) -> Result<Vec<$crate::BenchmarkResults>, &'static str> {
// Map the input to the selected benchmark.
let extrinsic = sp_std::str::from_utf8(extrinsic.as_slice())
.map_err(|_| "Could not find extrinsic")?;
let selected_benchmark = match extrinsic {
$( stringify!($name) => SelectedBenchmark::$name, )*
_ => return Err("Could not find extrinsic."),
};
// Warm up the DB
$crate::benchmarking::commit_db();
$crate::benchmarking::wipe_db();
// first one is set_identity.
let components = <SelectedBenchmark as $crate::BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&selected_benchmark);
// results go here
let mut results: Vec<$crate::BenchmarkResults> = Vec::new();
// Select the component we will be benchmarking. Each component will be benchmarked.
for (name, low, high) in components.iter() {
// Create up to `STEPS` steps for that component between high and low.
let step_size = ((high - low) / steps).max(1);
let num_of_steps = (high - low) / step_size;
for s in 0..num_of_steps {
// This is the value we will be testing for component `name`
let component_value = low + step_size * s;
// Select the mid value for all the other components.
let c: Vec<($crate::BenchmarkParameter, u32)> = components.iter()
.map(|(n, l, h)|
(*n, if n == name { component_value } else { (h - l) / 2 + l })
).collect();
// Run the benchmark `repeat` times.
for _ in 0..repeat {
// Set up the externalities environment for the setup we want to benchmark.
let (call, caller) = <SelectedBenchmark as $crate::BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::instance(&selected_benchmark, &c)?;
// Commit the externalities to the database, flushing the DB cache.
// This will enable worst case scenario for reading from the database.
$crate::benchmarking::commit_db();
// Run the benchmark.
let start = $crate::benchmarking::current_time();
call.dispatch(caller.into())?;
let finish = $crate::benchmarking::current_time();
let elapsed = finish - start;
results.push((c.clone(), elapsed));
// Wipe the DB back to the genesis state.
$crate::benchmarking::wipe_db();
}
}
}
return Ok(results);
}
}
}
}
#[macro_export]
#[allow(missing_docs)]
pub enum BenchmarkParameter {
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
}
/// Results from running benchmarks on a FRAME pallet.
/// Contains duration of the function call in nanoseconds along with the benchmark parameters
/// used for that benchmark result.
pub type BenchmarkResults = (Vec<(BenchmarkParameter, u32)>, u128);
sp_api::decl_runtime_apis! {
/// Runtime api for benchmarking a FRAME runtime.
pub trait Benchmark {
/// Dispatch the given benchmark.
fn dispatch_benchmark(
module: Vec<u8>,
extrinsic: Vec<u8>,
steps: u32,
repeat: u32,
) -> Option<Vec<BenchmarkResults>>;
macro_rules! benchmarks_iter {
// mutation arm:
(
{ $( $common:tt )* }
( $( $names:ident )* )
$name:ident { $( $code:tt )* }: _ ( $origin:expr $( , $arg:expr )* )
$( $rest:tt )*
) => {
$crate::benchmarks_iter! {
{ $( $common )* } ( $( $names )* ) $name { $( $code )* }: $name ( $origin $( , $arg )* ) $( $rest )*
}
};
// mutation arm:
(
{ $( $common:tt )* }
( $( $names:ident )* )
$name:ident { $( $code:tt )* }: $dispatch:ident ( $origin:expr $( , $arg:expr )* )
$( $rest:tt )*
) => {
$crate::benchmarks_iter! {
{ $( $common )* } ( $( $names )* ) $name { $( $code )* }: { Ok((crate::Call::<T>::$dispatch($($arg),*), $origin)) } $( $rest )*
}
};
// iteration arm:
(
{ $( $common:tt )* }
( $( $names:ident )* )
$name:ident { $( $code:tt )* }: { $eval:expr }
$( $rest:tt )*
) => {
$crate::benchmark_backend! {
$name { $( $common )* } { } { $eval } { $( $code )* }
}
$crate::benchmarks_iter!( { $( $common )* } ( $( $names )* $name ) $( $rest )* );
};
// iteration-exit arm
( { $( $common:tt )* } ( $( $names:ident )* ) ) => {
$crate::selected_benchmark!( $( $names ),* );
$crate::impl_benchmark!( $( $names ),* );
}
}
/// Interface that provides functions for benchmarking the runtime.
#[sp_runtime_interface::runtime_interface]
pub trait Benchmarking {
/// Get the number of nanoseconds passed since the UNIX epoch
///
/// WARNING! This is a non-deterministic call. Do not use this within
/// consensus critical logic.
fn current_time() -> u128 {
std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("Unix time doesn't go backwards; qed")
.as_nanos()
#[macro_export]
#[allow(missing_docs)]
macro_rules! benchmark_backend {
// parsing arms
($name:ident {
$( $common:tt )*
} {
$( PRE { $( $pre_parsed:tt )* } )*
} { $eval:expr } {
let $pre_id:tt : $pre_ty:ty = $pre_ex:expr;
$( $rest:tt )*
} ) => {
$crate::benchmark_backend! {
$name { $( $common )* } {
$( PRE { $( $pre_parsed )* } )*
PRE { $pre_id , $pre_ty , $pre_ex }
} { $eval } { $( $rest )* }
}
};
($name:ident {
$( $common:tt )*
} {
$( $parsed:tt )*
} { $eval:expr } {
let $param:ident in ( $param_from:expr ) .. $param_to:expr => $param_instancer:expr;
$( $rest:tt )*
}) => {
$crate::benchmark_backend! {
$name { $( $common )* } {
$( $parsed )*
PARAM { $param , $param_from , $param_to , $param_instancer }
} { $eval } { $( $rest )* }
}
};
// mutation arm to look after defaulting to a common param
($name:ident {
$( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )*
} {
$( $parsed:tt )*
} { $eval:expr } {
let $param:ident in ...;
$( $rest:tt )*
}) => {
$crate::benchmark_backend! {
$name {
$( { $common , $common_from , $common_to , $common_instancer } )*
} {
$( $parsed )*
} { $eval } {
let $param
in ({ $( let $common = $common_from; )* $param })
.. ({ $( let $common = $common_to; )* $param })
=> ({ $( let $common = || -> Result<(), &'static str> { $common_instancer ; Ok(()) }; )* $param()? });
$( $rest )*
}
}
};
// mutation arm to look after defaulting only the range to common param
($name:ident {
$( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )*
} {
$( $parsed:tt )*
} { $eval:expr } {
let $param:ident in _ .. _ => $param_instancer:expr ;
$( $rest:tt )*
}) => {
$crate::benchmark_backend! {
$name {
$( { $common , $common_from , $common_to , $common_instancer } )*
} {
$( $parsed )*
} { $eval } {
let $param
in ({ $( let $common = $common_from; )* $param })
.. ({ $( let $common = $common_to; )* $param })
=> $param_instancer ;
$( $rest )*
}
}
};
// mutation arm to look after a single tt for param_from.
($name:ident {
$( $common:tt )*
} {
$( $parsed:tt )*
} { $eval:expr } {
let $param:ident in $param_from:tt .. $param_to:expr => $param_instancer:expr ;
$( $rest:tt )*
}) => {
$crate::benchmark_backend! {
$name { $( $common )* } { $( $parsed )* } { $eval } {
let $param in ( $param_from ) .. $param_to => $param_instancer;
$( $rest )*
}
}
};
// mutation arm to look after the default tail of `=> ()`
($name:ident {
$( $common:tt )*
} {
$( $parsed:tt )*
} { $eval:expr } {
let $param:ident in $param_from:tt .. $param_to:expr;
$( $rest:tt )*
}) => {
$crate::benchmark_backend! {
$name { $( $common )* } { $( $parsed )* } { $eval } {
let $param in $param_from .. $param_to => ();
$( $rest )*
}
}
};
// mutation arm to look after `let _ =`
($name:ident {
$( $common:tt )*
} {
$( $parsed:tt )*
} { $eval:expr } {
let $pre_id:tt = $pre_ex:expr;
$( $rest:tt )*
}) => {
$crate::benchmark_backend! {
$name { $( $common )* } { $( $parsed )* } { $eval } {
let $pre_id : _ = $pre_ex;
$( $rest )*
}
}
};
// actioning arm
($name:ident {
$( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )*
} {
$( PRE { $pre_id:tt , $pre_ty:ty , $pre_ex:expr } )*
$( PARAM { $param:ident , $param_from:expr , $param_to:expr , $param_instancer:expr } )*
} { $eval:expr } { $( $post:tt )* } ) => {
#[allow(non_camel_case_types)]
struct $name;
#[allow(unused_variables)]
impl<T: Trait> $crate::BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for $name {
fn components(&self) -> Vec<($crate::BenchmarkParameter, u32, u32)> {
vec! [
$(
($crate::BenchmarkParameter::$param, $param_from, $param_to)
),*
]
}
fn instance(&self, components: &[($crate::BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
$(
let $common = $common_from;
)*
$(
// Prepare instance
let $param = components.iter().find(|&c| c.0 == $crate::BenchmarkParameter::$param).unwrap().1;
)*
$(
let $pre_id : $pre_ty = $pre_ex;
)*
$( $param_instancer ; )*
$( $post )*
$eval
}
}
}
/// Reset the trie database to the genesis state.
fn wipe_db(&mut self) {
self.wipe()
}
/// Commit pending storage changes to the trie database and clear the database cache.
fn commit_db(&mut self) {
self.commit()
}
}
/// The pallet benchmarking trait.
pub trait Benchmarking<T> {
/// Run the benchmarks for this pallet.
///
/// Parameters
/// - `extrinsic`: The name of extrinsic function you want to benchmark encoded as bytes.
/// - `steps`: The number of sample points you want to take across the range of parameters.
/// - `repeat`: The number of times you want to repeat a benchmark.
fn run_benchmark(extrinsic: Vec<u8>, steps: u32, repeat: u32) -> Result<Vec<T>, &'static str>;
}
/// The required setup for creating a benchmark.
pub trait BenchmarkingSetup<T, Call, RawOrigin> {
/// Return the components and their ranges which should be tested in this benchmark.
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>;
/// Set up the storage, and prepare a call and caller to test in a single run of the benchmark.
fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(Call, RawOrigin), &'static str>;
}
/// Creates a `SelectedBenchmark` enum implementing `BenchmarkingSetup`.
@@ -109,6 +426,7 @@ macro_rules! selected_benchmark {
$( $bench:ident ),*
) => {
// The list of available benchmarks for this pallet.
#[allow(non_camel_case_types)]
enum SelectedBenchmark {
$( $bench, )*
}
+97
View File
@@ -0,0 +1,97 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Interfaces, types and utils for benchmarking a FRAME runtime.
use codec::{Encode, Decode};
use sp_std::vec::Vec;
use sp_io::hashing::blake2_256;
/// An alphabet of possible parameters to use for benchmarking.
#[derive(codec::Encode, codec::Decode, Clone, Copy, PartialEq, Debug)]
#[allow(missing_docs)]
#[allow(non_camel_case_types)]
pub enum BenchmarkParameter {
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
}
/// Results from running benchmarks on a FRAME pallet.
/// Contains duration of the function call in nanoseconds along with the benchmark parameters
/// used for that benchmark result.
pub type BenchmarkResults = (Vec<(BenchmarkParameter, u32)>, u128);
sp_api::decl_runtime_apis! {
/// Runtime api for benchmarking a FRAME runtime.
pub trait Benchmark {
/// Dispatch the given benchmark.
fn dispatch_benchmark(
module: Vec<u8>,
extrinsic: Vec<u8>,
steps: u32,
repeat: u32,
) -> Option<Vec<BenchmarkResults>>;
}
}
/// Interface that provides functions for benchmarking the runtime.
#[sp_runtime_interface::runtime_interface]
pub trait Benchmarking {
/// Get the number of nanoseconds passed since the UNIX epoch
///
/// WARNING! This is a non-deterministic call. Do not use this within
/// consensus critical logic.
fn current_time() -> u128 {
std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("Unix time doesn't go backwards; qed")
.as_nanos()
}
/// Reset the trie database to the genesis state.
fn wipe_db(&mut self) {
self.wipe()
}
/// Commit pending storage changes to the trie database and clear the database cache.
fn commit_db(&mut self) {
self.commit()
}
}
/// The pallet benchmarking trait.
pub trait Benchmarking<T> {
/// Run the benchmarks for this pallet.
///
/// Parameters
/// - `extrinsic`: The name of extrinsic function you want to benchmark encoded as bytes.
/// - `steps`: The number of sample points you want to take across the range of parameters.
/// - `repeat`: The number of times you want to repeat a benchmark.
fn run_benchmark(extrinsic: Vec<u8>, steps: u32, repeat: u32) -> Result<Vec<T>, &'static str>;
}
/// The required setup for creating a benchmark.
pub trait BenchmarkingSetup<T, Call, RawOrigin> {
/// Return the components and their ranges which should be tested in this benchmark.
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>;
/// Set up the storage, and prepare a call and caller to test in a single run of the benchmark.
fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(Call, RawOrigin), &'static str>;
}
/// Grab an account, seeded by a name and index.
pub fn account<AccountId: Decode + Default>(name: &'static str, index: u32, seed: u32) -> AccountId {
let entropy = (name, index, seed).using_encoded(blake2_256);
AccountId::decode(&mut &entropy[..]).unwrap_or_default()
}
+111 -429
View File
@@ -20,10 +20,7 @@ use super::*;
use frame_system::RawOrigin;
use sp_io::hashing::blake2_256;
use frame_benchmarking::{
BenchmarkResults, BenchmarkParameter, selected_benchmark, benchmarking, Benchmarking,
BenchmarkingSetup,
};
use frame_benchmarking::benchmarks;
use sp_runtime::traits::{Bounded, Dispatchable};
use crate::Module as Identity;
@@ -90,150 +87,89 @@ fn create_identity_info<T: Trait>(num_fields: u32) -> IdentityInfo {
return info
}
// Benchmark `add_registrar` extrinsic.
struct AddRegistrar;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for AddRegistrar {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
]
benchmarks! {
// These are the common parameters along with their instancing.
_ {
let r in 1 .. MAX_REGISTRARS => add_registrars::<T>(r)?;
let s in 1 .. T::MaxSubAccounts::get() => {
// Give them s many sub accounts
let caller = account::<T>("caller", 0);
let _ = add_sub_accounts::<T>(caller, s)?;
};
let x in 1 .. T::MaxAdditionalFields::get() => {
// Create their main identity with x additional fields
let info = create_identity_info::<T>(x);
let caller = account::<T>("caller", 0);
let caller_origin = <T as frame_system::Trait>::Origin::from(RawOrigin::Signed(caller));
Identity::<T>::set_identity(caller_origin, info)?;
};
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Add r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
add_registrar {
let r in ...;
}: _(RawOrigin::Root, account::<T>("registrar", r + 1))
// Return the `add_registrar` r + 1 call
Ok((crate::Call::<T>::add_registrar(account::<T>("registrar", r + 1)), RawOrigin::Root))
}
}
set_identity {
let r in ...;
// This X doesn't affect the caller ID up front like with the others, so we don't use the
// standard preparation.
let x in _ .. _ => ();
let caller = {
// The target user
let caller = account::<T>("caller", 0);
let caller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(caller.clone());
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Benchmark `set_identity` extrinsic.
struct SetIdentity;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for SetIdentity {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
// Additional Field Count
(BenchmarkParameter::X, 1, T::MaxAdditionalFields::get())
]
}
// Add an initial identity
let initial_info = create_identity_info::<T>(1);
Identity::<T>::set_identity(caller_origin.clone(), initial_info)?;
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Add r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
// User requests judgement from all the registrars, and they approve
for i in 0..r {
Identity::<T>::request_judgement(caller_origin.clone(), i, 10.into())?;
Identity::<T>::provide_judgement(
RawOrigin::Signed(account::<T>("registrar", i)).into(),
i,
caller_lookup.clone(),
Judgement::Reasonable
)?;
}
caller
};
}: _(
RawOrigin::Signed(caller),
create_identity_info::<T>(x)
)
// The target user
let caller = account::<T>("caller", r);
let caller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(caller.clone());
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
set_subs {
let s in ...;
// Add an initial identity
let initial_info = create_identity_info::<T>(1);
Identity::<T>::set_identity(caller_origin.clone(), initial_info)?;
// User requests judgement from all the registrars, and they approve
for i in 0..r {
Identity::<T>::request_judgement(caller_origin.clone(), i, 10.into())?;
Identity::<T>::provide_judgement(
RawOrigin::Signed(account::<T>("registrar", i)).into(),
i,
caller_lookup.clone(),
Judgement::Reasonable
)?;
}
// Create identity info with x additional fields
let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1;
// 32 byte data that we reuse below
let info = create_identity_info::<T>(x);
// Return the `set_identity` call
Ok((crate::Call::<T>::set_identity(info), RawOrigin::Signed(caller)))
}
}
// Benchmark `set_subs` extrinsic.
struct SetSubs;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for SetSubs {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Subs Count
(BenchmarkParameter::S, 1, T::MaxSubAccounts::get()),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Generic data to be used.
let data = Data::Raw(vec![0; 32]);
// The target user
let caller = account::<T>("caller", 0);
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Create their main identity
let info = create_identity_info::<T>(1);
Identity::<T>::set_identity(caller_origin.clone(), info)?;
// Give them s many sub accounts
let s = components.iter().find(|&c| c.0 == BenchmarkParameter::S).unwrap().1;
let mut subs = add_sub_accounts::<T>(caller.clone(), s)?;
Identity::<T>::set_identity(caller_origin, info)?;
}: _(RawOrigin::Signed(caller), {
let mut subs = Module::<T>::subs(&caller);
// Generic data to be used.
let data = Data::Raw(vec![0; 32]);
// Create an s+1 sub account to add
subs.push((account::<T>("sub", s+1), data));
subs.push((account::<T>("sub", s + 1), data));
subs
})
// Return the `set_subs` call
Ok((crate::Call::<T>::set_subs(subs), RawOrigin::Signed(caller)))
}
}
// Benchmark `clear_identity` extrinsic.
struct ClearIdentity;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for ClearIdentity {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
// Subs Count
(BenchmarkParameter::S, 1, T::MaxSubAccounts::get()),
// Additional Field Count
(BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// The target user
clear_identity {
let caller = account::<T>("caller", 0);
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
let caller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(caller.clone());
let caller_origin = <T as frame_system::Trait>::Origin::from(RawOrigin::Signed(caller.clone()));
let caller_lookup = <T::Lookup as StaticLookup>::unlookup(caller.clone());
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Register r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
// Create their main identity with x additional fields
let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1;
let info = create_identity_info::<T>(x);
Identity::<T>::set_identity(caller_origin.clone(), info)?;
// Give them s many sub accounts
let s = components.iter().find(|&c| c.0 == BenchmarkParameter::S).unwrap().1;
let _ = add_sub_accounts::<T>(caller.clone(), s)?;
let r in ...;
let s in ...;
let x in ...;
// User requests judgement from all the registrars, and they approve
for i in 0..r {
@@ -245,260 +181,87 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
Judgement::Reasonable
)?;
}
}: _(RawOrigin::Signed(caller))
// Return the `clear_identity` call
Ok((crate::Call::<T>::clear_identity(), RawOrigin::Signed(caller)))
}
}
// Benchmark `request_judgement` extrinsic.
struct RequestJudgement;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for RequestJudgement {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
// Additional Field Count
(BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// The target user
let caller = account::<T>("caller", 0);
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Register r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
// Create their main identity with x additional fields
let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1;
let info = create_identity_info::<T>(x);
Identity::<T>::set_identity(caller_origin.clone(), info)?;
// Return the `request_judgement` call
Ok((crate::Call::<T>::request_judgement(r-1, 10.into()), RawOrigin::Signed(caller)))
}
}
// Benchmark `cancel_request` extrinsic.
struct CancelRequest;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for CancelRequest {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
// Additional Field Count
(BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// The target user
let caller = account::<T>("caller", 0);
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Register r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
// Create their main identity with x additional fields
let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1;
let info = create_identity_info::<T>(x);
Identity::<T>::set_identity(caller_origin.clone(), info)?;
// Request judgement
Identity::<T>::request_judgement(caller_origin.clone(), r-1, 10.into())?;
Ok((crate::Call::<T>::cancel_request(r-1), RawOrigin::Signed(caller)))
}
}
// Benchmark `set_fee` extrinsic.
struct SetFee;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for SetFee {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// The target user
request_judgement {
let caller = account::<T>("caller", 0);
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Register r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
let r in ...;
let x in ...;
}: _(RawOrigin::Signed(caller), r - 1, 10.into())
cancel_request {
let caller = account::<T>("caller", 0);
let caller_origin = <T as frame_system::Trait>::Origin::from(RawOrigin::Signed(caller.clone()));
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
let r in ...;
let x in ...;
Identity::<T>::request_judgement(caller_origin, r - 1, 10.into())?;
}: _(RawOrigin::Signed(caller), r - 1)
set_fee {
let caller = account::<T>("caller", 0);
let r in ...;
// Add caller as registrar
Identity::<T>::add_registrar(RawOrigin::Root.into(), caller.clone())?;
}: _(RawOrigin::Signed(caller), r, 10.into())
// Return `set_fee` call
Ok((crate::Call::<T>::set_fee(r, 10.into()), RawOrigin::Signed(caller)))
}
}
// Benchmark `set_account_id` extrinsic.
struct SetAccountId;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for SetAccountId {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// The target user
set_account_id {
let caller = account::<T>("caller", 0);
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Register r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
let r in ...;
// Add caller as registrar
Identity::<T>::add_registrar(RawOrigin::Root.into(), caller.clone())?;
}: _(RawOrigin::Signed(caller), r, account::<T>("new", 0))
// Return `set_account_id` call
Ok((crate::Call::<T>::set_account_id(r, account::<T>("new", 0)), RawOrigin::Signed(caller)))
}
}
// Benchmark `set_fields` extrinsic.
struct SetFields;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for SetFields {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// The target user
set_fields {
let caller = account::<T>("caller", 0);
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Register r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
let r in ...;
// Add caller as registrar
Identity::<T>::add_registrar(RawOrigin::Root.into(), caller.clone())?;
let fields = IdentityFields(
IdentityField::Display | IdentityField::Legal | IdentityField::Web | IdentityField::Riot
| IdentityField::Email | IdentityField::PgpFingerprint | IdentityField::Image | IdentityField::Twitter
);
}: _(RawOrigin::Signed(caller), r, fields)
// Return `set_account_id` call
Ok((crate::Call::<T>::set_fields(r, fields), RawOrigin::Signed(caller)))
}
}
// Benchmark `provide_judgement` extrinsic.g
struct ProvideJudgement;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for ProvideJudgement {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
// Additional Field Count
(BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// Add r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
provide_judgement {
// The user
let user = account::<T>("user", r);
let user_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(user.clone()).into();
let user_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(user.clone());
let user_origin = <T as frame_system::Trait>::Origin::from(RawOrigin::Signed(user.clone()));
let user_lookup = <T::Lookup as StaticLookup>::unlookup(user.clone());
let _ = T::Currency::make_free_balance_be(&user, BalanceOf::<T>::max_value());
// Create their main identity with x additional fields
let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1;
let info = create_identity_info::<T>(x);
Identity::<T>::set_identity(user_origin.clone(), info)?;
// The caller registrar
let caller = account::<T>("caller", r);
let caller = account::<T>("caller", 0);
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Add caller as registrar
let r in ...;
// For this x, it's the user identity that gts the fields, not the caller.
let x in _ .. _ => {
let info = create_identity_info::<T>(x);
Identity::<T>::set_identity(user_origin.clone(), info)?;
};
Identity::<T>::add_registrar(RawOrigin::Root.into(), caller.clone())?;
// User requests judgement from caller registrar
Identity::<T>::request_judgement(user_origin.clone(), r, 10.into())?;
}: _(RawOrigin::Signed(caller), r, user_lookup, Judgement::Reasonable)
// Return `provide_judgement` call
Ok((crate::Call::<T>::provide_judgement(
r,
user_lookup.clone(),
Judgement::Reasonable
), RawOrigin::Signed(caller)))
}
}
// Benchmark `kill_identity` extrinsic.
struct KillIdentity;
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for KillIdentity {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Registrar Count
(BenchmarkParameter::R, 1, MAX_REGISTRARS),
// Subs Count
(BenchmarkParameter::S, 1, T::MaxSubAccounts::get()),
// Additional Field Count
(BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()),
]
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
{
// The target user
kill_identity {
let caller = account::<T>("caller", 0);
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
let caller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(caller.clone());
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
// Register r registrars
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
add_registrars::<T>(r)?;
// Create their main identity with x additional fields
let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1;
let info = create_identity_info::<T>(x);
Identity::<T>::set_identity(caller_origin.clone(), info)?;
// Give them s many sub accounts
let s = components.iter().find(|&c| c.0 == BenchmarkParameter::S).unwrap().1;
let _ = add_sub_accounts::<T>(caller.clone(), s)?;
let r in ...;
let s in ...;
let x in ...;
// User requests judgement from all the registrars, and they approve
for i in 0..r {
@@ -510,86 +273,5 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
Judgement::Reasonable
)?;
}
// Return the `kill_identity` call
Ok((crate::Call::<T>::kill_identity(caller_lookup), RawOrigin::Root))
}
}
// The list of available benchmarks for this pallet.
selected_benchmark!(
AddRegistrar,
SetIdentity,
SetSubs,
ClearIdentity,
RequestJudgement,
CancelRequest,
SetFee,
SetAccountId,
SetFields,
ProvideJudgement,
KillIdentity
);
impl<T: Trait> Benchmarking<BenchmarkResults> for Module<T> {
fn run_benchmark(extrinsic: Vec<u8>, steps: u32, repeat: u32) -> Result<Vec<BenchmarkResults>, &'static str> {
// Map the input to the selected benchmark.
let selected_benchmark = match extrinsic.as_slice() {
b"add_registrar" => SelectedBenchmark::AddRegistrar,
b"set_identity" => SelectedBenchmark::SetIdentity,
b"set_subs" => SelectedBenchmark::SetSubs,
b"clear_identity" => SelectedBenchmark::ClearIdentity,
b"request_judgement" => SelectedBenchmark::RequestJudgement,
b"cancel_request" => SelectedBenchmark::CancelRequest,
b"set_fee" => SelectedBenchmark::SetFee,
b"set_account_id" => SelectedBenchmark::SetAccountId,
b"set_fields" => SelectedBenchmark::SetFields,
b"provide_judgement" => SelectedBenchmark::ProvideJudgement,
b"kill_identity" => SelectedBenchmark::KillIdentity,
_ => return Err("Could not find extrinsic."),
};
// Warm up the DB
benchmarking::commit_db();
benchmarking::wipe_db();
// first one is set_identity.
let components = <SelectedBenchmark as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&selected_benchmark);
// results go here
let mut results: Vec<BenchmarkResults> = Vec::new();
// Select the component we will be benchmarking. Each component will be benchmarked.
for (name, low, high) in components.iter() {
// Create up to `STEPS` steps for that component between high and low.
let step_size = ((high - low) / steps).max(1);
let num_of_steps = (high - low) / step_size;
for s in 0..num_of_steps {
// This is the value we will be testing for component `name`
let component_value = low + step_size * s;
// Select the mid value for all the other components.
let c: Vec<(BenchmarkParameter, u32)> = components.iter()
.map(|(n, l, h)|
(*n, if n == name { component_value } else { (h - l) / 2 + l })
).collect();
// Run the benchmark `repeat` times.
for _ in 0..repeat {
// Set up the externalities environment for the setup we want to benchmark.
let (call, caller) = <SelectedBenchmark as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::instance(&selected_benchmark, &c)?;
// Commit the externalities to the database, flushing the DB cache.
// This will enable worst case scenario for reading from the database.
benchmarking::commit_db();
// Run the benchmark.
let start = benchmarking::current_time();
call.dispatch(caller.into())?;
let finish = benchmarking::current_time();
let elapsed = finish - start;
results.push((c.clone(), elapsed));
// Wipe the DB back to the genesis state.
benchmarking::wipe_db();
}
}
}
return Ok(results);
}
}: _(RawOrigin::Root, caller_lookup)
}
+15 -5
View File
@@ -394,7 +394,7 @@ decl_storage! {
/// Alternative "sub" identities of this account.
///
/// The first item is the deposit, the second is a vector of the accounts.
pub SubsOf get(fn subs):
pub SubsOf get(fn subs_of):
map hasher(blake2_256) T::AccountId => (BalanceOf<T>, Vec<T::AccountId>);
/// The set of registrars. Not expected to get very big as can only be added through a
@@ -875,6 +875,16 @@ decl_module! {
}
}
impl<T: Trait> Module<T> {
/// Get the subs of an account.
pub fn subs(who: &T::AccountId) -> Vec<(T::AccountId, Data)> {
SubsOf::<T>::get(who).1
.into_iter()
.filter_map(|a| SuperOf::<T>::get(&a).map(|x| (a, x.1)))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -1097,14 +1107,14 @@ mod tests {
assert_ok!(Identity::set_identity(Origin::signed(10), ten()));
assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone()));
assert_eq!(Balances::free_balance(10), 80);
assert_eq!(Identity::subs(10), (10, vec![20]));
assert_eq!(Identity::subs_of(10), (10, vec![20]));
assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1]))));
// push another item and re-set it.
subs.push((30, Data::Raw(vec![50; 1])));
assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone()));
assert_eq!(Balances::free_balance(10), 70);
assert_eq!(Identity::subs(10), (20, vec![20, 30]));
assert_eq!(Identity::subs_of(10), (20, vec![20, 30]));
assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1]))));
assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1]))));
@@ -1112,7 +1122,7 @@ mod tests {
subs[0] = (40, Data::Raw(vec![60; 1]));
assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone()));
assert_eq!(Balances::free_balance(10), 70); // no change in the balance
assert_eq!(Identity::subs(10), (20, vec![40, 30]));
assert_eq!(Identity::subs_of(10), (20, vec![40, 30]));
assert_eq!(Identity::super_of(20), None);
assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1]))));
assert_eq!(Identity::super_of(40), Some((10, Data::Raw(vec![60; 1]))));
@@ -1120,7 +1130,7 @@ mod tests {
// clear
assert_ok!(Identity::set_subs(Origin::signed(10), vec![]));
assert_eq!(Balances::free_balance(10), 90);
assert_eq!(Identity::subs(10), (0, vec![]));
assert_eq!(Identity::subs_of(10), (0, vec![]));
assert_eq!(Identity::super_of(30), None);
assert_eq!(Identity::super_of(40), None);
+9 -80
View File
@@ -21,88 +21,17 @@ use super::*;
use sp_std::prelude::*;
use frame_system::RawOrigin;
use frame_benchmarking::{
BenchmarkResults, BenchmarkParameter, selected_benchmark, benchmarking,
Benchmarking, BenchmarkingSetup,
};
use frame_benchmarking::benchmarks;
use sp_runtime::traits::Dispatchable;
/// Benchmark `set` extrinsic.
struct Set;
impl<T: Trait> BenchmarkingSetup<T, Call<T>, RawOrigin<T::AccountId>> for Set {
fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> {
vec![
// Current time ("Now")
(BenchmarkParameter::N, 1, 100),
]
const MAX_TIME: u32 = 100;
benchmarks! {
_ {
let n in 1 .. MAX_TIME => ();
}
fn instance(&self, components: &[(BenchmarkParameter, u32)])
-> Result<(Call<T>, RawOrigin<T::AccountId>), &'static str>
{
let user_origin = RawOrigin::None;
let now = components.iter().find(|&c| c.0 == BenchmarkParameter::N).unwrap().1;
// Return the `set` call
Ok((Call::<T>::set(now.into()), user_origin))
}
}
selected_benchmark!(Set);
impl<T: Trait> Benchmarking<BenchmarkResults> for Module<T> {
fn run_benchmark(extrinsic: Vec<u8>, steps: u32, repeat: u32) -> Result<Vec<BenchmarkResults>, &'static str> {
// Map the input to the selected benchmark.
let selected_benchmark = match extrinsic.as_slice() {
b"set" => SelectedBenchmark::Set,
_ => return Err("Could not find extrinsic."),
};
// Warm up the DB
benchmarking::commit_db();
benchmarking::wipe_db();
let components = <SelectedBenchmark as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&selected_benchmark);
let mut results: Vec<BenchmarkResults> = Vec::new();
// Select the component we will be benchmarking. Each component will be benchmarked.
for (name, low, high) in components.iter() {
// Create up to `STEPS` steps for that component between high and low.
let step_size = ((high - low) / steps).max(1);
let num_of_steps = (high - low) / step_size;
for s in 0..num_of_steps {
// This is the value we will be testing for component `name`
let component_value = low + step_size * s;
// Select the mid value for all the other components.
let c: Vec<(BenchmarkParameter, u32)> = components.iter()
.map(|(n, l, h)|
(*n, if n == name { component_value } else { (h - l) / 2 + l })
).collect();
// Run the benchmark `repeat` times.
for _ in 0..repeat {
// Set up the externalities environment for the setup we want to benchmark.
let (call, caller) = <SelectedBenchmark as BenchmarkingSetup<
T,
Call<T>,
RawOrigin<T::AccountId>,
>>::instance(&selected_benchmark, &c)?;
// Commit the externalities to the database, flushing the DB cache.
// This will enable worst case scenario for reading from the database.
benchmarking::commit_db();
// Run the benchmark.
let start = benchmarking::current_time();
call.dispatch(caller.into())?;
let finish = benchmarking::current_time();
let elapsed = finish - start;
results.push((c.clone(), elapsed));
// Wipe the DB back to the genesis state.
benchmarking::wipe_db();
}
}
}
return Ok(results);
}
set {
let n in ...;
}: _(RawOrigin::None, n.into())
}