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 const WARMUP_KEY: &[u8] = b":benchmark_warmup:";
298 self.place_storage(WARMUP_KEY.to_vec(), Some(vec![0u8; 32]));
299 self.place_storage(WARMUP_KEY.to_vec(), None);
300
301 self.reset_read_write_count();
303 }
304
305 fn read_write_count(&self) -> AllocateAndReturnByCodec<(u32, u32, u32, u32)> {
307 self.read_write_count()
308 }
309
310 fn reset_read_write_count(&mut self) {
312 self.reset_read_write_count()
313 }
314
315 fn get_whitelist(&self) -> AllocateAndReturnByCodec<Vec<TrackedStorageKey>> {
317 self.get_whitelist()
318 }
319
320 fn set_whitelist(&mut self, new: PassFatPointerAndDecode<Vec<TrackedStorageKey>>) {
322 self.set_whitelist(new)
323 }
324
325 fn add_to_whitelist(&mut self, add: PassFatPointerAndDecode<TrackedStorageKey>) {
327 let mut whitelist = self.get_whitelist();
328 match whitelist.iter_mut().find(|x| x.key == add.key) {
329 Some(item) => {
332 item.reads += add.reads;
333 item.writes += add.writes;
334 item.whitelisted = item.whitelisted || add.whitelisted;
335 },
336 None => {
338 whitelist.push(add);
339 },
340 }
341 self.set_whitelist(whitelist);
342 }
343
344 fn remove_from_whitelist(&mut self, remove: PassFatPointerAndRead<Vec<u8>>) {
346 let mut whitelist = self.get_whitelist();
347 whitelist.retain(|x| x.key != remove);
348 self.set_whitelist(whitelist);
349 }
350
351 fn get_read_and_written_keys(
352 &self,
353 ) -> AllocateAndReturnByCodec<Vec<(Vec<u8>, u32, u32, bool)>> {
354 self.get_read_and_written_keys()
355 }
356
357 fn proof_size(&self) -> AllocateAndReturnByCodec<Option<u32>> {
359 self.proof_size()
360 }
361}
362
363pub trait Benchmarking {
365 fn benchmarks(extra: bool) -> Vec<BenchmarkMetadata>;
372
373 fn run_benchmark(
375 name: &[u8],
376 selected_components: &[(BenchmarkParameter, u32)],
377 whitelist: &[TrackedStorageKey],
378 verify: bool,
379 internal_repeats: u32,
380 ) -> Result<Vec<BenchmarkResult>, BenchmarkError>;
381}
382
383pub trait Recording {
385 fn start(&mut self) {}
387
388 fn stop(&mut self) {}
390}
391
392struct NoopRecording;
394impl Recording for NoopRecording {}
395
396struct TestRecording<'a> {
398 on_before_start: Option<&'a dyn Fn()>,
399}
400
401impl<'a> TestRecording<'a> {
402 fn new(on_before_start: &'a dyn Fn()) -> Self {
403 Self { on_before_start: Some(on_before_start) }
404 }
405}
406
407impl<'a> Recording for TestRecording<'a> {
408 fn start(&mut self) {
409 (self.on_before_start.take().expect("start called more than once"))();
410 }
411}
412
413pub struct BenchmarkRecording<'a> {
415 on_before_start: Option<&'a dyn Fn()>,
416 start_extrinsic: Option<u128>,
417 finish_extrinsic: Option<u128>,
418 start_pov: Option<u32>,
419 end_pov: Option<u32>,
420}
421
422impl<'a> BenchmarkRecording<'a> {
423 pub fn new(on_before_start: &'a dyn Fn()) -> Self {
424 Self {
425 on_before_start: Some(on_before_start),
426 start_extrinsic: None,
427 finish_extrinsic: None,
428 start_pov: None,
429 end_pov: None,
430 }
431 }
432}
433
434impl<'a> Recording for BenchmarkRecording<'a> {
435 fn start(&mut self) {
436 (self.on_before_start.take().expect("start called more than once"))();
437 self.start_pov = crate::benchmarking::proof_size();
438 self.start_extrinsic = Some(current_time());
439 }
440
441 fn stop(&mut self) {
442 self.finish_extrinsic = Some(current_time());
443 self.end_pov = crate::benchmarking::proof_size();
444 }
445}
446
447impl<'a> BenchmarkRecording<'a> {
448 pub fn start_pov(&self) -> Option<u32> {
449 self.start_pov
450 }
451
452 pub fn end_pov(&self) -> Option<u32> {
453 self.end_pov
454 }
455
456 pub fn diff_pov(&self) -> Option<u32> {
457 self.start_pov.zip(self.end_pov).map(|(start, end)| end.saturating_sub(start))
458 }
459
460 pub fn elapsed_extrinsic(&self) -> Option<u128> {
461 self.start_extrinsic
462 .zip(self.finish_extrinsic)
463 .map(|(start, end)| end.saturating_sub(start))
464 }
465}
466
467pub trait BenchmarkingSetup<T, I = ()> {
472 fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>;
474
475 fn instance(
477 &self,
478 recording: &mut impl Recording,
479 components: &[(BenchmarkParameter, u32)],
480 verify: bool,
481 ) -> Result<(), BenchmarkError>;
482
483 fn test_instance(
485 &self,
486 components: &[(BenchmarkParameter, u32)],
487 on_before_start: &dyn Fn(),
488 ) -> Result<(), BenchmarkError> {
489 return self.instance(&mut TestRecording::new(on_before_start), components, true);
490 }
491
492 fn unit_test_instance(
494 &self,
495 components: &[(BenchmarkParameter, u32)],
496 ) -> Result<(), BenchmarkError> {
497 return self.instance(&mut NoopRecording {}, components, true);
498 }
499}
500
501pub fn account<AccountId: Decode>(name: &'static str, index: u32, seed: u32) -> AccountId {
503 let entropy = (name, index, seed).using_encoded(blake2_256);
504 Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
505 .expect("infinite length input; no invalid inputs for type; qed")
506}
507
508pub fn whitelisted_caller<AccountId: Decode>() -> AccountId {
510 account::<AccountId>("whitelisted_caller", 0, 0)
511}
512
513#[macro_export]
514macro_rules! whitelist_account {
515 ($acc:ident) => {
516 frame_benchmarking::benchmarking::add_to_whitelist(
517 frame_system::Account::<T>::hashed_key_for(&$acc).into(),
518 );
519 };
520}