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