Files
pezkuwi-subxt/substrate/frame/balances/src/benchmarking.rs
T
Sam Johnson 55ff791d80 generation of real benchmark functions for benchmarking v2 (#13224)
* function generation with _name working, need to modify signature

* WIP

* support custom BenchmarkResult<T> type

* full support for BenchmarkResult<T> on benchmark function defs

* support () return type for benchmark function defs that don't use ?

* uncomment

* fix where clause handling

* fix benchmark function call bodies

* proper parsing of return type

* add UI tests for bad return type

* fix detection of missing last_stmt with defined return type

* UI tests covering missing last_stmt

* properly detect and complain about empty benchmark function defs

* fix missing Comma in Result<T, BenchmarkError> parsing + test

* add additional UI test

* allow complex path for BenchmarkResult and BenchmarkError in fn defs

* add UI tests covering complex path for BenchmarkResult, BenchmarkError

* retain doc comments and attributes

* also add attributes to struct

* add docs for benchmark function definition support

* fix imports on benchmark example

* fix issue with unused variables in extrinsic call fn def

* fix up docs

* remove support for v2::BenchmarkResult because it was confusing

* fix typo

* remove ability to use custom T for Result<T, BenchmarkError> in v2

* use missing call error instead of empty_fn()

* remove unneeded match statement

* Add a proper QED

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* fix other QED

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* cargo fmt

* add an explicit error for non TypePath as return type

* tweak error warning and add a UI test for non TypePath return

* remove comment

* add docs about T and I generic params

* improve docs referring to section "below"

* pull out return type checking logic into its own function

* pull out params parsing into its own function

* pull out call_def parsing into its own function

* add doc comment for missing_call()

* replace spaces with tabs

* add a result-based example to the benchmarking examples

---------

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
2023-02-22 14:09:11 +00:00

257 lines
9.9 KiB
Rust

// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Balances pallet benchmarking.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use crate::Pallet as Balances;
use frame_benchmarking::v2::*;
use frame_system::RawOrigin;
const SEED: u32 = 0;
// existential deposit multiplier
const ED_MULTIPLIER: u32 = 10;
#[instance_benchmarks]
mod benchmarks {
use super::*;
// Benchmark `transfer` extrinsic with the worst possible conditions:
// * Transfer will kill the sender account.
// * Transfer will create the recipient account.
#[benchmark]
fn transfer() {
let existential_deposit = T::ExistentialDeposit::get();
let caller = whitelisted_caller();
// Give some multiple of the existential deposit
let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
let _ = <Balances<T, I> 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: T::AccountId = account("recipient", 0, SEED);
let recipient_lookup = T::Lookup::unlookup(recipient.clone());
let transfer_amount =
existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into();
#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount);
assert_eq!(Balances::<T, I>::free_balance(&caller), Zero::zero());
assert_eq!(Balances::<T, I>::free_balance(&recipient), transfer_amount);
}
// Benchmark `transfer` with the best possible condition:
// * Both accounts exist and will continue to exist.
#[benchmark(extra)]
fn transfer_best_case() {
let caller = whitelisted_caller();
let recipient: T::AccountId = account("recipient", 0, SEED);
let recipient_lookup = T::Lookup::unlookup(recipient.clone());
// Give the sender account max funds for transfer (their account will never reasonably be
// killed).
let _ =
<Balances<T, I> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value());
// Give the recipient account existential deposit (thus their account already exists).
let existential_deposit = T::ExistentialDeposit::get();
let _ =
<Balances<T, I> as Currency<_>>::make_free_balance_be(&recipient, existential_deposit);
let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
#[extrinsic_call]
transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount);
assert!(!Balances::<T, I>::free_balance(&caller).is_zero());
assert!(!Balances::<T, I>::free_balance(&recipient).is_zero());
}
// Benchmark `transfer_keep_alive` with the worst possible condition:
// * The recipient account is created.
#[benchmark]
fn transfer_keep_alive() {
let caller = whitelisted_caller();
let recipient: T::AccountId = account("recipient", 0, SEED);
let recipient_lookup = T::Lookup::unlookup(recipient.clone());
// Give the sender account max funds, thus a transfer will not kill account.
let _ =
<Balances<T, I> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value());
let existential_deposit = T::ExistentialDeposit::get();
let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount);
assert!(!Balances::<T, I>::free_balance(&caller).is_zero());
assert_eq!(Balances::<T, I>::free_balance(&recipient), transfer_amount);
}
// Benchmark `set_balance` coming from ROOT account. This always creates an account.
#[benchmark]
fn set_balance_creating() {
let user: T::AccountId = account("user", 0, SEED);
let user_lookup = T::Lookup::unlookup(user.clone());
// Give the user some initial balance.
let existential_deposit = T::ExistentialDeposit::get();
let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
let _ = <Balances<T, I> as Currency<_>>::make_free_balance_be(&user, balance_amount);
#[extrinsic_call]
set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount);
assert_eq!(Balances::<T, I>::free_balance(&user), balance_amount);
assert_eq!(Balances::<T, I>::reserved_balance(&user), balance_amount);
}
// Benchmark `set_balance` coming from ROOT account. This always kills an account.
#[benchmark]
fn set_balance_killing() {
let user: T::AccountId = account("user", 0, SEED);
let user_lookup = T::Lookup::unlookup(user.clone());
// Give the user some initial balance.
let existential_deposit = T::ExistentialDeposit::get();
let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
let _ = <Balances<T, I> as Currency<_>>::make_free_balance_be(&user, balance_amount);
#[extrinsic_call]
set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero());
assert!(Balances::<T, I>::free_balance(&user).is_zero());
}
// Benchmark `force_transfer` extrinsic with the worst possible conditions:
// * Transfer will kill the sender account.
// * Transfer will create the recipient account.
#[benchmark]
fn force_transfer() {
let existential_deposit = T::ExistentialDeposit::get();
let source: T::AccountId = account("source", 0, SEED);
let source_lookup = T::Lookup::unlookup(source.clone());
// Give some multiple of the existential deposit
let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
let _ = <Balances<T, I> as Currency<_>>::make_free_balance_be(&source, balance);
// Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account,
// and reap this user.
let recipient: T::AccountId = account("recipient", 0, SEED);
let recipient_lookup = T::Lookup::unlookup(recipient.clone());
let transfer_amount =
existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into();
#[extrinsic_call]
_(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount);
assert_eq!(Balances::<T, I>::free_balance(&source), Zero::zero());
assert_eq!(Balances::<T, I>::free_balance(&recipient), transfer_amount);
}
// This benchmark performs the same operation as `transfer` in the worst case scenario,
// but additionally introduces many new users into the storage, increasing the the merkle
// trie and PoV size.
#[benchmark(extra)]
fn transfer_increasing_users(u: Linear<0, 1_000>) {
// 1_000 is not very much, but this upper bound can be controlled by the CLI.
let existential_deposit = T::ExistentialDeposit::get();
let caller = whitelisted_caller();
// Give some multiple of the existential deposit
let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
let _ = <Balances<T, I> 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: T::AccountId = account("recipient", 0, SEED);
let recipient_lookup = T::Lookup::unlookup(recipient.clone());
let transfer_amount =
existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into();
// Create a bunch of users in storage.
for i in 0..u {
// The `account` function uses `blake2_256` to generate unique accounts, so these
// should be quite random and evenly distributed in the trie.
let new_user: T::AccountId = account("new_user", i, SEED);
let _ = <Balances<T, I> as Currency<_>>::make_free_balance_be(&new_user, balance);
}
#[extrinsic_call]
transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount);
assert_eq!(Balances::<T, I>::free_balance(&caller), Zero::zero());
assert_eq!(Balances::<T, I>::free_balance(&recipient), transfer_amount);
}
// Benchmark `transfer_all` with the worst possible condition:
// * The recipient account is created
// * The sender is killed
#[benchmark]
fn transfer_all() {
let caller = whitelisted_caller();
let recipient: T::AccountId = account("recipient", 0, SEED);
let recipient_lookup = T::Lookup::unlookup(recipient.clone());
// Give some multiple of the existential deposit
let existential_deposit = T::ExistentialDeposit::get();
let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
let _ = <Balances<T, I> as Currency<_>>::make_free_balance_be(&caller, balance);
#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), recipient_lookup, false);
assert!(Balances::<T, I>::free_balance(&caller).is_zero());
assert_eq!(Balances::<T, I>::free_balance(&recipient), balance);
}
#[benchmark]
fn force_unreserve() -> Result<(), BenchmarkError> {
let user: T::AccountId = account("user", 0, SEED);
let user_lookup = T::Lookup::unlookup(user.clone());
// Give some multiple of the existential deposit
let existential_deposit = T::ExistentialDeposit::get();
let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
let _ = <Balances<T, I> as Currency<_>>::make_free_balance_be(&user, balance);
// Reserve the balance
<Balances<T, I> as ReservableCurrency<_>>::reserve(&user, balance)?;
assert_eq!(Balances::<T, I>::reserved_balance(&user), balance);
assert!(Balances::<T, I>::free_balance(&user).is_zero());
#[extrinsic_call]
_(RawOrigin::Root, user_lookup, balance);
assert!(Balances::<T, I>::reserved_balance(&user).is_zero());
assert_eq!(Balances::<T, I>::free_balance(&user), balance);
Ok(())
}
impl_benchmark_test_suite! {
Balances,
crate::tests_composite::ExtBuilder::default().build(),
crate::tests_composite::Test,
}
}