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