frame_benchmarking_cli/block/
bench.rs1use codec::DecodeAll;
21use frame_support::weights::constants::WEIGHT_REF_TIME_PER_NANOS;
22use frame_system::ConsumedWeight;
23use sc_block_builder::BlockBuilderApi;
24use sc_cli::{Error, Result};
25use sc_client_api::{
26 Backend as ClientBackend, BlockBackend, HeaderBackend, StorageProvider, UsageProvider,
27};
28use sp_api::{ApiExt, Core, ProvideRuntimeApi};
29use sp_blockchain::Error::RuntimeApiError;
30use sp_runtime::{
31 generic::BlockId,
32 traits::{Block as BlockT, Header as HeaderT},
33 DigestItem, OpaqueExtrinsic,
34};
35use sp_storage::StorageKey;
36
37use clap::Args;
38use log::{info, warn};
39use serde::Serialize;
40use std::{fmt::Debug, marker::PhantomData, sync::Arc, time::Instant};
41use thousands::Separable;
42
43use crate::shared::{StatSelect, Stats};
44
45const LOG_TARGET: &'static str = "benchmark::block::weight";
47
48#[derive(Debug, Default, Serialize, Clone, PartialEq, Args)]
50pub struct BenchmarkParams {
51 #[arg(long)]
53 pub from: u32,
54
55 #[arg(long)]
57 pub to: u32,
58
59 #[arg(long, default_value_t = 10)]
61 pub repeat: u32,
62}
63
64pub struct Benchmark<Block, BA, C> {
66 client: Arc<C>,
67 params: BenchmarkParams,
68 _p: PhantomData<(Block, BA, C)>,
69}
70
71type NanoSeconds = u64;
73
74impl<Block, BA, C> Benchmark<Block, BA, C>
75where
76 Block: BlockT<Extrinsic = OpaqueExtrinsic>,
77 BA: ClientBackend<Block>,
78 C: ProvideRuntimeApi<Block>
79 + StorageProvider<Block, BA>
80 + UsageProvider<Block>
81 + BlockBackend<Block>
82 + HeaderBackend<Block>,
83 C::Api: ApiExt<Block> + BlockBuilderApi<Block>,
84{
85 pub fn new(client: Arc<C>, params: BenchmarkParams) -> Self {
87 Self { client, params, _p: PhantomData }
88 }
89
90 pub fn run(&self) -> Result<()> {
92 if self.params.from == 0 {
93 return Err("Cannot benchmark the genesis block".into())
94 }
95
96 for i in self.params.from..=self.params.to {
97 let block_num = BlockId::Number(i.into());
98 let hash = self.client.expect_block_hash_from_id(&block_num)?;
99 let consumed = self.consumed_weight(hash)?;
100
101 let block = self.client.block(hash)?.ok_or(format!("Block {} not found", block_num))?;
102 let block = self.unsealed(block.block);
103 let took = self.measure_block(&block, *block.header().parent_hash())?;
104
105 self.log_weight(i, block.extrinsics().len(), consumed, took);
106 }
107
108 Ok(())
109 }
110
111 fn measure_block(&self, block: &Block, parent_hash: Block::Hash) -> Result<NanoSeconds> {
113 let mut record = Vec::<NanoSeconds>::default();
114 for _ in 0..self.params.repeat {
117 let block = block.clone();
118 let runtime_api = self.client.runtime_api();
119 let start = Instant::now();
120
121 runtime_api
122 .execute_block(parent_hash, block)
123 .map_err(|e| Error::Client(RuntimeApiError(e)))?;
124
125 record.push(start.elapsed().as_nanos() as NanoSeconds);
126 }
127
128 let took = Stats::new(&record)?.select(StatSelect::Average);
129 Ok(took)
130 }
131
132 fn consumed_weight(&self, block_hash: Block::Hash) -> Result<NanoSeconds> {
137 let hash = array_bytes::hex2bytes(
140 "26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96",
141 )?;
142 let key = StorageKey(hash);
143
144 let mut raw_weight = &self
145 .client
146 .storage(block_hash, &key)?
147 .ok_or(format!("Could not find System::BlockWeight for block: {}", block_hash))?
148 .0[..];
149
150 let weight = ConsumedWeight::decode_all(&mut raw_weight)?;
151 Ok((weight.total().ref_time() as f64 / WEIGHT_REF_TIME_PER_NANOS as f64).floor()
153 as NanoSeconds)
154 }
155
156 fn log_weight(&self, num: u32, num_ext: usize, consumed: NanoSeconds, took: NanoSeconds) {
158 let percent = (took as f64 / consumed as f64) * 100.0;
161
162 let msg = format!(
163 "Block {} with {: >5} tx used {: >6.2}% of its weight ({: >14} of {: >14} ns)",
164 num,
165 num_ext,
166 percent,
167 took.separate_with_commas(),
168 consumed.separate_with_commas()
169 );
170
171 if took <= consumed {
172 info!(target: LOG_TARGET, "{}", msg);
173 } else {
174 warn!(target: LOG_TARGET, "{} - OVER WEIGHT!", msg);
175 }
176 }
177
178 fn unsealed(&self, block: Block) -> Block {
180 let (mut header, exts) = block.deconstruct();
181 header.digest_mut().logs.retain(|item| !matches!(item, DigestItem::Seal(_, _)));
182 Block::new(header, exts)
183 }
184}