Skip to content
This repository was archived by the owner on Jul 4, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
19b67cd
Export GRANDPA AuthorityPair when full_crypto is enabled (#4872)
h4x3rotab Feb 10, 2020
8b6b093
Create Benchmarking Setup for Identity Pallet #4695 (#4818)
shawntabrizi Feb 10, 2020
f735d3e
Fix vesting logic (#4864)
gavofyork Feb 10, 2020
7e8ac2e
Update trie-db to the latest (#4874)
cecton Feb 10, 2020
ae03ee9
Fix timer panics in the wasm light client (#4561)
expenses Feb 10, 2020
0d4586b
Don't expose `Benchmarking` host functions by default (#4875)
bkchr Feb 10, 2020
571e3c4
Add trace on import block. (#4871)
seerscode Feb 10, 2020
1fa3f7f
Refactor and document allocator (#4855)
pepyakin Feb 10, 2020
d472cd6
Avoid challenging those that can't be suspended anyway (#4804)
gavofyork Feb 10, 2020
b388338
Add trait to get module and call names. (#4854)
seerscode Feb 10, 2020
26a4b73
Fix runtime-interface tests on windows (#4805)
bkchr Feb 10, 2020
613a3bc
update primitive types to 0.6.2 (#4866)
NikVolf Feb 10, 2020
671cb85
Run offchain workers at hash, not number. (#4878)
tomusdrw Feb 10, 2020
50bb62f
Use prefixed iterator from trie. (#4858)
cheme Feb 11, 2020
a5a61df
Add support for json output in subkey (#4882)
hbakkum-dotstar Feb 11, 2020
1dd662b
impl Randomness trait for Babe and remove unused RandomBeacon trait (…
rphmeier Feb 11, 2020
60f0569
Pause Kademlia if too many connections (#4828)
tomaka Feb 11, 2020
657484a
Refactor tx factory 1 (#4870)
seerscode Feb 11, 2020
7647c39
pallet-evm: optional nonce parameter (#4893)
sorpaas Feb 11, 2020
d0e354a
Increase the penality for being offline (#4889)
tomaka Feb 11, 2020
00a400f
Add a sub command to generate a node key file (#4884)
hbakkum-dotstar Feb 11, 2020
10332c9
Benchmark Timestamp Pallet (#4891)
seerscode Feb 12, 2020
f5f7852
Add command-line flag to enable yamux flow control. (#4892)
twittner Feb 12, 2020
2723b9c
Do not allow zero Existential Deposit when using Balances (#4894)
shawntabrizi Feb 12, 2020
ea721a1
network: Use "one shot" protocol handler. (#3520)
twittner Feb 12, 2020
2290645
Benchmark the Balances Pallet (#4879)
shawntabrizi Feb 12, 2020
e1668c2
client/network-gossip: Integrate GossipEngine tasks into Future impl …
mxinden Feb 12, 2020
b955d17
add some more docs on PreRuntime digests (#4896)
rphmeier Feb 12, 2020
1b42f24
serialize partial_fee into string (#4898)
xlc Feb 12, 2020
47076a9
add sr25519 bench (#4905)
NikVolf Feb 12, 2020
d78534e
Fix chain-spec and make sure it does not breaks again (#4906)
bkchr Feb 13, 2020
b01bd0d
Full block import benchmark (#4865)
NikVolf Feb 13, 2020
d940c02
Per-things trait. (#4904)
kianenigma Feb 13, 2020
0b2ae97
executor: Migrate wasmtime backend to a high-level API (#4686)
pepyakin Feb 13, 2020
b36497a
reference sc-service with rocksdb feature (#4918)
NikVolf Feb 13, 2020
067c884
pallet-evm: add support for transaction-level create2 (#4907)
sorpaas Feb 13, 2020
7d544ef
pallet-evm: refactor duplicate code in call/create/create2 (#4922)
sorpaas Feb 13, 2020
d02c720
Adds a test to ensure that we clear the heap between calls into runti…
bkchr Feb 14, 2020
29454c3
Composite accounts (#4820)
gavofyork Feb 14, 2020
a6e7c05
Allow to distinguish out of gas from other traps (#4883)
athei Feb 14, 2020
16bb192
Adds fork-awareness and finalization notifications to transaction poo…
seunlanlege Feb 14, 2020
b27d50c
Adds `with_pair!` macro to application-crypto (#4885)
bkchr Feb 14, 2020
877e193
Remove rename for finalized event and add some docs. (#4930)
tomusdrw Feb 15, 2020
7aba7ff
Merged commit '877e193b314d42c9d77bb1740e70656b26bd0ff9' from Substrate
hoani Feb 16, 2020
9469920
Reverted #4820 (substrate), add doughnut and delegatedDispatchVerifie…
hoani Feb 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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 <[email protected]>
  • Loading branch information
shawntabrizi and gavofyork authored Feb 12, 2020
commit 2290645fb253cd6629525be775cbbb6bd43658e8
1 change: 1 addition & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
322 changes: 322 additions & 0 deletions frame/balances/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
1 change: 1 addition & 0 deletions frame/balances/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down