Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Benchmarking pallet-example #8301

Merged
merged 22 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
34853d2
Trying to benchmark pallet-example
jimmychu0807 Feb 28, 2021
19ff8c4
added the weights info in function
jimmychu0807 Mar 2, 2021
6e5902a
updated naming
jimmychu0807 Mar 8, 2021
c4c0376
updated with benchmarking and weight
jimmychu0807 Mar 9, 2021
86a3333
Merge branch 'master' into jc/example-benchmarking
jimmychu0807 Mar 9, 2021
2a3ea00
Updated test for pallet-example
jimmychu0807 Mar 9, 2021
3d68f86
adding weights.rs benchmarked in release mode
jimmychu0807 Mar 9, 2021
c436b14
Removed pallet-example from node-runtime
jimmychu0807 Mar 9, 2021
3ab9102
adjusted
jimmychu0807 Mar 9, 2021
8d2cdf9
modification based on peer comments
jimmychu0807 Mar 12, 2021
abd4507
updated so far
jimmychu0807 Mar 19, 2021
ef98f05
Merge branch 'master' into jc/example-benchmarking
jimmychu0807 Mar 19, 2021
7614873
updated
jimmychu0807 Mar 20, 2021
911fdcc
updated
jimmychu0807 Mar 20, 2021
a291e13
updated code
jimmychu0807 Mar 25, 2021
612cde9
Added explanation on impl_benchmark_test_suite! macro
jimmychu0807 Mar 25, 2021
f4ff4f7
Remove pallet-example from runtime
jimmychu0807 Mar 25, 2021
8c66979
Merge remote-tracking branch 'gh-parity/master' into jc/example-bench…
jimmychu0807 Mar 25, 2021
a103aca
rm pallet-example
jimmychu0807 Mar 25, 2021
f28e430
Update frame/example/src/benchmarking.rs
Mar 29, 2021
a697b9a
Merge remote-tracking branch 'gh-parity/master' into jc/example-bench…
jimmychu0807 Mar 29, 2021
60b614d
additional comment on manually configuring weight
jimmychu0807 Mar 29, 2021
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frame/elections-phragmen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,7 @@ mod tests {
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type SS58Prefix = ();
}

parameter_types! {
Expand Down
2 changes: 1 addition & 1 deletion frame/example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pallet-example"
version = "2.0.0"
version = "3.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "Unlicense"
Expand Down
40 changes: 40 additions & 0 deletions frame/example/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![cfg(feature = "runtime-benchmarks")]
jimmychu0807 marked this conversation as resolved.
Show resolved Hide resolved

use crate::*;

mod benchmarking {
jimmychu0807 marked this conversation as resolved.
Show resolved Hide resolved
use super::*;
use frame_benchmarking::{benchmarks, account, impl_benchmark_test_suite};
use frame_system::RawOrigin;

benchmarks!{
// This will measure the execution time of `accumulate_dummy` for b in [1..1000] range.
accumulate_dummy {
let b in 1 .. 1000;
let caller = account("caller", 0, 0);
jimmychu0807 marked this conversation as resolved.
Show resolved Hide resolved
}: _ (RawOrigin::Signed(caller), b.into())
jimmychu0807 marked this conversation as resolved.
Show resolved Hide resolved

// This will measure the execution time of `set_dummy` for b in [1..1000] range.
set_dummy {
let b in 1 .. 1000;
}: set_dummy (RawOrigin::Root, b.into())
jimmychu0807 marked this conversation as resolved.
Show resolved Hide resolved

// This will measure the execution time of `set_dummy` for b in [1..10] range.
another_set_dummy {
let b in 1 .. 10;
}: set_dummy (RawOrigin::Root, b.into())
jimmychu0807 marked this conversation as resolved.
Show resolved Hide resolved

// This will measure the execution time of sorting a vector.
sort_vector {
let x in 0 .. 10000;
let mut m = Vec::<u32>::new();
for i in (0..x).rev() {
m.push(i);
}
}: {
m.sort();
}
}

impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test);
}
218 changes: 16 additions & 202 deletions frame/example/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,14 @@
// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]

