Substrate provides a way to compute the computational resources used when extrinsics and hooks are called to keep the runtime safe to attack. Substrate uses the concept of weight to represent the execution time. In this post, we will quickly see how to implement the benchmark for one pallet and, more precisely, how to use the generated weight file in the runtime using examples from the Duniter and Polkadot runtime.
The goal of benchmarking is simple: find the longest path (i.e., the more computational) of each extrinsic and use this path in the benchmark. Each benchmark is constituted of three parts:
Let’s take an example to see how it is done for a simple pallet.
The pallet im_online has one extrinsic. The complexity of this extrinsic depends on the length of the key and of heartbeat address that needs to be decoded and/or encoded.
The benchmark is defined in this file. The complexity parameters use the syntax
let k in 1 .. MAX and will be used in the
WeightInfo generated after the benchmark.
In this example, there is no verify statement; one can see that we can verify that the event
HeartbeatReceived is deposited (see this example that verify events).
For an instance pallet, i.e., a pallet with several instances, the benchmark is defined using the macro
benchmarks_instance_pallet. The process is the same as for a regular pallet; the benchmark needs to be included in the runtime for each pallet. The benchmark will generate one weight file by instance.
Running benchmarks in test mock and the runtime is documented at https://docs.substrate.io/test/benchmark/ and is relatively easy to do.
When the benchmarks run in the runtime, the next step is to use the generated weight file inside the pallet and the mock.
prune_item_identities_namesextrinsic that have one complexity parameter :
#[pallet::weight(1_000_000_000)]is replaced by
#[pallet::weight(T::WeightInfo::prune_item_identities_names(names.len() as u32))].
WeightInfofor the pallet, we must manually create trait for
(). To do so, it is possible to copy the
SubstrateWeight<T>from the generated file and replace
RocksDbWeight::(see the im_pallet for a complete example).
The last step is to include the computed weights in the runtime. We will see two runtimes design:
First, we need to replace the Substrate weights with our computed weight, for example, replacing
type WeightInfo = pallet_im_online::weights::SubstrateWeight<Runtime>; by
type WeightInfo = common_runtime::weights::pallet_im_online::WeightInfo<Runtime>;
Note: The path resolving between the name defined in the
define_benchmark macro and the
WeightInfo type can sometimes be confusing. The name defined in the macro will directly impact the trait that the benchmark will generate. For example
[pallet_im_online, ImOnline] will generate
impl<T: frame_system::Config> pallet_im_online::WeightInfo for WeightInfo<T>. Then depending on where you store the weight files and how you include and where you define the weight mod, you can add a prefix so the trait and the type
WeightInfo can resolve correctly.
In Duniter, the pallet names in the
define_benchmark are not prefixed, but the
WeightInfo in the Config trait is prefixed by
common_runtime. This is because the Config trait is defined by a macro in the common runtime with weight files stored in the common runtime. The common runtime is then included in the runtime of each chain, hence the prefix.
In contrast, Polkadot defined the Config trait in each chain or in the common runtime depending on the pallet, but all the weight files are stored in the chain folder, thus the prefix in the
In Polkadot, the pallet’s weights used in the runtime are stored in each chain. For example, for the Polkadot chain, they are placed in the folder
runtime/polkadot/src/weights/. To resolve the paths correctly, we need to define the benchmark accurately in the runtime, for example [pallet_im_online, ImOnline] in the define_benchmarks macro.
The weight mod is defined in the
mod.rs file, including all the weight files and then included in the runtime. The
WeightInfo defined as
type WeightInfo = weights::pallet_im_online::WeightInfo<Runtime>;.
In Duniter, the pallet’s weights are stored in the folder
duniter-v2s/runtime/common/src/weights/ and will be used for all the chains.
To resolve the paths correctly, we need to define the benchmark correctly in the runtime, for example, [pallet_im_online, ImOnline] in the define_benchmarks macro.
The weight mod is defined in
duniter-v2s/runtime/common/src/weights/weight.rs and included in the common runtime and will be used in each chain. The
WeightInfo defined as
type WeightInfo = common_runtime::weights::pallet_im_online::WeightInfo<Runtime>;.