referrerpolicy=no-referrer-when-downgrade

frame_benchmarking_cli/extrinsic/
cmd.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use sc_block_builder::BlockBuilderApi;
19use sc_cli::{CliConfiguration, ImportParams, Result, SharedParams};
20use sc_client_api::UsageProvider;
21use sp_api::{ApiExt, CallApiAt, ProvideRuntimeApi};
22use sp_runtime::{traits::Block as BlockT, DigestItem, OpaqueExtrinsic};
23
24use clap::{Args, Parser};
25use log::info;
26use serde::Serialize;
27use std::{fmt::Debug, sync::Arc};
28
29use super::{
30	bench::{Benchmark, BenchmarkParams},
31	extrinsic_factory::ExtrinsicFactory,
32};
33
34/// Benchmark the execution time of different extrinsics.
35///
36/// This is calculated by filling a block with a specific extrinsic and executing the block.
37/// The result time is then divided by the number of extrinsics in that block.
38///
39/// NOTE: The BlockExecutionWeight is ignored  in this case since it
40// is very small compared to the total block execution time.
41#[derive(Debug, Parser)]
42pub struct ExtrinsicCmd {
43	#[allow(missing_docs)]
44	#[clap(flatten)]
45	pub shared_params: SharedParams,
46
47	#[allow(missing_docs)]
48	#[clap(flatten)]
49	pub import_params: ImportParams,
50
51	#[allow(missing_docs)]
52	#[clap(flatten)]
53	pub params: ExtrinsicParams,
54}
55
56/// The params for the [`ExtrinsicCmd`].
57#[derive(Debug, Default, Serialize, Clone, PartialEq, Args)]
58pub struct ExtrinsicParams {
59	#[clap(flatten)]
60	pub bench: BenchmarkParams,
61
62	/// List all available pallets and extrinsics.
63	///
64	/// The format is CSV with header `pallet, extrinsic`.
65	#[arg(long)]
66	pub list: bool,
67
68	/// Pallet name of the extrinsic to benchmark.
69	#[arg(long, value_name = "PALLET", required_unless_present = "list")]
70	pub pallet: Option<String>,
71
72	/// Extrinsic to benchmark.
73	#[arg(long, value_name = "EXTRINSIC", required_unless_present = "list")]
74	pub extrinsic: Option<String>,
75
76	/// Enable the Trie cache.
77	///
78	/// This should only be used for performance analysis and not for final results.
79	#[arg(long)]
80	pub enable_trie_cache: bool,
81}
82
83impl ExtrinsicCmd {
84	/// Benchmark the execution time of a specific type of extrinsic.
85	///
86	/// The output will be printed to console.
87	pub fn run<Block, C>(
88		&self,
89		client: Arc<C>,
90		inherent_data: sp_inherents::InherentData,
91		digest_items: Vec<DigestItem>,
92		ext_factory: &ExtrinsicFactory,
93	) -> Result<()>
94	where
95		Block: BlockT<Extrinsic = OpaqueExtrinsic>,
96		C: ProvideRuntimeApi<Block>
97			+ CallApiAt<Block>
98			+ UsageProvider<Block>
99			+ sp_blockchain::HeaderBackend<Block>,
100		C::Api: ApiExt<Block> + BlockBuilderApi<Block>,
101	{
102		// Short circuit if --list was specified.
103		if self.params.list {
104			let list: Vec<String> = ext_factory.0.iter().map(|b| b.name()).collect();
105			info!(
106				"Listing available extrinsics ({}):\npallet, extrinsic\n{}",
107				list.len(),
108				list.join("\n")
109			);
110			return Ok(())
111		}
112
113		let pallet = self.params.pallet.clone().unwrap_or_default();
114		let extrinsic = self.params.extrinsic.clone().unwrap_or_default();
115		let ext_builder = match ext_factory.try_get(&pallet, &extrinsic) {
116			Some(ext_builder) => ext_builder,
117			None =>
118				return Err("Unknown pallet or extrinsic. Use --list for a complete list.".into()),
119		};
120
121		let bench =
122			Benchmark::new(client, self.params.bench.clone(), inherent_data, digest_items, false);
123		let stats = bench.bench_extrinsic(ext_builder)?;
124		info!(
125			"Executing a {}::{} extrinsic takes[ns]:\n{:?}",
126			ext_builder.pallet(),
127			ext_builder.extrinsic(),
128			stats
129		);
130
131		Ok(())
132	}
133}
134
135// Boilerplate
136impl CliConfiguration for ExtrinsicCmd {
137	fn shared_params(&self) -> &SharedParams {
138		&self.shared_params
139	}
140
141	fn import_params(&self) -> Option<&ImportParams> {
142		Some(&self.import_params)
143	}
144
145	fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
146		if self.params.enable_trie_cache {
147			Ok(self.import_params().map(|x| x.trie_cache_maximum_size()).unwrap_or_default())
148		} else {
149			Ok(None)
150		}
151	}
152}