use sp_std::marker::PhantomData;
use sp_std::{
prelude::*,
marker::PhantomData
};
use frame_support::{
dispatch::DispatchResult, traits::IsSubType,
weights::{DispatchClass, ClassifyDispatch, WeighData, Weight, PaysFee, Pays},
};
use sp_std::prelude::*;
use frame_system::{ensure_signed};
use codec::{Encode, Decode};
use sp_runtime::{
Expand Down Expand Up @@ -318,6 +320,11 @@ type BalanceOf<T> = <T as pallet_balances::Config>::Balance;
// Re-export pallet items so that they can be accessed from the crate namespace.
pub use pallet::*;

mod tests;
mod benchmarking;
pub mod weights;
pub use weights::*;

// Definition of the pallet logic, to be aggregated at runtime definition through
// `construct_runtime`.
#[frame_support::pallet]
Expand All @@ -336,6 +343,7 @@ pub mod pallet {
pub trait Config: pallet_balances::Config + frame_system::Config {
/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
type WeightInfo: WeightInfo;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type WeightInfo: WeightInfo;
/// Somethign representing the weights of this pallet.
type WeightInfo: WeightInfo;

}

// Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and
Expand Down Expand Up @@ -455,7 +463,9 @@ pub mod pallet {
// difficulty) of the transaction and the latter demonstrates the [`DispatchClass`] of the
// call. A higher weight means a larger transaction (less of which can be placed in a
// single block).
#[pallet::weight(0)]
#[pallet::weight(
<T as pallet::Config>::WeightInfo::accumulate_dummy(0)
jimmychu0807 marked this conversation as resolved.
Show resolved Hide resolved
)]
pub(super) fn accumulate_dummy(
origin: OriginFor<T>,
increase_by: T::Balance
Expand Down Expand Up @@ -496,7 +506,9 @@ pub mod pallet {
// calls to be executed - we don't need to care why. Because it's privileged, we can
// assume it's a one-off operation and substantial processing/storage/memory can be used
// without worrying about gameability or attack scenarios.
#[pallet::weight(WeightForSetDummy::<T>(<BalanceOf<T>>::from(100u32)))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the documentation for WeightForSetDummy is outdated now.

Maybe we can just remove it as we prefer to just show benchmarking way, not sure. cc @kianenigma

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking it is a good way to showcasing how to set the weight: 1) thru benchmarking, 2) manually setting it via WeightForSetDummy, and it shows setting an extrinsic call as operational call.

I think it will be great showing runtime developers alternatives that they can override the measured benchmarking and tweak the weight function as they want. Will there be some scenarios that this is necessary for dev?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally what people need to understand is that pallet::weight take into argument any type which implements:

  • WeighData<(Arg1, Arg2, ..)>
  • ClassifyDispatch<(Arg1, Arg2, ..)>
  • PaysFee<(Arg1, Arg2, ..)>

where Arg1, Arg2, ..., are the type of the argument of the call.

Make we can keep WeightForSetDummy for this call.

I think if we keep WeightForSetDummy in the code and never use it, people can't understand how to use it.

Copy link
Contributor

@kianenigma kianenigma Mar 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh boy I totally have forgotten about these manual weights.

I think they can stay, only if we mention that this is an advanced feature if you want some custom functionality. Most often, you want to use WeightInfo stuff only.

If too much for now, @jimmychu0807 you can remove it in this PR, make a new Issue in the devhub for custom weight calculators and ping me there, I can help with that.

Copy link
Contributor Author

@jimmychu0807 jimmychu0807 Mar 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kianenigma We kind of have that in devhub here, though we should probably move it to the Transaction Weight document.

And the example implementation of WeightForSetDummy demonstrates what you could customize.

Added additional comment in code for readers to consider running benchmarking toolchain instead of manually setting weight unless they know what they are doing.

#[pallet::weight(
<T as pallet::Config>::WeightInfo::set_dummy((*new_value).saturated_into())
)]
fn set_dummy(
origin: OriginFor<T>,
#[pallet::compact] new_value: T::Balance,
Expand Down Expand Up @@ -691,201 +703,3 @@ where
}
}
}

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking {
use super::*;
use frame_benchmarking::{benchmarks, account, impl_benchmark_test_suite};
use frame_system::RawOrigin;

benchmarks!{
// This will measure the execution time of `accumulate_dummy` for b in [1..1000] range.
accumulate_dummy {
let b in 1 .. 1000;
let caller = account("caller", 0, 0);
}: _ (RawOrigin::Signed(caller), b.into())

// This will measure the execution time of `set_dummy` for b in [1..1000] range.
set_dummy {
let b in 1 .. 1000;
}: set_dummy (RawOrigin::Root, b.into())

// This will measure the execution time of `set_dummy` for b in [1..10] range.
another_set_dummy {
let b in 1 .. 10;
}: set_dummy (RawOrigin::Root, b.into())

// This will measure the execution time of sorting a vector.
sort_vector {
let x in 0 .. 10000;
let mut m = Vec::<u32>::new();
for i in (0..x).rev() {
m.push(i);
}
}: {
m.sort();
}
}

impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test);
}

