1use alloc::vec::Vec;
20use codec::{Decode, Encode};
21use frame_support::{dispatch::DispatchErrorWithPostInfo, pallet_prelude::*, traits::StorageInfo};
22use scale_info::TypeInfo;
23#[cfg(feature = "std")]
24use serde::{Deserialize, Serialize};
25use sp_io::hashing::blake2_256;
26use sp_runtime::{
27 traits::TrailingZeroInput, transaction_validity::TransactionValidityError, DispatchError,
28};
29use sp_runtime_interface::pass_by::{
30 AllocateAndReturnByCodec, AllocateAndReturnPointer, PassFatPointerAndDecode,
31 PassFatPointerAndRead,
32};
33use sp_storage::TrackedStorageKey;
34
35#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
37#[derive(Encode, Decode, Clone, Copy, PartialEq, Debug, TypeInfo)]
38#[allow(missing_docs)]
39#[allow(non_camel_case_types)]
40pub enum BenchmarkParameter {
41 a,
42 b,
43 c,
44 d,
45 e,
46 f,
47 g,
48 h,
49 i,
50 j,
51 k,
52 l,
53 m,
54 n,
55 o,
56 p,
57 q,
58 r,
59 s,
60 t,
61 u,
62 v,
63 w,
64 x,
65 y,
66 z,
67}
68
69#[cfg(feature = "std")]
70impl std::fmt::Display for BenchmarkParameter {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "{:?}", self)
73 }
74}
75
76#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
78#[derive(Encode, Decode, Clone, PartialEq, Debug, TypeInfo)]
79pub struct BenchmarkBatch {
80 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
82 pub pallet: Vec<u8>,
83 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
85 pub instance: Vec<u8>,
86 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
88 pub benchmark: Vec<u8>,
89 pub results: Vec<BenchmarkResult>,
91}
92
93#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
96#[derive(Encode, Decode, Clone, PartialEq, Debug)]
97pub struct BenchmarkBatchSplitResults {
98 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
100 pub pallet: Vec<u8>,
101 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
103 pub instance: Vec<u8>,
104 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
106 pub benchmark: Vec<u8>,
107 pub time_results: Vec<BenchmarkResult>,
109 pub db_results: Vec<BenchmarkResult>,
111}
112
113#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
117#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
118pub struct BenchmarkResult {
119 pub components: Vec<(BenchmarkParameter, u32)>,
120 pub extrinsic_time: u128,
121 pub storage_root_time: u128,
122 pub reads: u32,
123 pub repeat_reads: u32,
124 pub writes: u32,
125 pub repeat_writes: u32,
126 pub proof_size: u32,
127 #[cfg_attr(feature = "std", serde(skip))]
128 pub keys: Vec<(Vec<u8>, u32, u32, bool)>,
129}
130
131impl BenchmarkResult {
132 pub fn from_weight(w: Weight) -> Self {
133 Self { extrinsic_time: (w.ref_time() / 1_000) as u128, ..Default::default() }
134 }
135}
136
137#[cfg(feature = "std")]
139mod serde_as_str {
140 pub fn serialize<S>(value: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
141 where
142 S: serde::Serializer,
143 {
144 let s = std::str::from_utf8(value).map_err(serde::ser::Error::custom)?;
145 serializer.collect_str(s)
146 }
147
148 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
149 where
150 D: serde::de::Deserializer<'de>,
151 {
152 let s: &str = serde::de::Deserialize::deserialize(deserializer)?;
153 Ok(s.into())
154 }
155}
156
157#[derive(Clone, PartialEq, Debug)]
159pub enum BenchmarkError {
160 Stop(&'static str),
162 Override(BenchmarkResult),
165 Skip,
168 Weightless,
173}
174
175impl From<BenchmarkError> for &'static str {
176 fn from(e: BenchmarkError) -> Self {
177 match e {
178 BenchmarkError::Stop(s) => s,
179 BenchmarkError::Override(_) => "benchmark override",
180 BenchmarkError::Skip => "benchmark skip",
181 BenchmarkError::Weightless => "benchmark weightless",
182 }
183 }
184}
185
186impl From<&'static str> for BenchmarkError {
187 fn from(s: &'static str) -> Self {
188 Self::Stop(s)
189 }
190}
191
192impl From<DispatchErrorWithPostInfo> for BenchmarkError {
193 fn from(e: DispatchErrorWithPostInfo) -> Self {
194 Self::Stop(e.into())
195 }
196}
197
198impl From<DispatchError> for BenchmarkError {
199 fn from(e: DispatchError) -> Self {
200 Self::Stop(e.into())
201 }
202}
203
204impl From<TransactionValidityError> for BenchmarkError {
205 fn from(e: TransactionValidityError) -> Self {
206 Self::Stop(e.into())
207 }
208}
209
210#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
212pub struct BenchmarkConfig {
213 pub pallet: Vec<u8>,
215 pub instance: Vec<u8>,
217 pub benchmark: Vec<u8>,
219 pub selected_components: Vec<(BenchmarkParameter, u32)>,
221 pub verify: bool,
223 pub internal_repeats: u32,
225}
226
227#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
231pub struct BenchmarkList {
232 pub pallet: Vec<u8>,
233 pub instance: Vec<u8>,
234 pub benchmarks: Vec<BenchmarkMetadata>,
235}
236
237#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
238pub struct BenchmarkMetadata {
239 pub name: Vec<u8>,
240 pub components: Vec<(BenchmarkParameter, u32, u32)>,
241 pub pov_modes: Vec<(Vec<u8>, Vec<u8>)>,
242}
243
244sp_api::decl_runtime_apis! {
245 #[api_version(2)]
247 pub trait Benchmark {
248 fn benchmark_metadata(extra: bool) -> (Vec<BenchmarkList>, Vec<StorageInfo>);
254
255 fn dispatch_benchmark(config: BenchmarkConfig) -> Result<Vec<BenchmarkBatch>, alloc::string::String>;
257 }
258}
259
260pub fn current_time() -> u128 {
265 u128::from_le_bytes(self::benchmarking::current_time())
266}
267
268#[sp_runtime_interface::runtime_interface]
270pub trait Benchmarking {
271 fn current_time() -> AllocateAndReturnPointer<[u8; 16], 16> {
278 std::time::SystemTime::now()
279 .duration_since(std::time::SystemTime::UNIX_EPOCH)
280 .expect("Unix time doesn't go backwards; qed")
281 .as_nanos()
282 .to_le_bytes()
283 }
284
285 fn wipe_db(&mut self) {
287 self.wipe()
288 }
289
290 fn commit_db(&mut self) {
292 self.commit();
293 }
294
295 fn read_write_count(&self) -> AllocateAndReturnByCodec<(u32, u32, u32, u32)> {
297 self.read_write_count()
298 }
299
300 fn reset_read_write_count(&mut self) {
302 self.reset_read_write_count()
303 }
304
305 fn get_whitelist(&self) -> AllocateAndReturnByCodec<Vec<TrackedStorageKey>> {
307 self.get_whitelist()
308 }
309
310 fn set_whitelist(&mut self, new: PassFatPointerAndDecode<Vec<TrackedStorageKey>>) {
312 self.set_whitelist(new)
313 }
314
315 fn add_to_whitelist(&mut self, add: PassFatPointerAndDecode<TrackedStorageKey>) {
317 let mut whitelist = self.get_whitelist();
318 match whitelist
319 .iter_mut()
320 .find(|x| x.key == add.key && x.child_trie_key == add.child_trie_key)
321 {
322 Some(item) => {
325 item.reads += add.reads;
326 item.writes += add.writes;
327 item.whitelisted = item.whitelisted || add.whitelisted;
328 },
329 None => {
331 whitelist.push(add);
332 },
333 }
334 self.set_whitelist(whitelist);
335 }
336
337 fn remove_from_whitelist(&mut self, remove: PassFatPointerAndRead<Vec<u8>>) {
339 let mut whitelist = self.get_whitelist();
340 whitelist.retain(|x| x.key != remove);
341 self.set_whitelist(whitelist);
342 }
343
344 fn get_read_and_written_keys(
345 &self,
346 ) -> AllocateAndReturnByCodec<Vec<(Vec<u8>, u32, u32, bool)>> {
347 self.get_read_and_written_keys()
348 }
349
350 fn proof_size(&self) -> AllocateAndReturnByCodec<Option<u32>> {
352 self.proof_size()
353 }
354}
355
356pub trait Benchmarking {
358 fn benchmarks(extra: bool) -> Vec<BenchmarkMetadata>;
365
366 fn run_benchmark(
368 name: &[u8],
369 selected_components: &[(BenchmarkParameter, u32)],
370 whitelist: &[TrackedStorageKey],
371 verify: bool,
372 internal_repeats: u32,
373 ) -> Result<Vec<BenchmarkResult>, BenchmarkError>;
374}
375
376pub trait Recording {
378 fn start(&mut self) {}
380
381 fn stop(&mut self) {}
383}
384
385struct NoopRecording;
387impl Recording for NoopRecording {}
388
389struct TestRecording<'a> {
391 on_before_start: Option<&'a dyn Fn()>,
392}
393
394impl<'a> TestRecording<'a> {
395 fn new(on_before_start: &'a dyn Fn()) -> Self {
396 Self { on_before_start: Some(on_before_start) }
397 }
398}
399
400impl<'a> Recording for TestRecording<'a> {
401 fn start(&mut self) {
402 (self.on_before_start.take().expect("start called more than once"))();
403 }
404}
405
406pub struct BenchmarkRecording<'a> {
408 on_before_start: Option<&'a dyn Fn()>,
409 start_extrinsic: Option<u128>,
410 finish_extrinsic: Option<u128>,
411 start_pov: Option<u32>,
412 end_pov: Option<u32>,
413}
414
415impl<'a> BenchmarkRecording<'a> {
416 pub fn new(on_before_start: &'a dyn Fn()) -> Self {
417 Self {
418 on_before_start: Some(on_before_start),
419 start_extrinsic: None,
420 finish_extrinsic: None,
421 start_pov: None,
422 end_pov: None,
423 }
424 }
425}
426
427impl<'a> Recording for BenchmarkRecording<'a> {
428 fn start(&mut self) {
429 (self.on_before_start.take().expect("start called more than once"))();
430 self.start_pov = crate::benchmarking::proof_size();
431 self.start_extrinsic = Some(current_time());
432 }
433
434 fn stop(&mut self) {
435 self.finish_extrinsic = Some(current_time());
436 self.end_pov = crate::benchmarking::proof_size();
437 }
438}
439
440impl<'a> BenchmarkRecording<'a> {
441 pub fn start_pov(&self) -> Option<u32> {
442 self.start_pov
443 }
444
445 pub fn end_pov(&self) -> Option<u32> {
446 self.end_pov
447 }
448
449 pub fn diff_pov(&self) -> Option<u32> {
450 self.start_pov.zip(self.end_pov).map(|(start, end)| end.saturating_sub(start))
451 }
452
453 pub fn elapsed_extrinsic(&self) -> Option<u128> {
454 self.start_extrinsic
455 .zip(self.finish_extrinsic)
456 .map(|(start, end)| end.saturating_sub(start))
457 }
458}
459
460pub trait BenchmarkingSetup<T, I = ()> {
465 fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>;
467
468 fn instance(
470 &self,
471 recording: &mut impl Recording,
472 components: &[(BenchmarkParameter, u32)],
473 verify: bool,
474 ) -> Result<(), BenchmarkError>;
475
476 fn test_instance(
478 &self,
479 components: &[(BenchmarkParameter, u32)],
480 on_before_start: &dyn Fn(),
481 ) -> Result<(), BenchmarkError> {
482 return self.instance(&mut TestRecording::new(on_before_start), components, true);
483 }
484
485 fn unit_test_instance(
487 &self,
488 components: &[(BenchmarkParameter, u32)],
489 ) -> Result<(), BenchmarkError> {
490 return self.instance(&mut NoopRecording {}, components, true);
491 }
492}
493
494pub fn account<AccountId: Decode>(name: &'static str, index: u32, seed: u32) -> AccountId {
496 let entropy = (name, index, seed).using_encoded(blake2_256);
497 Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
498 .expect("infinite length input; no invalid inputs for type; qed")
499}
500
501pub fn whitelisted_caller<AccountId: Decode>() -> AccountId {
503 account::<AccountId>("whitelisted_caller", 0, 0)
504}
505
506#[macro_export]
507macro_rules! whitelist_account {
508 ($acc:ident) => {
509 frame_benchmarking::benchmarking::add_to_whitelist(
510 frame_system::Account::<T>::hashed_key_for(&$acc).into(),
511 );
512 };
513}
514
515pub fn add_to_whitelist_child(child_trie_key: Vec<u8>, key: Vec<u8>) {
517 let mut tracked_key = sp_storage::TrackedStorageKey::new_child(child_trie_key, key);
518 tracked_key.whitelist();
519 self::benchmarking::add_to_whitelist(tracked_key);
520}