owned this note changed 3 years ago
Linked with GitHub

Benchmarking and Weights for a New Pallet

Note these instructions are for a pallet authored into Substrate's frame path, and don't work for externally authored pallets yet.

Write the initial benchmarks.rs

Copy the following text into a new file benchmarking.rs, prepended with whatever licence you want:

//! Benchmarks for Whatever Pallet

#![cfg(feature = "runtime-benchmarks")]

use sp_std::prelude::*;
use super::*;
use sp_runtime::traits::Bounded;
use frame_system::RawOrigin;
use frame_benchmarking::{benchmarks, whitelisted_caller, impl_benchmark_test_suite};
use crate::Pallet as Whatever;

const SEED: u32 = 0;

benchmarks! {
}

impl_benchmark_test_suite!(
	Whatever,
	crate::mock::new_test_ext(),
	crate::mock::Test,
);

Introduce benchmarking code blocks

Inside the benchmarks! macro, introduce a block like this for each dispatchable:

	example {
		let x in 0..42;
		let caller: T::AccountId = whitelisted_caller();
        let arg = vec![1u32; x];
	}: _(RawOrigin::Signed(caller.clone()), arg)
	verify {
		assert_eq!(1, 1);
	}

Introduce let ... in ... lines for each complexity factor your dispatchable has.

Integrate benchmarks into your pallet

Ensure that your pallet is integrated into node

Note this step may already be done as part of your usual pallet authoring process.

  • Ensure that bin/node/cli/src/chain_spec.rs includes your pallet's config if needed. Note: This is only needed if your pallet has a GenesisConfig.
  • In bin/node/runtime/src/lib.rs:
    • Ensure that Runtime struct has an impl for your pallet's Config.
    • Ensure that construct_runtime! macro has a line for your pallet.
    • Ensure that fn dispatch_benchmark has a add_benchmark! macro invocation for your pallet.
  • In bin/node/runtime/Cargo.toml:
    • Ensure that your pallet is included as a dependency.
    • Ensure that your pallet imitates the enabling of the std feature.
    • Ensure that your pallet imitates the enabling of the runtime-benchmarks feature. Don't forget this or you'll get weird build errors!

Enable benchmarks in your pallet

  • In your pallet's lib.rs, ensure you have a pub mod benchmarking; line at the top.
  • In your pallet's Cargo.toml, ensure you pass runtime-benchmarks feature down to sp-runtime, frame-support and frame-system crates, and use it to enable the runtime-benchmarks crate:
runtime-benchmarks = [
	"frame-benchmarking",
	"sp-runtime/runtime-benchmarks",
	"frame-support/runtime-benchmarks",
	"frame-system/runtime-benchmarks",
]

Testing benchmarks

Just use cargo test, but with all features enabled:

cargo test -p pallet-what-ever --all-features

Generating your trait and evaluating benchmarks

This step creates or updates your pallet's weights.rs.

This step is run to generate your pallet's Weights trait, for use in your pallet's #[pallet::weight] tags. It should also be re-run to incorporate changes to the complexities of functions or new dispatchables.

cd bin/node/cli
cargo build --release --features=runtime-benchmarks
../../../target/release/substrate benchmark \
    --chain dev \
    --steps 5 \
    --repeat 2 \
    --pallet pallet_what_ever \
    --extrinsic "*" \
    --raw --execution=wasm \
    --wasm-execution=compiled \
    --output ../../../frame/what-ever/src/weights.rs \
    --template ../../../.maintain/frame-weight-template.hbs

Replace what_ever & what-ever with your pallet's name (e.g. gilt, elections_phragmen & elections-phragmen).

Incorporate weight formulae

In your pallet's lib.rs:

  • Introduce pub mod weights; at the top.
    • Introduce pub use crate::weights::WeightInfo; to expose the WeightInfo trait from your pallet.
  • Introduce a WeightInfo type into the Config trait:
pub trait Config: frame_system::Config {
    // ...
    
    /// Information on runtime weights.
    type WeightInfo: WeightInfo;
}
  • For each of your dispatchables, introduce an appropriate weights line to determine the weight using the configured WeightInfo type, e.g.:
#[pallet::weight(T::WeightInfo::example(x.len()))]
fn example(origin: OriginFor<T>, arg: Vec<u32>) {
    //...
}

Define the WeightInfo type in Config trait impls

Additionally, you'll need to define the WeightInfo trait in both your pallet's tests mockup mock.rs and the Substrate node's runtime.

In your pallet's Config trait impl in mock.rs, introduce the line:

type WeightInfo = ();

In your pallet's Config trait impl in bin/node/runtime/src/lib.rs, introduce the line:

type WeightInfo = pallet_what_ever::weights::SubstrateWeight<Runtime>;

Integrating into other runtimes

When integrating into other runtimes, you'll want to use that runtime's custom overarching WeightInfo.

Ensure that the runtime's fn dispatch_benchmark has a add_benchmark! macro invocation for your pallet.

Initially, just use the () as the WeightInfo type for your pallet. E.g.:

impl pallet_what_ever::Config for Runtime {
	type Event = Event;
    //...
	type WeightInfo = ();
}

Run the benchmarks for the runtime as usual. This will require you building your node's CLI with --features runtime-benchmarks and then running with the appropriate CLI options as above. E.g. for Kusama:

cargo run --release --features=runtime-benchmarks -- benchmark \
  --chain=kusama-dev \
  --steps=50 \
  --repeat=20 \
  --pallet=pallet_what_ever \
  --extrinsic=* \
  --execution=wasm \
  --wasm-execution=compiled \
  --heap-pages=4096 \
  --header=./file_header.txt \
  --output=./runtime/kusama/src/weights/

Reference hardware should really be used. E.g. on the Polkadot CI to benchmark for Kusama, issue a comment:

/benchmark runtime kusama pallet_what_ever

One you have the new file, you'll want to integrate it into your runtime. First go into the runtime's weights mod.rs and include the pub mod line to use the new weight file.

E.g. for Kusama, go into runtime/kusama/src/weights/mod.rs and add to the list of pub mod lines:

pub mod pallet_what_ever;

Once that's done, you can switch the pallet's config trait impl to use the real weights:

impl pallet_what_ever::Config for Runtime {
	type Event = Event;
    //...
	type WeightInfo = weights::pallet_what_ever::WeightInfo<Runtime>;
}

That's it!

Select a repo