#[cfg(test)]
mod tests {
use super::*;

use frame_support::{
assert_ok, parameter_types,
weights::{DispatchInfo, GetDispatchInfo}, traits::{OnInitialize, OnFinalize}
};
use sp_core::H256;
// The testing primitives are very useful for avoiding having to work with signatures
// or public keys. `u64` is used as the `AccountId` and no `Signature`s are required.
use sp_runtime::{
testing::Header, BuildStorage,
traits::{BlakeTwo256, IdentityLookup},
};
// Reexport crate as its pallet name for construct_runtime.
use crate as pallet_example;

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;

// For testing the pallet, we construct a mock runtime.
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Module, Call, Config, Storage, Event<T>},
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
Example: pallet_example::{Module, Call, Storage, Config<T>, Event<T>},
}
);

parameter_types! {
pub const BlockHashCount: u64 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(1024);
}
impl frame_system::Config for Test {
type BaseCallFilter = ();
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Call = Call;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
}
impl pallet_balances::Config for Test {
type MaxLocks = ();
type Balance = u64;
type DustRemoval = ();
type Event = Event;
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
}
impl Config for Test {
type Event = Event;
}

// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
pub fn new_test_ext() -> sp_io::TestExternalities {
let t = GenesisConfig {
// We use default for brevity, but you can configure as desired if needed.
frame_system: Default::default(),
pallet_balances: Default::default(),
pallet_example: pallet_example::GenesisConfig {
dummy: 42,
// we configure the map with (key, value) pairs.
bar: vec![(1, 2), (2, 3)],
foo: 24,
},
}.build_storage().unwrap();
t.into()
}

#[test]
fn it_works_for_optional_value() {
new_test_ext().execute_with(|| {
// Check that GenesisBuilder works properly.
assert_eq!(Example::dummy(), Some(42));

// Check that accumulate works when we have Some value in Dummy already.
assert_ok!(Example::accumulate_dummy(Origin::signed(1), 27));
assert_eq!(Example::dummy(), Some(69));

// Check that finalizing the block removes Dummy from storage.
<Example as OnFinalize<u64>>::on_finalize(1);
assert_eq!(Example::dummy(), None);

// Check that accumulate works when we Dummy has None in it.
<Example as OnInitialize<u64>>::on_initialize(2);
assert_ok!(Example::accumulate_dummy(Origin::signed(1), 42));
assert_eq!(Example::dummy(), Some(42));
});
}

#[test]
fn it_works_for_default_value() {
new_test_ext().execute_with(|| {
assert_eq!(Example::foo(), 24);
assert_ok!(Example::accumulate_foo(Origin::signed(1), 1));
assert_eq!(Example::foo(), 25);
});
}

#[test]
fn signed_ext_watch_dummy_works() {
new_test_ext().execute_with(|| {
let call = <pallet_example::Call<Test>>::set_dummy(10).into();
let info = DispatchInfo::default();

assert_eq!(
WatchDummy::<Test>(PhantomData).validate(&1, &call, &info, 150)
.unwrap()
.priority,
u64::max_value(),
);
assert_eq!(
WatchDummy::<Test>(PhantomData).validate(&1, &call, &info, 250),
InvalidTransaction::ExhaustsResources.into(),
);
})
}

#[test]
fn weights_work() {
// must have a defined weight.
let default_call = <pallet_example::Call<Test>>::accumulate_dummy(10);
let info = default_call.get_dispatch_info();
// aka. `let info = <Call<Test> as GetDispatchInfo>::get_dispatch_info(&default_call);`
assert_eq!(info.weight, 0);

// must have a custom weight of `100 * arg = 2000`
let custom_call = <pallet_example::Call<Test>>::set_dummy(20);
let info = custom_call.get_dispatch_info();
assert_eq!(info.weight, 2000);
}
}
Loading