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_names
extrinsic 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))]
.WeightInfo
for the pallet, we must manually create trait for ()
. To do so, it is possible to copy the WeightInfo
trait for SubstrateWeight<T>
from the generated file and replace T::DbWeight::
by 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 define_benchmark
.
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>;
.