Add benchmarks
Please read Substrate to Polkadot SDK page first.
This guide illustrates how to write a simple benchmark for a pallet, test the benchmark, and run benchmarking commands to generate realistic estimates about the execution time required for the functions in a pallet. This guide does not cover how to use the benchmarking results to update transaction weights.
Add benchmarking to the pallet
- Open the
Cargo.tomlfile for your pallet in a text editor. -
Add the
frame-benchmarkingcrate to the [dependencies] for the pallet using the same version and branch as the other dependencies in the pallet.For example:
frame-benchmarking = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", branch = "polkadot-v1.0.0", optional = true } -
Add
runtime-benchmarksto the list of [features] for the pallet.For example:
[features] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -
Add
frame-benchmarking/stdto the list ofstdfeatures for the pallet.For example:
std = [ ... "frame-benchmarking/std", ... ]
Add a benchmarking module
- Create a new text file—for example,
benchmarking.rs—in thesrcfolder for your pallet. -
Open the
benchmarking.rsfile in a text editor and create a Rust module that defines benchmarks for your pallet.You can use the
benchmarking.rsfor any prebuilt pallet as an example of what to include in the Rust module. In general, the module should include code similar to the following:#![cfg(feature = "runtime-benchmarks")] mod benchmarking; use crate::*; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; benchmarks! { // Add individual benchmarks here benchmark_name { /* code to set the initial state */ }: { /* code to test the function benchmarked */ } verify { /* optional verification */ } } -
Write individual benchmarks to test the most computationally expensive paths for the functions in the pallet.
The benchmarking macro automatically generates a test function for each benchmark you include in the benchmarking module. For example, the macro creates test functions similar to the following:
fn test_benchmarking_[benchmark_name]<T>::() -> Result<(), &'static str>The benchmarking module for pallet-example-basic provides a few simple sample benchmarks. For example:
benchmarks! { set_dummy_benchmark { // Benchmark setup phase let b in 1 .. 1000; }: set_dummy(RawOrigin::Root, b.into()) // Execution phase verify { // Optional verification phase assert_eq!(Pallet::<T>::dummy(), Some(b.into())) } }In this sample code:
- The name of the benchmark is
set_dummy_benchmark. - The variable
bstores input that is used to test the execution time of theset_dummyfunction. - The value of
bvaries between 1 to 1,000, so you can run the benchmark test repeatedly to measure the execution time using different input values.
- The name of the benchmark is
Test the benchmarks
After you have added benchmarks to the benchmarks! macros in the benchmarking module for your pallet, you can use a mock runtime to do unit testing and ensure that the test functions for your benchmarks return Ok(()) as a result.
- Open the
benchmarking.rsbenchmarking module in a text editor. -
Add the
impl_benchmark_test_suite!macro to the bottom of your benchmarking module:impl_benchmark_test_suite!( MyPallet, crate::mock::new_test_ext(), crate::mock::Test, );The
impl_benchmark_test_suite!macro takes the following input:- The Pallet struct generated by your pallet, in this example
MyPallet. - A function that generates a test genesis storage,
new_text_ext(). - The full mock runtime struct,
Test.
This is the same information you use to set up a mock runtime for unit testing. If all benchmark tests pass in the mock runtime test environment, it's likely that they will work when you run the benchmarks in the actual runtime.
- The Pallet struct generated by your pallet, in this example
-
Execute the benchmark unit tests generated for your pallet in a mock runtime by running a command similar to the following for a pallet named
pallet-mycustom:cargo test --package pallet-mycustom --features runtime-benchmarks - Verify the test results.
For example:
running 4 tests
test mock::__construct_runtime_integrity_test::runtime_integrity_tests ... ok
test tests::it_works_for_default_value ... ok
test tests::correct_error_for_none_value ... ok
test benchmarking::bench_do_something ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sAdd benchmarking to the runtime
After you have added benchmarking to your pallet, you must also update the runtime to include the pallet and the benchmarks for the pallet.
- Open the
Cargo.tomlfile for your runtime in a text editor. -
Add your pallet to the list of
[dependencies]for the runtime:pallet-mycustom = { default-features = false, path = "../pallets/pallet-mycustom"} -
Update the
[features]for the runtime to include theruntime-benchmarksfor your pallet:[features] runtime-benchmarks = [ ... 'pallet-mycustom/runtime-benchmarks' ... ] -
Update the
stdfeatures for the runtime to include your pallet:std = [ # -- snip -- 'pallet-mycustom/std' ] - Add the configuration trait for your pallet to the runtime.
-
Add the pallet the the
construct_runtime!macro.If you need more details about adding a pallet to the runtime, see Add a pallet to the runtime or Import a pallet.
-
Add your pallet to the
define_benchmark!macro in theruntime-benchmarksfeature.#[cfg(feature = "runtime-benchmarks")] mod benches { define_benchmarks!( [frame_benchmarking, BaselineBench::<Runtime>] [pallet_assets, Assets] [pallet_babe, Babe] ... [pallet_mycustom, MyPallet] ... ); }
Run your benchmarks
After you update the runtime, you are ready to compile it with the runtime-benchmarks features enabled and start the benchmarking analysis for your pallet.
-
Build your project with the
runtime-benchmarksfeature enabled by running the following command:cargo build --package node-template --release --features runtime-benchmarks -
Review the command-line options for the node
benchmark palletsubcommand:./target/release/node-template benchmark pallet --helpThe
benchmark palletsubcommand supports several command-line options that can help you automate your benchmarking. For example, you can set the--stepsand--repeatcommand-line options to execute function calls multiple times with different values. -
Start benchmarking for your pallet by running a command similar to the following:
./target/release/node-template benchmark pallet \ --chain dev \ --pallet pallet_mycustom \ --extrinsic '*' \ --steps 20 \ --repeat 10 \ --output pallets/pallet-mycustom/src/weights.rsThis command creates a
weights.rsfile in the specified directory. For information about how to configure your pallet to use those weights, see Use custom weights.
Examples
You can use the benchmarking.rs and weights.rs files for any prebuilt pallet to learn more about benchmarking different types of functions.
