referrerpolicy=no-referrer-when-downgrade

cumulus_pallet_xcmp_queue/
benchmarking.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Benchmarking setup for cumulus-pallet-xcmp-queue
17
18use crate::{weights_ext::get_average_page_pos, *};
19
20use alloc::vec;
21use codec::DecodeAll;
22use frame_benchmarking::v2::*;
23use frame_support::{assert_ok, traits::Hooks};
24use frame_system::RawOrigin;
25use xcm::MAX_INSTRUCTIONS_TO_DECODE;
26
27#[benchmarks]
28mod benchmarks {
29	use super::*;
30
31	/// Modify any of the `QueueConfig` fields with a new `u32` value.
32	///
33	/// Used as weight for:
34	/// - update_suspend_threshold
35	/// - update_drop_threshold
36	/// - update_resume_threshold
37	#[benchmark]
38	fn set_config_with_u32() {
39		#[extrinsic_call]
40		Pallet::<T>::update_resume_threshold(RawOrigin::Root, 1);
41	}
42
43	/// Add a XCMP message of `n` bytes to the message queue.
44	///
45	/// The message will be added on a new page and also, the `BookState` will be added
46	/// to the ready ring.
47	#[benchmark]
48	fn enqueue_n_bytes_xcmp_message(n: Linear<0, { MaxXcmpMessageLenOf::<T>::get() }>) {
49		#[cfg(test)]
50		{
51			mock::EnqueuedMessages::set(vec![]);
52		}
53
54		let msg = BoundedVec::try_from(vec![0; n as usize]).unwrap();
55
56		#[cfg(not(test))]
57		let fp_before = T::XcmpQueue::footprint(0.into());
58		#[block]
59		{
60			assert_ok!(Pallet::<T>::enqueue_xcmp_messages(
61				0.into(),
62				&[msg.as_bounded_slice()],
63				true,
64				&mut WeightMeter::new()
65			));
66		}
67		#[cfg(not(test))]
68		{
69			let fp_after = T::XcmpQueue::footprint(0.into());
70			assert_eq!(fp_after.ready_pages, fp_before.ready_pages + 1);
71		}
72	}
73
74	/// Add `n` XCMP message of 0 bytes to the message queue.
75	///
76	/// Only for the first message a new page will be created and the `BookState` will be added
77	/// to the ready ring.
78	#[benchmark]
79	fn enqueue_n_empty_xcmp_messages(n: Linear<0, 1000>) {
80		#[cfg(test)]
81		{
82			mock::EnqueuedMessages::set(vec![]);
83			<QueueConfig<T>>::set(QueueConfigData {
84				suspend_threshold: 1100,
85				drop_threshold: 1100,
86				resume_threshold: 1100,
87			});
88		}
89
90		let msg = BoundedVec::new();
91		let msgs = vec![msg.as_bounded_slice(); n as usize];
92
93		#[cfg(not(test))]
94		let fp_before = T::XcmpQueue::footprint(0.into());
95		#[block]
96		{
97			assert_ok!(Pallet::<T>::enqueue_xcmp_messages(
98				0.into(),
99				&msgs,
100				true,
101				&mut WeightMeter::new()
102			));
103		}
104		#[cfg(not(test))]
105		{
106			let fp_after = T::XcmpQueue::footprint(0.into());
107			if !msgs.is_empty() {
108				assert_eq!(fp_after.ready_pages, fp_before.ready_pages + 1);
109			}
110		}
111	}
112
113	/// Add an XCMP message of 0 bytes to the message queue at the provided position
114	/// on an existing page.
115	#[benchmark(pov_mode = Measured)]
116	fn enqueue_empty_xcmp_message_at(
117		n: Linear<0, { crate::MaxXcmpMessageLenOf::<T>::get() - 10 }>,
118	) {
119		#[cfg(test)]
120		{
121			mock::EnqueuedMessages::set(vec![]);
122		}
123
124		assert_ok!(Pallet::<T>::enqueue_xcmp_messages(
125			0.into(),
126			&[BoundedVec::try_from(vec![0; n as usize]).unwrap().as_bounded_slice()],
127			true,
128			&mut WeightMeter::new()
129		));
130
131		#[cfg(not(test))]
132		let fp_before = T::XcmpQueue::footprint(0.into());
133		#[block]
134		{
135			assert_ok!(Pallet::<T>::enqueue_xcmp_messages(
136				0.into(),
137				&[BoundedVec::new().as_bounded_slice()],
138				true,
139				&mut WeightMeter::new()
140			));
141		}
142		#[cfg(not(test))]
143		{
144			let fp_after = T::XcmpQueue::footprint(0.into());
145			assert_eq!(fp_after.ready_pages, fp_before.ready_pages);
146		}
147	}
148
149	/// Add `n` pages to the message queue.
150	///
151	/// We add one page by enqueueing a maximal size message which fills it.
152	#[benchmark]
153	fn enqueue_n_full_pages(n: Linear<0, 100>) {
154		#[cfg(test)]
155		{
156			mock::EnqueuedMessages::set(vec![]);
157		}
158		<QueueConfig<T>>::set(QueueConfigData {
159			suspend_threshold: 200,
160			drop_threshold: 200,
161			resume_threshold: 200,
162		});
163
164		let max_msg_len = MaxXcmpMessageLenOf::<T>::get() as usize;
165		let mut msgs = vec![];
166		for _i in 0..n {
167			let msg = BoundedVec::try_from(vec![0; max_msg_len]).unwrap();
168			msgs.push(msg);
169		}
170
171		#[cfg(not(test))]
172		let fp_before = T::XcmpQueue::footprint(0.into());
173		#[block]
174		{
175			assert_ok!(Pallet::<T>::enqueue_xcmp_messages(
176				0.into(),
177				&msgs.iter().map(|msg| msg.as_bounded_slice()).collect::<Vec<_>>(),
178				true,
179				&mut WeightMeter::new()
180			));
181		}
182		#[cfg(not(test))]
183		{
184			let fp_after = T::XcmpQueue::footprint(0.into());
185			assert_eq!(fp_after.ready_pages, fp_before.ready_pages + n);
186		}
187	}
188
189	#[benchmark(pov_mode = Measured)]
190	fn enqueue_1000_small_xcmp_messages() {
191		#[cfg(test)]
192		{
193			<QueueConfig<T>>::set(QueueConfigData {
194				suspend_threshold: 1100,
195				drop_threshold: 1100,
196				resume_threshold: 1100,
197			});
198		}
199
200		assert_ok!(Pallet::<T>::enqueue_xcmp_messages(
201			0.into(),
202			&[BoundedVec::try_from(vec![
203				0;
204				get_average_page_pos(MaxXcmpMessageLenOf::<T>::get())
205					as usize
206			])
207			.unwrap()
208			.as_bounded_slice()],
209			true,
210			&mut WeightMeter::new()
211		));
212
213		let mut msgs = vec![];
214		for _i in 0..1000 {
215			msgs.push(BoundedVec::try_from(vec![0; 3]).unwrap());
216		}
217
218		#[cfg(not(test))]
219		let fp_before = T::XcmpQueue::footprint(0.into());
220		#[block]
221		{
222			assert_ok!(Pallet::<T>::enqueue_xcmp_messages(
223				0.into(),
224				&msgs.iter().map(|msg| msg.as_bounded_slice()).collect::<Vec<_>>(),
225				true,
226				&mut WeightMeter::new()
227			));
228		}
229		#[cfg(not(test))]
230		{
231			let fp_after = T::XcmpQueue::footprint(0.into());
232			assert_eq!(fp_after.ready_pages, fp_before.ready_pages);
233		}
234	}
235
236	#[benchmark]
237	fn suspend_channel() {
238		let para = 123.into();
239		let data = ChannelSignal::Suspend.encode();
240
241		#[block]
242		{
243			ChannelSignal::decode_all(&mut &data[..]).unwrap();
244			Pallet::<T>::suspend_channel(para);
245		}
246
247		assert_eq!(
248			OutboundXcmpStatus::<T>::get()
249				.iter()
250				.find(|p| p.recipient == para)
251				.unwrap()
252				.state,
253			OutboundState::Suspended
254		);
255	}
256
257	#[benchmark]
258	fn resume_channel() {
259		let para = 123.into();
260		let data = ChannelSignal::Resume.encode();
261
262		Pallet::<T>::suspend_channel(para);
263
264		#[block]
265		{
266			ChannelSignal::decode_all(&mut &data[..]).unwrap();
267			Pallet::<T>::resume_channel(para);
268		}
269
270		assert!(
271			OutboundXcmpStatus::<T>::get().iter().all(|p| p.recipient != para),
272			"No messages in the channel; therefore removed."
273		);
274	}
275
276	/// Split a singular XCM.
277	#[benchmark]
278	fn take_first_concatenated_xcm(
279		n: Linear<0, { MAX_INSTRUCTIONS_TO_DECODE as u32 - MAX_XCM_DECODE_DEPTH }>,
280	) {
281		let mut xcm = Xcm::<T>(vec![ClearOrigin; n as usize]);
282		for _ in 0..MAX_XCM_DECODE_DEPTH - 1 {
283			xcm = Xcm::<T>(vec![Instruction::SetAppendix(xcm)]);
284		}
285		let data = VersionedXcm::<T>::from(xcm).encode();
286
287		#[block]
288		{
289			Pallet::<T>::take_first_concatenated_xcm(&mut &data[..], &mut WeightMeter::new())
290				.unwrap();
291		}
292	}
293
294	/// Benchmark the migration for a maximal sized message.
295	#[benchmark]
296	fn on_idle_good_msg() {
297		use migration::v3;
298
299		let block = 5;
300		let para = ParaId::from(4);
301		let message = vec![123u8; MaxXcmpMessageLenOf::<T>::get() as usize];
302		let message_metadata = vec![(block, XcmpMessageFormat::ConcatenatedVersionedXcm)];
303
304		v3::InboundXcmpMessages::<T>::insert(para, block, message);
305		v3::InboundXcmpStatus::<T>::set(Some(vec![v3::InboundChannelDetails {
306			sender: para,
307			state: v3::InboundState::Ok,
308			message_metadata,
309		}]));
310
311		#[block]
312		{
313			Pallet::<T>::on_idle(0u32.into(), Weight::MAX);
314		}
315	}
316
317	/// Benchmark the migration with a 64 KiB message that will not be possible to enqueue.
318	#[benchmark]
319	fn on_idle_large_msg() {
320		use migration::v3;
321
322		let block = 5;
323		let para = ParaId::from(4);
324		let message = vec![123u8; 1 << 16]; // 64 KiB message
325		let message_metadata = vec![(block, XcmpMessageFormat::ConcatenatedVersionedXcm)];
326
327		v3::InboundXcmpMessages::<T>::insert(para, block, message);
328		v3::InboundXcmpStatus::<T>::set(Some(vec![v3::InboundChannelDetails {
329			sender: para,
330			state: v3::InboundState::Ok,
331			message_metadata,
332		}]));
333
334		#[block]
335		{
336			Pallet::<T>::on_idle(0u32.into(), Weight::MAX);
337		}
338	}
339
340	impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
341}