referrerpolicy=no-referrer-when-downgrade

cumulus_pallet_parachain_system/
benchmarking.rs

1// This file is part of Cumulus.
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
18//! Benchmarking for the parachain-system pallet.
19
20#![cfg(feature = "runtime-benchmarks")]
21
22use super::*;
23use crate::{
24	block_weight::{
25		BlockWeightMode, DynamicMaxBlockWeight, MaxParachainBlockWeight, FULL_CORE_WEIGHT,
26	},
27	parachain_inherent::InboundDownwardMessages,
28};
29use cumulus_primitives_core::{
30	relay_chain::Hash as RelayHash, BlockBundleInfo, CoreInfo, InboundDownwardMessage,
31};
32use frame_benchmarking::v2::*;
33use frame_support::{
34	dispatch::{DispatchInfo, PostDispatchInfo},
35	weights::constants::WEIGHT_REF_TIME_PER_SECOND,
36};
37use frame_system::RawOrigin;
38use sp_core::ConstU32;
39use sp_runtime::traits::{BlakeTwo256, DispatchTransaction, Dispatchable};
40
41fn has_use_full_core_digest<T: Config>() -> bool {
42	let digest = frame_system::Pallet::<T>::digest();
43	CumulusDigestItem::contains_use_full_core(&digest)
44}
45
46#[benchmarks(where
47	T: Send + Sync,
48    T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
49)]
50mod benchmarks {
51	use super::*;
52
53	/// Enqueue `n` messages via `enqueue_inbound_downward_messages`.
54	///
55	/// The limit is set to `1000` for benchmarking purposes as the actual limit is only known at
56	/// runtime. However, the limit (and default) for Dotsama are magnitudes smaller.
57	#[benchmark]
58	fn enqueue_inbound_downward_messages(n: Linear<0, 1000>) {
59		let msg = InboundDownwardMessage {
60			sent_at: n, // The block number does not matter.
61			msg: vec![0u8; MaxDmpMessageLenOf::<T>::get() as usize],
62		};
63		let msgs = vec![msg; n as usize];
64		let head = mqp_head(&msgs);
65
66		#[block]
67		{
68			Pallet::<T>::enqueue_inbound_downward_messages(
69				head,
70				InboundDownwardMessages::new(msgs).into_abridged(&mut usize::MAX.clone()),
71			);
72		}
73
74		assert_eq!(ProcessedDownwardMessages::<T>::get(), n);
75		assert_eq!(LastDmqMqcHead::<T>::get().head(), head);
76	}
77
78	/// Re-implements an easy version of the `MessageQueueChain` for testing purposes.
79	fn mqp_head(msgs: &Vec<InboundDownwardMessage>) -> RelayHash {
80		let mut head = Default::default();
81		for msg in msgs.iter() {
82			let msg_hash = BlakeTwo256::hash_of(&msg.msg);
83			head = BlakeTwo256::hash_of(&(head, msg.sent_at, msg_hash));
84		}
85		head
86	}
87
88	/// The worst-case scenario for the block weight transaction extension.
89	///
90	/// Before executing an extrinsic `FractionOfCore` is set, changed to `PotentialFullCore` and
91	/// post dispatch switches to `FullCore`.
92	#[benchmark]
93	fn block_weight_tx_extension_max_weight() -> Result<(), BenchmarkError> {
94		let caller = account("caller", 0, 0);
95
96		frame_system::Pallet::<T>::note_inherents_applied();
97
98		frame_system::Pallet::<T>::set_extrinsic_index(1);
99
100		frame_system::Pallet::<T>::deposit_log(
101			BlockBundleInfo { index: 0, is_last: false }.to_digest_item(),
102		);
103		frame_system::Pallet::<T>::deposit_log(
104			CoreInfo {
105				selector: 0.into(),
106				claim_queue_offset: 0.into(),
107				number_of_cores: 1.into(),
108			}
109			.to_digest_item(),
110		);
111		let target_weight = MaxParachainBlockWeight::<T, ConstU32<4>>::target_block_weight();
112
113		let info = DispatchInfo {
114			// The weight needs to be more than the target weight.
115			call_weight: target_weight
116				.saturating_add(Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0)),
117			extension_weight: Weight::zero(),
118			class: DispatchClass::Normal,
119			..Default::default()
120		};
121		let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into();
122		let post_info = PostDispatchInfo { actual_weight: None, pays_fee: Default::default() };
123		let len = 0_usize;
124
125		crate::BlockWeightMode::<T>::put(BlockWeightMode::fraction_of_core(None));
126
127		let ext = DynamicMaxBlockWeight::<T, (), ConstU32<4>>::new(());
128
129		#[block]
130		{
131			ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| {
132				// Normally this is done by `CheckWeight`
133				frame_system::Pallet::<T>::register_extra_weight_unchecked(
134					info.call_weight,
135					DispatchClass::Normal,
136				);
137				Ok(post_info)
138			})
139			.unwrap()
140			.unwrap();
141		}
142
143		assert_eq!(crate::BlockWeightMode::<T>::get().unwrap(), BlockWeightMode::full_core());
144		assert!(has_use_full_core_digest::<T>());
145		assert_eq!(MaxParachainBlockWeight::<T, ConstU32<4>>::get(), FULL_CORE_WEIGHT);
146
147		Ok(())
148	}
149
150	/// A benchmark that assumes that an extrinsic was executed with `FractionOfCore` set.
151	#[benchmark]
152	fn block_weight_tx_extension_stays_fraction_of_core() -> Result<(), BenchmarkError> {
153		let caller = account("caller", 0, 0);
154
155		frame_system::Pallet::<T>::note_inherents_applied();
156
157		frame_system::Pallet::<T>::set_extrinsic_index(1);
158
159		frame_system::Pallet::<T>::deposit_log(
160			BlockBundleInfo { index: 0, is_last: false }.to_digest_item(),
161		);
162		frame_system::Pallet::<T>::deposit_log(
163			CoreInfo {
164				selector: 0.into(),
165				claim_queue_offset: 0.into(),
166				number_of_cores: 1.into(),
167			}
168			.to_digest_item(),
169		);
170		let target_weight = MaxParachainBlockWeight::<T, ConstU32<4>>::target_block_weight();
171
172		let info = DispatchInfo {
173			call_weight: Weight::from_parts(1024, 1024),
174			extension_weight: Weight::zero(),
175			class: DispatchClass::Normal,
176			..Default::default()
177		};
178		let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into();
179		let post_info = PostDispatchInfo { actual_weight: None, pays_fee: Default::default() };
180		let len = 0_usize;
181
182		crate::BlockWeightMode::<T>::put(BlockWeightMode::fraction_of_core(None));
183
184		let ext = DynamicMaxBlockWeight::<T, (), ConstU32<4>>::new(());
185
186		#[block]
187		{
188			ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| {
189				// Normally this is done by `CheckWeight`
190				frame_system::Pallet::<T>::register_extra_weight_unchecked(
191					info.call_weight,
192					DispatchClass::Normal,
193				);
194				Ok(post_info)
195			})
196			.unwrap()
197			.unwrap();
198		}
199
200		assert_eq!(
201			crate::BlockWeightMode::<T>::get().unwrap(),
202			BlockWeightMode::fraction_of_core(Some(1))
203		);
204		assert!(!has_use_full_core_digest::<T>());
205		assert_eq!(MaxParachainBlockWeight::<T, ConstU32<4>>::get(), target_weight);
206
207		Ok(())
208	}
209
210	/// A benchmark that assumes that `FullCore` was set already before executing an extrinsic.
211	#[benchmark]
212	fn block_weight_tx_extension_full_core() -> Result<(), BenchmarkError> {
213		let caller = account("caller", 0, 0);
214
215		frame_system::Pallet::<T>::set_block_number(1u32.into());
216
217		frame_system::Pallet::<T>::note_inherents_applied();
218
219		frame_system::Pallet::<T>::set_extrinsic_index(1);
220
221		frame_system::Pallet::<T>::deposit_log(
222			BlockBundleInfo { index: 0, is_last: false }.to_digest_item(),
223		);
224		frame_system::Pallet::<T>::deposit_log(
225			CoreInfo {
226				selector: 0.into(),
227				claim_queue_offset: 0.into(),
228				number_of_cores: 1.into(),
229			}
230			.to_digest_item(),
231		);
232
233		let info = DispatchInfo {
234			call_weight: Weight::from_parts(1024, 1024),
235			extension_weight: Weight::zero(),
236			class: DispatchClass::Normal,
237			..Default::default()
238		};
239		let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into();
240		let post_info = PostDispatchInfo { actual_weight: None, pays_fee: Default::default() };
241		let len = 0_usize;
242
243		crate::BlockWeightMode::<T>::put(BlockWeightMode::full_core());
244
245		let ext = DynamicMaxBlockWeight::<T, (), ConstU32<4>>::new(());
246
247		#[block]
248		{
249			ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| {
250				// Normally this is done by `CheckWeight`
251				frame_system::Pallet::<T>::register_extra_weight_unchecked(
252					info.call_weight,
253					DispatchClass::Normal,
254				);
255				Ok(post_info)
256			})
257			.unwrap()
258			.unwrap();
259		}
260
261		assert_eq!(crate::BlockWeightMode::<T>::get().unwrap(), BlockWeightMode::full_core());
262
263		Ok(())
264	}
265
266	impl_benchmark_test_suite! {
267		Pallet,
268		crate::mock::new_test_ext(),
269		crate::mock::Test
270	}
271}