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.iter_mut().find(|x| x.key == add.key) {
319 Some(item) => {
322 item.reads += add.reads;
323 item.writes += add.writes;
324 item.whitelisted = item.whitelisted || add.whitelisted;
325 },
326 None => {
328 whitelist.push(add);
329 },
330 }
331 self.set_whitelist(whitelist);
332 }
333
334 fn remove_from_whitelist(&mut self, remove: PassFatPointerAndRead<Vec<u8>>) {
336 let mut whitelist = self.get_whitelist();
337 whitelist.retain(|x| x.key != remove);
338 self.set_whitelist(whitelist);
339 }
340
341 fn get_read_and_written_keys(
342 &self,
343 ) -> AllocateAndReturnByCodec<Vec<(Vec<u8>, u32, u32, bool)>> {
344 self.get_read_and_written_keys()
345 }
346
347 fn proof_size(&self) -> AllocateAndReturnByCodec<Option<u32>> {
349 self.proof_size()
350 }
351}
352
353pub trait Benchmarking {
355 fn benchmarks(extra: bool) -> Vec<BenchmarkMetadata>;
362
363 fn run_benchmark(
365 name: &[u8],
366 selected_components: &[(BenchmarkParameter, u32)],
367 whitelist: &[TrackedStorageKey],
368 verify: bool,
369 internal_repeats: u32,
370 ) -> Result<Vec<BenchmarkResult>, BenchmarkError>;
371}
372
373pub trait Recording {
375 fn start(&mut self) {}
377
378 fn stop(&mut self) {}
380}
381
382struct NoopRecording;
384impl Recording for NoopRecording {}
385
386struct TestRecording<'a> {
388 on_before_start: Option<&'a dyn Fn()>,
389}
390
391impl<'a> TestRecording<'a> {
392 fn new(on_before_start: &'a dyn Fn()) -> Self {
393 Self { on_before_start: Some(on_before_start) }
394 }
395}
396
397impl<'a> Recording for TestRecording<'a> {
398 fn start(&mut self) {
399 (self.on_before_start.take().expect("start called more than once"))();
400 }
401}
402
403pub struct BenchmarkRecording<'a> {
405 on_before_start: Option<&'a dyn Fn()>,
406 start_extrinsic: Option<u128>,
407 finish_extrinsic: Option<u128>,
408 start_pov: Option<u32>,
409 end_pov: Option<u32>,
410}
411
412impl<'a> BenchmarkRecording<'a> {
413 pub fn new(on_before_start: &'a dyn Fn()) -> Self {
414 Self {
415 on_before_start: Some(on_before_start),
416 start_extrinsic: None,
417 finish_extrinsic: None,
418 start_pov: None,
419 end_pov: None,
420 }
421 }
422}
423
424impl<'a> Recording for BenchmarkRecording<'a> {
425 fn start(&mut self) {
426 (self.on_before_start.take().expect("start called more than once"))();
427 self.start_pov = crate::benchmarking::proof_size();
428 self.start_extrinsic = Some(current_time());
429 }
430
431 fn stop(&mut self) {
432 self.finish_extrinsic = Some(current_time());
433 self.end_pov = crate::benchmarking::proof_size();
434 }
435}
436
437impl<'a> BenchmarkRecording<'a> {
438 pub fn start_pov(&self) -> Option<u32> {
439 self.start_pov
440 }
441
442 pub fn end_pov(&self) -> Option<u32> {
443 self.end_pov
444 }
445
446 pub fn diff_pov(&self) -> Option<u32> {
447 self.start_pov.zip(self.end_pov).map(|(start, end)| end.saturating_sub(start))
448 }
449
450 pub fn elapsed_extrinsic(&self) -> Option<u128> {
451 self.start_extrinsic
452 .zip(self.finish_extrinsic)
453 .map(|(start, end)| end.saturating_sub(start))
454 }
455}
456
457pub trait BenchmarkingSetup<T, I = ()> {
462 fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>;
464
465 fn instance(
467 &self,
468 recording: &mut impl Recording,
469 components: &[(BenchmarkParameter, u32)],
470 verify: bool,
471 ) -> Result<(), BenchmarkError>;
472
473 fn test_instance(
475 &self,
476 components: &[(BenchmarkParameter, u32)],
477 on_before_start: &dyn Fn(),
478 ) -> Result<(), BenchmarkError> {
479 return self.instance(&mut TestRecording::new(on_before_start), components, true);
480 }
481
482 fn unit_test_instance(
484 &self,
485 components: &[(BenchmarkParameter, u32)],
486 ) -> Result<(), BenchmarkError> {
487 return self.instance(&mut NoopRecording {}, components, true);
488 }
489}
490
491pub fn account<AccountId: Decode>(name: &'static str, index: u32, seed: u32) -> AccountId {
493 let entropy = (name, index, seed).using_encoded(blake2_256);
494 Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
495 .expect("infinite length input; no invalid inputs for type; qed")
496}
497
498pub fn whitelisted_caller<AccountId: Decode>() -> AccountId {
500 account::<AccountId>("whitelisted_caller", 0, 0)
501}
502
503#[macro_export]
504macro_rules! whitelist_account {
505 ($acc:ident) => {
506 frame_benchmarking::benchmarking::add_to_whitelist(
507 frame_system::Account::<T>::hashed_key_for(&$acc).into(),
508 );
509 };
510}