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::{traits::TrailingZeroInput, DispatchError};
27use sp_storage::TrackedStorageKey;
28
29#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
31#[derive(Encode, Decode, Clone, Copy, PartialEq, Debug, TypeInfo)]
32#[allow(missing_docs)]
33#[allow(non_camel_case_types)]
34pub enum BenchmarkParameter {
35 a,
36 b,
37 c,
38 d,
39 e,
40 f,
41 g,
42 h,
43 i,
44 j,
45 k,
46 l,
47 m,
48 n,
49 o,
50 p,
51 q,
52 r,
53 s,
54 t,
55 u,
56 v,
57 w,
58 x,
59 y,
60 z,
61}
62
63#[cfg(feature = "std")]
64impl std::fmt::Display for BenchmarkParameter {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "{:?}", self)
67 }
68}
69
70#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
72#[derive(Encode, Decode, Clone, PartialEq, Debug, TypeInfo)]
73pub struct BenchmarkBatch {
74 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
76 pub pallet: Vec<u8>,
77 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
79 pub instance: Vec<u8>,
80 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
82 pub benchmark: Vec<u8>,
83 pub results: Vec<BenchmarkResult>,
85}
86
87#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
90#[derive(Encode, Decode, Clone, PartialEq, Debug)]
91pub struct BenchmarkBatchSplitResults {
92 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
94 pub pallet: Vec<u8>,
95 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
97 pub instance: Vec<u8>,
98 #[cfg_attr(feature = "std", serde(with = "serde_as_str"))]
100 pub benchmark: Vec<u8>,
101 pub time_results: Vec<BenchmarkResult>,
103 pub db_results: Vec<BenchmarkResult>,
105}
106
107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
111#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
112pub struct BenchmarkResult {
113 pub components: Vec<(BenchmarkParameter, u32)>,
114 pub extrinsic_time: u128,
115 pub storage_root_time: u128,
116 pub reads: u32,
117 pub repeat_reads: u32,
118 pub writes: u32,
119 pub repeat_writes: u32,
120 pub proof_size: u32,
121 #[cfg_attr(feature = "std", serde(skip))]
122 pub keys: Vec<(Vec<u8>, u32, u32, bool)>,
123}
124
125impl BenchmarkResult {
126 pub fn from_weight(w: Weight) -> Self {
127 Self { extrinsic_time: (w.ref_time() / 1_000) as u128, ..Default::default() }
128 }
129}
130
131#[cfg(feature = "std")]
133mod serde_as_str {
134 pub fn serialize<S>(value: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
135 where
136 S: serde::Serializer,
137 {
138 let s = std::str::from_utf8(value).map_err(serde::ser::Error::custom)?;
139 serializer.collect_str(s)
140 }
141
142 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
143 where
144 D: serde::de::Deserializer<'de>,
145 {
146 let s: &str = serde::de::Deserialize::deserialize(deserializer)?;
147 Ok(s.into())
148 }
149}
150
151#[derive(Clone, PartialEq, Debug)]
153pub enum BenchmarkError {
154 Stop(&'static str),
156 Override(BenchmarkResult),
159 Skip,
162 Weightless,
167}
168
169impl From<BenchmarkError> for &'static str {
170 fn from(e: BenchmarkError) -> Self {
171 match e {
172 BenchmarkError::Stop(s) => s,
173 BenchmarkError::Override(_) => "benchmark override",
174 BenchmarkError::Skip => "benchmark skip",
175 BenchmarkError::Weightless => "benchmark weightless",
176 }
177 }
178}
179
180impl From<&'static str> for BenchmarkError {
181 fn from(s: &'static str) -> Self {
182 Self::Stop(s)
183 }
184}
185
186impl From<DispatchErrorWithPostInfo> for BenchmarkError {
187 fn from(e: DispatchErrorWithPostInfo) -> Self {
188 Self::Stop(e.into())
189 }
190}
191
192impl From<DispatchError> for BenchmarkError {
193 fn from(e: DispatchError) -> Self {
194 Self::Stop(e.into())
195 }
196}
197
198#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
200pub struct BenchmarkConfig {
201 pub pallet: Vec<u8>,
203 pub instance: Vec<u8>,
205 pub benchmark: Vec<u8>,
207 pub selected_components: Vec<(BenchmarkParameter, u32)>,
209 pub verify: bool,
211 pub internal_repeats: u32,
213}
214
215#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
219pub struct BenchmarkList {
220 pub pallet: Vec<u8>,
221 pub instance: Vec<u8>,
222 pub benchmarks: Vec<BenchmarkMetadata>,
223}
224
225#[derive(Encode, Decode, Default, Clone, PartialEq, Debug, TypeInfo)]
226pub struct BenchmarkMetadata {
227 pub name: Vec<u8>,
228 pub components: Vec<(BenchmarkParameter, u32, u32)>,
229 pub pov_modes: Vec<(Vec<u8>, Vec<u8>)>,
230}
231
232sp_api::decl_runtime_apis! {
233 #[api_version(2)]
235 pub trait Benchmark {
236 fn benchmark_metadata(extra: bool) -> (Vec<BenchmarkList>, Vec<StorageInfo>);
242
243 fn dispatch_benchmark(config: BenchmarkConfig) -> Result<Vec<BenchmarkBatch>, alloc::string::String>;
245 }
246}
247
248#[sp_runtime_interface::runtime_interface]
250pub trait Benchmarking {
251 fn current_time() -> u128 {
256 std::time::SystemTime::now()
257 .duration_since(std::time::SystemTime::UNIX_EPOCH)
258 .expect("Unix time doesn't go backwards; qed")
259 .as_nanos()
260 }
261
262 fn wipe_db(&mut self) {
264 self.wipe()
265 }
266
267 fn commit_db(&mut self) {
269 self.commit()
270 }
271
272 fn read_write_count(&self) -> (u32, u32, u32, u32) {
274 self.read_write_count()
275 }
276
277 fn reset_read_write_count(&mut self) {
279 self.reset_read_write_count()
280 }
281
282 fn get_whitelist(&self) -> Vec<TrackedStorageKey> {
284 self.get_whitelist()
285 }
286
287 fn set_whitelist(&mut self, new: Vec<TrackedStorageKey>) {
289 self.set_whitelist(new)
290 }
291
292 fn add_to_whitelist(&mut self, add: TrackedStorageKey) {
294 let mut whitelist = self.get_whitelist();
295 match whitelist.iter_mut().find(|x| x.key == add.key) {
296 Some(item) => {
299 item.reads += add.reads;
300 item.writes += add.writes;
301 item.whitelisted = item.whitelisted || add.whitelisted;
302 },
303 None => {
305 whitelist.push(add);
306 },
307 }
308 self.set_whitelist(whitelist);
309 }
310
311 fn remove_from_whitelist(&mut self, remove: Vec<u8>) {
313 let mut whitelist = self.get_whitelist();
314 whitelist.retain(|x| x.key != remove);
315 self.set_whitelist(whitelist);
316 }
317
318 fn get_read_and_written_keys(&self) -> Vec<(Vec<u8>, u32, u32, bool)> {
319 self.get_read_and_written_keys()
320 }
321
322 fn proof_size(&self) -> Option<u32> {
324 self.proof_size()
325 }
326}
327
328pub trait Benchmarking {
330 fn benchmarks(extra: bool) -> Vec<BenchmarkMetadata>;
337
338 fn run_benchmark(
340 name: &[u8],
341 selected_components: &[(BenchmarkParameter, u32)],
342 whitelist: &[TrackedStorageKey],
343 verify: bool,
344 internal_repeats: u32,
345 ) -> Result<Vec<BenchmarkResult>, BenchmarkError>;
346}
347
348pub trait Recording {
350 fn start(&mut self) {}
352
353 fn stop(&mut self) {}
355}
356
357struct NoopRecording;
359impl Recording for NoopRecording {}
360
361struct TestRecording<'a> {
363 on_before_start: Option<&'a dyn Fn()>,
364}
365
366impl<'a> TestRecording<'a> {
367 fn new(on_before_start: &'a dyn Fn()) -> Self {
368 Self { on_before_start: Some(on_before_start) }
369 }
370}
371
372impl<'a> Recording for TestRecording<'a> {
373 fn start(&mut self) {
374 (self.on_before_start.take().expect("start called more than once"))();
375 }
376}
377
378pub struct BenchmarkRecording<'a> {
380 on_before_start: Option<&'a dyn Fn()>,
381 start_extrinsic: Option<u128>,
382 finish_extrinsic: Option<u128>,
383 start_pov: Option<u32>,
384 end_pov: Option<u32>,
385}
386
387impl<'a> BenchmarkRecording<'a> {
388 pub fn new(on_before_start: &'a dyn Fn()) -> Self {
389 Self {
390 on_before_start: Some(on_before_start),
391 start_extrinsic: None,
392 finish_extrinsic: None,
393 start_pov: None,
394 end_pov: None,
395 }
396 }
397}
398
399impl<'a> Recording for BenchmarkRecording<'a> {
400 fn start(&mut self) {
401 (self.on_before_start.take().expect("start called more than once"))();
402 self.start_pov = crate::benchmarking::proof_size();
403 self.start_extrinsic = Some(crate::benchmarking::current_time());
404 }
405
406 fn stop(&mut self) {
407 self.finish_extrinsic = Some(crate::benchmarking::current_time());
408 self.end_pov = crate::benchmarking::proof_size();
409 }
410}
411
412impl<'a> BenchmarkRecording<'a> {
413 pub fn start_pov(&self) -> Option<u32> {
414 self.start_pov
415 }
416
417 pub fn end_pov(&self) -> Option<u32> {
418 self.end_pov
419 }
420
421 pub fn diff_pov(&self) -> Option<u32> {
422 self.start_pov.zip(self.end_pov).map(|(start, end)| end.saturating_sub(start))
423 }
424
425 pub fn elapsed_extrinsic(&self) -> Option<u128> {
426 self.start_extrinsic
427 .zip(self.finish_extrinsic)
428 .map(|(start, end)| end.saturating_sub(start))
429 }
430}
431
432pub trait BenchmarkingSetup<T, I = ()> {
437 fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>;
439
440 fn instance(
442 &self,
443 recording: &mut impl Recording,
444 components: &[(BenchmarkParameter, u32)],
445 verify: bool,
446 ) -> Result<(), BenchmarkError>;
447
448 fn test_instance(
450 &self,
451 components: &[(BenchmarkParameter, u32)],
452 on_before_start: &dyn Fn(),
453 ) -> Result<(), BenchmarkError> {
454 return self.instance(&mut TestRecording::new(on_before_start), components, true);
455 }
456
457 fn unit_test_instance(
459 &self,
460 components: &[(BenchmarkParameter, u32)],
461 ) -> Result<(), BenchmarkError> {
462 return self.instance(&mut NoopRecording {}, components, true);
463 }
464}
465
466pub fn account<AccountId: Decode>(name: &'static str, index: u32, seed: u32) -> AccountId {
468 let entropy = (name, index, seed).using_encoded(blake2_256);
469 Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
470 .expect("infinite length input; no invalid inputs for type; qed")
471}
472
473pub fn whitelisted_caller<AccountId: Decode>() -> AccountId {
475 account::<AccountId>("whitelisted_caller", 0, 0)
476}
477
478#[macro_export]
479macro_rules! whitelist_account {
480 ($acc:ident) => {
481 frame_benchmarking::benchmarking::add_to_whitelist(
482 frame_system::Account::<T>::hashed_key_for(&$acc).into(),
483 );
484 };
485}