mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Benchmark the Balances Pallet (#4879)
* Initial transfer bench * Add best case * Transfer keep alive * Set balance benchmarks * Bump impl * Fix text Co-authored-by: Gavin Wood <github@gavwood.com>
This commit is contained in:
@@ -812,6 +812,7 @@ impl_runtime_apis! {
|
||||
-> Option<Vec<BenchmarkResults>>
|
||||
{
|
||||
match module.as_slice() {
|
||||
b"pallet-balances" | b"balances" => Balances::run_benchmark(extrinsic, steps, repeat).ok(),
|
||||
b"pallet-identity" | b"identity" => Identity::run_benchmark(extrinsic, steps, repeat).ok(),
|
||||
b"pallet-timestamp" | b"timestamp" => Timestamp::run_benchmark(extrinsic, steps, repeat).ok(),
|
||||
_ => return None,
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
// 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/>.
|
||||
|
||||
//! Balances pallet benchmarking.
|
||||
|
||||
use super::*;
|
||||
|
||||
use frame_system::RawOrigin;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_runtime::{BenchmarkResults, BenchmarkParameter};
|
||||
use sp_runtime::traits::{Bounded, Benchmarking, BenchmarkingSetup, 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()
|
||||
}
|
||||
|
||||
// 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),
|
||||
]
|
||||
}
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
// Constants
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
|
||||
// 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());
|
||||
|
||||
// Give some multiple of the existential deposit + creation fee + transfer fee
|
||||
let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1;
|
||||
let mut balance = ed.saturating_mul(e.into());
|
||||
balance += T::CreationFee::get();
|
||||
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&user, 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_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(recipient);
|
||||
let transfer_amt = ed.saturating_mul((e - 1).into()) + 1.into();
|
||||
|
||||
// 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.
|
||||
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 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());
|
||||
|
||||
// Give the recipient account existential deposit (thus their account already exists).
|
||||
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&recipient, ed);
|
||||
|
||||
// Transfer e * existential deposit.
|
||||
let transfer_amt = ed.saturating_mul(e.into());
|
||||
|
||||
// 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;
|
||||
|
||||
// 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());
|
||||
|
||||
// Transfer e * existential deposit.
|
||||
let transfer_amt = ed.saturating_mul(e.into());
|
||||
|
||||
// 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_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());
|
||||
|
||||
// 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.
|
||||
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_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
|
||||
sp_io::benchmarking::commit_db();
|
||||
sp_io::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.
|
||||
sp_io::benchmarking::commit_db();
|
||||
// Run the benchmark.
|
||||
let start = sp_io::benchmarking::current_time();
|
||||
call.dispatch(caller.clone().into())?;
|
||||
let finish = sp_io::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.
|
||||
sp_io::benchmarking::wipe_db();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(results);
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,7 @@ mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod migration;
|
||||
mod benchmarking;
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::{cmp, result, mem, fmt::Debug, ops::BitOr};
|
||||
|
||||
Reference in New Issue
Block a user