Skip to content
Snippets Groups Projects
lib.rs 35.8 KiB
Newer Older
//! Macro for benchmarking a Substrate runtime. A fork of `frame-benchmarking`
//! pallet.

#![cfg_attr(not(feature = "std"), no_std)]

mod tests;

pub use frame_benchmarking::{
Xiliang Chen's avatar
Xiliang Chen committed
	benchmarking, whitelisted_caller, BenchmarkBatch, BenchmarkConfig, BenchmarkParameter, BenchmarkResults,
	Benchmarking, BenchmarkingSetup,
#[cfg(feature = "std")]
pub use frame_benchmarking::{Analysis, BenchmarkSelector};
#[doc(hidden)]
pub use frame_support;
#[doc(hidden)]
pub use paste;
#[doc(hidden)]
pub use sp_io::storage::root as storage_root;
#[doc(hidden)]
pub use sp_runtime::traits::Zero;
#[doc(hidden)]
pub use sp_std::{self, boxed::Box, prelude::Vec, vec};
#[doc(hidden)]
pub use sp_storage::TrackedStorageKey;
/// Construct pallet benchmarks for weighing dispatchables.
///
/// Works around the idea of complexity parameters, named by a single letter
/// (which is usually upper cased in complexity notation but is lower-cased for
/// use in this macro).
/// Complexity parameters ("parameters") have a range which is a `u32` pair.
/// Every time a benchmark is prepared and run, this parameter takes a concrete
/// value within the range. There is an associated instancing block, which is a
/// single expression that is evaluated during preparation. It may use `?`
/// (`i.e. `return Err(...)`) to bail with a string error. Here's a
/// few examples:
///
/// ```ignore
/// // These two are equivalent:
/// let x in 0 .. 10;
/// let x in 0 .. 10 => ();
/// // This one calls a setup function and might return an error (which would be terminal).
/// let y in 0 .. 10 => setup(y)?;
/// // This one uses a code block to do lots of stuff:
/// let z in 0 .. 10 => {
///   let a = z * z / 5;
///   let b = do_something(a)?;
///   combine_into(z, b);
/// }
/// ```
///
/// Note that due to parsing restrictions, if the `from` expression is not a
/// single token (i.e. a literal or constant), then it must be parenthesised.
/// The macro allows for a number of "arms", each representing an individual
/// benchmark. Using the simple syntax, the associated dispatchable function
/// maps 1:1 with the benchmark and the name of the benchmark is the same as
/// that of the associated function. However, extended syntax allows
/// for arbitrary expresions to be evaluated in a benchmark (including for
/// example, `on_initialize`).
/// Note that the ranges are *inclusive* on both sides. This is in contrast to
/// ranges in Rust which are left-inclusive right-exclusive.
/// Each arm may also have a block of code which is run prior to any instancing
/// and a block of code which is run afterwards. All code blocks may draw upon
/// the specific value of each parameter at any time. Local variables are shared
/// between the two pre- and post- code blocks, but do not leak from the
/// interior of any instancing expressions.
///
/// Example:
/// ```ignore
/// use path_to_node_runtime::MyRuntime;
/// use path_to_account_id::AccountId;
/// use frame_benchmarking::account;
/// use orml_benchmarking::runtime_benchmarks;
///
/// runtime_benchmarks! {
///   // The constructed runtime struct, and the pallet to benchmark.
///   { MyRuntime, my_pallet }
///
///
///   // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of
///   // size `l`, which we allow to be initialized as usual.
///   foo {
///     let caller = account::<AccountId>(b"caller", 0, benchmarks_seed);
///     let l in 1 .. MAX_LENGTH => initialize_l(l);
///   }: _(Origin::Signed(caller), vec![0u8; l])
///
///   // second dispatchable: bar; this is a root dispatchable and accepts a `u8` vector of size
///   // In this case, we explicitly name the call using `bar` instead of `_`.
///   bar {
///     let l in 1 .. MAX_LENGTH => initialize_l(l);
///   }: bar(Origin::Root, vec![0u8; l])
///
///   // third dispatchable: baz; this is a user dispatchable. It isn't dependent on length like the
///   // other two but has its own complexity `c` that needs setting up. It uses `caller` (in the
///   // pre-instancing block) within the code block. This is only allowed in the param instancers
///   baz1 {
///     let caller = account::<AccountId>(b"caller", 0, benchmarks_seed);
///     let c = 0 .. 10 => setup_c(&caller, c);
///   }: baz(Origin::Signed(caller))
///
///   // this is a second benchmark of the baz dispatchable with a different setup.
///   baz2 {
///     let caller = account::<AccountId>(b"caller", 0, benchmarks_seed);
///     let c = 0 .. 10 => setup_c_in_some_other_way(&caller, c);
///   }: baz(Origin::Signed(caller))
///
///   // this is benchmarking some code that is not a dispatchable.
///   populate_a_set {
///     let x in 0 .. 10_000;
///     let mut m = Vec::<u32>::new();
///     for i in 0..x {
///       m.insert(i);
///     }
///   }: { m.into_iter().collect::<BTreeSet>() }
/// }
/// ```
///
/// Test functions are automatically generated for each benchmark and are
/// accessible to you when you run `cargo test`. All tests are named
/// `test_benchmark_<benchmark_name>`, expect you to pass them the Runtime
Shaopeng Wang's avatar
Shaopeng Wang committed
/// Config, and run them in a test externalities environment. The test function
/// runs your benchmark just like a regular benchmark, but only testing at the
/// lowest and highest values for each component. The function will return
/// `Ok(())` if the benchmarks return no errors.
/// You can optionally add a `verify` code block at the end of a benchmark to
/// test any final state of your benchmark in a unit test. For example:
///
/// ```ignore
/// sort_vector {
Keith Yeung's avatar
Keith Yeung committed
///     let x in 1 .. 10000;
///     let mut m = Vec::<u32>::new();
///     for i in (0..x).rev() {
///         m.push(i);
///     }
/// }: {
Keith Yeung's avatar
Keith Yeung committed
///     m.sort();
/// } verify {
Keith Yeung's avatar
Keith Yeung committed
///     ensure!(m[0] == 0, "You forgot to sort!")
/// }
/// ```
///
/// These `verify` blocks will not affect your benchmark results!
///
/// You can construct benchmark tests like so:
///
/// ```ignore
/// #[test]
/// fn test_benchmarks() {
///   new_test_ext().execute_with(|| {
///     assert_ok!(test_benchmark_dummy());
///     assert_err!(test_benchmark_other_name(), "Bad origin");
///     assert_ok!(test_benchmark_sort_vector());
///     assert_err!(test_benchmark_broken_benchmark(), "You forgot to sort!");
///   });
/// }
/// ```
#[macro_export]
macro_rules! runtime_benchmarks {
	(
		{ $runtime:ident, $pallet:ident }
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter!(
			$runtime
			$pallet
			( )
/// Same as [`benchmarks`] but for instantiable module.
#[macro_export]
macro_rules! runtime_benchmarks_instance {
	(
		{ $runtime:ident, $pallet:ident, $instance:ident }
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter!(
			$runtime
			$pallet
			( )
			$( $rest )*
		);
	}
}

#[macro_export]
#[doc(hidden)]
macro_rules! benchmarks_iter {
	// detect and extract extra tag:
		{ $( $instance:ident )? }
		$runtime:ident
		$pallet:ident
		( $( $names:tt )* )
		( $( $names_extra:tt )* )
		#[extra]
		$name:ident
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			{ $( $instance)? }
			$runtime
			$pallet
			( $( $names )* )
			( $( $names_extra )* $name )
			$name
	// mutation arm:
		{ $( $instance:ident )? }
		$runtime:ident
		$pallet:ident
		( $( $names:tt )* ) // This contains $( $( { $instance } )? $name:ident )*
		( $( $names_extra:tt )* )
		$name:ident { $( $code:tt )* }: _ ( $origin:expr $( , $arg:expr )* )
		verify $postcode:block
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			{ $( $instance)? }
			$runtime
			$pallet
			( $( $names )* )
			( $( $names_extra )* )
			$name { $( $code )* }: $name ( $origin $( , $arg )* )
			verify $postcode
			$( $rest )*
		}
	};
	// mutation arm:
		{ $( $instance:ident )? }
		$runtime:ident
		$pallet:ident
		( $( $names:tt )* )
		( $( $names_extra:tt )* )
		$name:ident { $( $code:tt )* }: $dispatch:ident ( $origin:expr $( , $arg:expr )* )
		verify $postcode:block
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			{ $( $instance)? }
			$runtime
			$pallet
			( $( $names )* )
			( $( $names_extra )* )
			$name { $( $code )* }: {
					$pallet::Call<$runtime $(, $instance)? > as $crate::frame_support::traits::UnfilteredDispatchable
				>
				::dispatch_bypass_filter(
					$pallet::Call::<$runtime $(, $instance)? >::$dispatch($($arg),*), $origin.into()
				)?;
			}
			verify $postcode
			$( $rest )*
		}
	};
	// iteration arm:
	(
		{ $( $instance:ident )? }
		$runtime:ident
		$pallet:ident
		( $( $names:tt )* )
		( $( $names_extra:tt )* )
		$name:ident { $( $code:tt )* }: $eval:block
		verify $postcode:block
		$( $rest:tt )*
	) => {
		$crate::benchmark_backend! {
			{ $( $instance)? }
			$name
			$runtime
			$pallet
			{ }
			{ $eval }
			{ $( $code )* }
			$postcode
		}
		$crate::impl_benchmark_test!(
			$runtime
			$pallet
			{ $( $instance)? }
			$name
		);
		$crate::benchmarks_iter!(
			{ $( $instance)? }
			$runtime
			$pallet
			( $( $names )* { $( $instance )? } $name )
			( $( $names_extra )* )
			$( $rest )*
		);
	};
	// iteration-exit arm
	(
		{ $( $instance:ident )? }
		$runtime:ident
		$pallet:ident
		( $( $names:tt )* )
		( $( $names_extra:tt )* )
	) => {
		$crate::selected_benchmark!(
			$runtime
			$pallet
			{ $( $instance)? }
			$( $names )*
		);
		$crate::impl_benchmark!(
			$runtime
			$pallet
			{ $( $instance)? }
			( $( $names )* )
			( $( $names_extra ),* )
		);
	};
	// add verify block to _() format
	(
		{ $( $instance:ident )? }
		$runtime:ident
		$pallet:ident
		( $( $names:tt )* )
		( $( $names_extra:tt )* )
		$name:ident { $( $code:tt )* }: _ ( $origin:expr $( , $arg:expr )* )
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			{ $( $instance)? }
			$runtime
			$pallet
			( $( $names )* )
			( $( $names_extra )* )
			$name { $( $code )* }: _ ( $origin $( , $arg )* )
			verify { }
			$( $rest )*
		}
	};
	// add verify block to name() format
	(
		{ $( $instance:ident )? }
		$runtime:ident
		$pallet:ident
		( $( $names:tt )* )
		( $( $names_extra:tt )* )
		$name:ident { $( $code:tt )* }: $dispatch:ident ( $origin:expr $( , $arg:expr )* )
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			{ $( $instance)? }
			$runtime
			$pallet
			( $( $names )* )
			( $( $names_extra )* )
			$name { $( $code )* }: $dispatch ( $origin $( , $arg )* )
			verify { }
			$( $rest )*
		}
	};
	// add verify block to {} format
	(
		{ $( $instance:ident )? }
		$runtime:ident
		$pallet:ident
		( $( $names:tt )* )
		( $( $names_extra:tt )* )
		$name:ident { $( $code:tt )* }: $eval:block
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter!(
			{ $( $instance)? }
			$runtime
			$pallet
			( $( $names )* )
			( $( $names_extra )* )
			$name { $( $code )* }: $eval
			verify { }
			$( $rest )*
		);
	};
}

#[macro_export]
#[doc(hidden)]
macro_rules! benchmark_backend {
	// parsing arms
Xiliang Chen's avatar
Xiliang Chen committed
	(
		{ $( $instance:ident )? }
		$name:ident
		$runtime:ident
		$pallet:ident
		{ $( PRE { $( $pre_parsed:tt )* } )* }
		{ $eval:block }
		{
			let $pre_id:tt : $pre_ty:ty = $pre_ex:expr;
			$( $rest:tt )*
Xiliang Chen's avatar
Xiliang Chen committed
		}
		$postcode:block
	) => {
		$crate::benchmark_backend! {
Xiliang Chen's avatar
Xiliang Chen committed
			{ $( $instance)? }
			$name
			$runtime
			$pallet
			{
				$( PRE { $( $pre_parsed )* } )*
				PRE { $pre_id , $pre_ty , $pre_ex }
Xiliang Chen's avatar
Xiliang Chen committed
			}
			{ $eval }
			{ $( $rest )* }
			$postcode
Xiliang Chen's avatar
Xiliang Chen committed
	(
		{ $( $instance:ident )? }
		$name:ident
		$runtime:ident
		$pallet:ident
		{ $( $parsed:tt )* }
		{ $eval:block }
		{
			let $param:ident in ( $param_from:expr ) .. $param_to:expr => $param_instancer:expr;
			$( $rest:tt )*
		}
		$postcode:block
	) => {
		$crate::benchmark_backend! {
Xiliang Chen's avatar
Xiliang Chen committed
			{ $( $instance)? }
			$name
			$runtime
			$pallet
			{
				$( $parsed )*
				PARAM { $param , $param_from , $param_to , $param_instancer }
Xiliang Chen's avatar
Xiliang Chen committed
			}
			{ $eval }
			{ $( $rest )* }
			$postcode
		}
	};
	// mutation arm to look after a single tt for param_from.
Xiliang Chen's avatar
Xiliang Chen committed
	(
		{ $( $instance:ident )? }
		$name:ident
		$runtime:ident
		$pallet:ident
		{ $( $parsed:tt )* }
		{ $eval:block }
		{
			let $param:ident in $param_from:tt .. $param_to:expr => $param_instancer:expr ;
			$( $rest:tt )*
		}
		$postcode:block
	) => {
		$crate::benchmark_backend! {
			{ $( $instance)? }
Xiliang Chen's avatar
Xiliang Chen committed
			$name
			$runtime
			$pallet
			{ $( $parsed )* }
			{ $eval }
			{
				let $param in ( $param_from ) .. $param_to => $param_instancer;
				$( $rest )*
Xiliang Chen's avatar
Xiliang Chen committed
			}
			$postcode
		}
	};
	// mutation arm to look after the default tail of `=> ()`
Xiliang Chen's avatar
Xiliang Chen committed
	(
		{ $( $instance:ident )? }
		$name:ident
		$runtime:ident
		$pallet:ident
		{ $( $parsed:tt )* }
		{ $eval:block }
		{
			let $param:ident in $param_from:tt .. $param_to:expr;
			$( $rest:tt )*
		}
		$postcode:block
	) => {
		$crate::benchmark_backend! {
			{ $( $instance)? }
Xiliang Chen's avatar
Xiliang Chen committed
			$name
			$runtime
			$pallet
			{ $( $parsed )* }
			{ $eval }
			{
				let $param in $param_from .. $param_to => ();
				$( $rest )*
Xiliang Chen's avatar
Xiliang Chen committed
			}
			$postcode
		}
	};
	// mutation arm to look after `let _ =`
Xiliang Chen's avatar
Xiliang Chen committed
	(
		{ $( $instance:ident )? }
		$name:ident
		$runtime:ident
		$pallet:ident
		{ $( $parsed:tt )* }
		{ $eval:block }
		{
			let $pre_id:tt = $pre_ex:expr;
			$( $rest:tt )*
		}
		$postcode:block
	) => {
		$crate::benchmark_backend! {
			{ $( $instance)? }
Xiliang Chen's avatar
Xiliang Chen committed
			$name
			$runtime
			$pallet
			{ $( $parsed )* }
			{ $eval }
			{
				let $pre_id : _ = $pre_ex;
				$( $rest )*
Xiliang Chen's avatar
Xiliang Chen committed
			}
			$postcode
	// actioning arm
Xiliang Chen's avatar
Xiliang Chen committed
	(
		{ $( $instance:ident )? }
		$name:ident
		$runtime:ident
		$pallet:ident
		{
			$( PRE { $pre_id:tt , $pre_ty:ty , $pre_ex:expr } )*
			$( PARAM { $param:ident , $param_from:expr , $param_to:expr , $param_instancer:expr } )*
		}
		{ $eval:block }
		{ $( $post:tt )* }
		$postcode:block
	) => {
		#[allow(non_camel_case_types)]
		struct $name;
		#[allow(unused_variables)]
		impl $crate::BenchmarkingSetup<$runtime $(, $instance)?> for $name {
			fn components(&self) -> $crate::Vec<($crate::BenchmarkParameter, u32, u32)> {
				$crate::vec! [
					$(
						($crate::BenchmarkParameter::$param, $param_from, $param_to)
					),*
				]
			}

			fn instance(
				&self,
				components: &[($crate::BenchmarkParameter, u32)],
				verify: bool
			) -> Result<$crate::Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str> {
				$(
					// Prepare instance
					let $param = components.iter()
						.find(|&c| c.0 == $crate::BenchmarkParameter::$param)
						.ok_or("Could not find component in benchmark preparation.")?
						.1;
				)*
				$(
					let $pre_id : $pre_ty = $pre_ex;
				)*
				$( $param_instancer ; )*
				$( $post )*

				Ok($crate::Box::new(move || -> Result<(), &'static str> {
					$eval;
					if verify {
						$postcode;
					}
					Ok(())
				}))
// Creates a `SelectedBenchmark` enum implementing `BenchmarkingSetup`.
//
// Every variant must implement [`BenchmarkingSetup`].
//
// ```nocompile
// struct Transfer;
// impl BenchmarkingSetup for Transfer { ... }
//
// struct SetBalance;
// impl BenchmarkingSetup for SetBalance { ... }
//
// selected_benchmark!({} Transfer {} SetBalance);
#[macro_export]
#[doc(hidden)]
macro_rules! selected_benchmark {
	(
Xiliang Chen's avatar
Xiliang Chen committed
		$runtime:ident
		$pallet:ident
		{ $( $instance:ident )? }
		$( { $( $bench_inst:ident )? } $bench:ident )*
	) => {
		// The list of available benchmarks for this pallet.
		#[allow(non_camel_case_types)]
		enum SelectedBenchmark {
			$( $bench, )*
		}

		// Allow us to select a benchmark from the list of available benchmarks.
		impl $crate::BenchmarkingSetup<$runtime $(, $instance)?> for SelectedBenchmark {
			fn components(&self) -> $crate::Vec<($crate::BenchmarkParameter, u32, u32)> {
				match self {
					$(
						Self::$bench => <
							$bench as $crate::BenchmarkingSetup<$runtime $(, $bench_inst)? >
						>::components(&$bench),
					)*
			fn instance(
				&self,
				components: &[($crate::BenchmarkParameter, u32)],
				verify: bool
			) -> Result<$crate::Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str> {
				match self {
					$(
						Self::$bench => <
							$bench as $crate::BenchmarkingSetup<$runtime $(, $bench_inst)? >
						>::instance(&$bench, components, verify),
#[doc(hidden)]
macro_rules! impl_benchmark {
	(
Xiliang Chen's avatar
Xiliang Chen committed
		$runtime:ident
		$pallet:ident
		{ $( $instance:ident )? }
		( $( { $( $name_inst:ident )? } $name:ident )* )
		( $( $name_extra:ident ),* )
	) => {
		pub struct Benchmark;

		impl $crate::Benchmarking<$crate::BenchmarkResults> for Benchmark {
			fn benchmarks(extra: bool) -> $crate::Vec<&'static [u8]> {
				let mut all = $crate::vec![ $( stringify!($name).as_ref() ),* ];
				if !extra {
					let extra = [ $( stringify!($name_extra).as_ref() ),* ];
					all.retain(|x| !extra.contains(x));
			}

			fn run_benchmark(
				extrinsic: &[u8],
				lowest_range_values: &[u32],
				highest_range_values: &[u32],
				steps: &[u32],
				repeat: u32,
				whitelist: &[$crate::TrackedStorageKey],
				verify: bool,
			) -> Result<$crate::Vec<$crate::BenchmarkResults>, &'static str> {
				// Map the input to the selected benchmark.
				let extrinsic = $crate::sp_std::str::from_utf8(extrinsic)
					.map_err(|_| "`extrinsic` is not a valid utf8 string!")?;
				let selected_benchmark = match extrinsic {
					$( stringify!($name) => SelectedBenchmark::$name, )*
					_ => return Err("Could not find extrinsic."),
				};
				let mut results: $crate::Vec<$crate::BenchmarkResults> = $crate::Vec::new();
				if repeat == 0 {
					return Ok(results);
				}

Xiliang Chen's avatar
Xiliang Chen committed
				// Add whitelist to DB including whitelisted caller
				let mut whitelist = whitelist.to_vec();
				let whitelisted_caller_key =
					<frame_system::Account::<$runtime> as $crate::frame_support::storage::StorageMap<_,_>>::hashed_key_for(
Shaopeng Wang's avatar
Shaopeng Wang committed
						$crate::whitelisted_caller::<<$runtime as frame_system::Config>::AccountId>()
Xiliang Chen's avatar
Xiliang Chen committed
					);
				whitelist.push(whitelisted_caller_key.into());
				$crate::benchmarking::set_whitelist(whitelist);
				// Warm up the DB
				$crate::benchmarking::commit_db();
				$crate::benchmarking::wipe_db();

				let components = <
					SelectedBenchmark as $crate::BenchmarkingSetup<$runtime $(, $instance)?>
				>::components(&selected_benchmark);

				// Default number of steps for a component.
				let mut prev_steps = 10;

				let repeat_benchmark = |
					repeat: u32,
					c: &[($crate::BenchmarkParameter, u32)],
					results: &mut $crate::Vec<$crate::BenchmarkResults>,
					verify: bool,
				| -> Result<(), &'static str> {
					// Run the benchmark `repeat` times.
					for _ in 0..repeat {
						// Set up the externalities environment for the setup we want to
						// benchmark.
						let closure_to_benchmark = <
							SelectedBenchmark as $crate::BenchmarkingSetup<$runtime $(, $instance)?>
						>::instance(&selected_benchmark, c, verify)?;
						// Set the block number to at least 1 so events are deposited.
						if $crate::Zero::is_zero(&frame_system::Pallet::<$runtime>::block_number()) {
							frame_system::Pallet::<$runtime>::set_block_number(1u32.into());
						// Commit the externalities to the database, flushing the DB cache.
						// This will enable worst case scenario for reading from the database.
						$crate::benchmarking::commit_db();

						// Reset the read/write counter so we don't count operations in the setup process.
						$crate::benchmarking::reset_read_write_count();

						if verify {
							closure_to_benchmark()?;
						} else {
							// Time the extrinsic logic.
							$crate::frame_support::log::trace!(
								target: "benchmark",
								"Start Benchmark: {:?}", c
							);

							let start_pov = $crate::benchmarking::proof_size();
							let start_extrinsic = $crate::benchmarking::current_time();
							closure_to_benchmark()?;
							let finish_extrinsic = $crate::benchmarking::current_time();
							let end_pov = $crate::benchmarking::proof_size();

							// Calculate the diff caused by the benchmark.
							let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic);
							let diff_pov = match (start_pov, end_pov) {
								(Some(start), Some(end)) => end.saturating_sub(start),
								_ => Default::default(),
							};

							// Commit the changes to get proper write count
							$crate::benchmarking::commit_db();
							$crate::frame_support::log::trace!(
								target: "benchmark",
								"End Benchmark: {} ns", elapsed_extrinsic
							);
							let read_write_count = $crate::benchmarking::read_write_count();
							$crate::frame_support::log::trace!(
								target: "benchmark",
								"Read/Write Count {:?}", read_write_count
							);

							// Time the storage root recalculation.
							let start_storage_root = $crate::benchmarking::current_time();
							$crate::storage_root();
							let finish_storage_root = $crate::benchmarking::current_time();
							let elapsed_storage_root = finish_storage_root - start_storage_root;

							results.push($crate::BenchmarkResults {
								components: c.to_vec(),
								extrinsic_time: elapsed_extrinsic,
								storage_root_time: elapsed_storage_root,
								reads: read_write_count.0,
								repeat_reads: read_write_count.1,
								writes: read_write_count.2,
								repeat_writes: read_write_count.3,
								proof_size: diff_pov,

						// Wipe the DB back to the genesis state.
						$crate::benchmarking::wipe_db();
					}
				if components.is_empty() {
					if verify {
						// If `--verify` is used, run the benchmark once to verify it would complete.
						repeat_benchmark(1, Default::default(), &mut $crate::Vec::new(), true)?;
					}
					repeat_benchmark(repeat, Default::default(), &mut results, false)?;
				} else {
					// Select the component we will be benchmarking. Each component will be benchmarked.
					for (idx, (name, low, high)) in components.iter().enumerate() {
						// Get the number of steps for this component.
						let steps = steps.get(idx).cloned().unwrap_or(prev_steps);
						prev_steps = steps;

						// Skip this loop if steps is zero
						if steps == 0 { continue }

						let lowest = lowest_range_values.get(idx).cloned().unwrap_or(*low);
						let highest = highest_range_values.get(idx).cloned().unwrap_or(*high);

						let diff = highest - lowest;

						// Create up to `STEPS` steps for that component between high and low.
						let step_size = (diff / steps).max(1);
						let num_of_steps = diff / step_size + 1;

						for s in 0..num_of_steps {
							// This is the value we will be testing for component `name`
							let component_value = lowest + step_size * s;

							// Select the max value for all the other components.
							let c: $crate::Vec<($crate::BenchmarkParameter, u32)> = components.iter()
								.enumerate()
								.map(|(idx, (n, _, h))|
									if n == name {
										(*n, component_value)
									} else {
										(*n, *highest_range_values.get(idx).unwrap_or(h))
									}
								)
								.collect();

							if verify {
								// If `--verify` is used, run the benchmark once to verify it would complete.
								repeat_benchmark(1, &c, &mut $crate::Vec::new(), true)?;
							}
							repeat_benchmark(repeat, &c, &mut results, false)?;

		/// Test a particular benchmark by name.
		///
		/// This isn't called `test_benchmark_by_name` just in case some end-user eventually
		/// writes a benchmark, itself called `by_name`; the function would be shadowed in
		/// that case.
		///
		/// This is generally intended to be used by child test modules such as those created
		/// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet
		/// author chooses not to implement benchmarks.
		#[cfg(test)]
		#[allow(unused)]
		fn test_bench_by_name(name: &[u8]) -> Result<(), &'static str> {
			let name = $crate::sp_std::str::from_utf8(name)
				.map_err(|_| "`name` is not a valid utf8 string!")?;
			match name {
				$( stringify!($name) => {
					$crate::paste::paste! { [< test_benchmark_ $name >]() }
				} )*
				_ => Err("Could not find test for requested benchmark."),
			}
		}
// This creates a unit test for one benchmark of the main benchmark macro.
// It runs the benchmark using the `high` and `low` value for each component
// and ensure that everything completes successfully.
#[macro_export]
#[doc(hidden)]
macro_rules! impl_benchmark_test {
Xiliang Chen's avatar
Xiliang Chen committed
		$runtime:ident
		$pallet:ident
		{ $( $instance:ident )? }
		$name:ident
		$crate::paste::item! {
			fn [<test_benchmark_ $name>] () -> Result<(), &'static str>
			{
				let selected_benchmark = SelectedBenchmark::$name;
				let components = <
					SelectedBenchmark as $crate::BenchmarkingSetup<$runtime, _>
				>::components(&selected_benchmark);

				let execute_benchmark = |
					c: $crate::Vec<($crate::BenchmarkParameter, u32)>
				| -> Result<(), &'static str> {
					// Set up the benchmark, return execution + verification function.
					let closure_to_verify = <
						SelectedBenchmark as $crate::BenchmarkingSetup<$runtime, _>
					>::instance(&selected_benchmark, &c, true)?;

					// Set the block number to at least 1 so events are deposited.
					if $crate::Zero::is_zero(&frame_system::Pallet::<$runtime>::block_number()) {
						frame_system::Pallet::<$runtime>::set_block_number(1u32.into());
					// Run execution + verification
					closure_to_verify()?;
					// Reset the state
					$crate::benchmarking::wipe_db();
				if components.is_empty() {
					execute_benchmark(Default::default())?;
				} else {
					for (_, (name, low, high)) in components.iter().enumerate() {
						// Test only the low and high value, assuming values in the middle won't break
						for component_value in $crate::vec![low, high] {
							// Select the max value for all the other components.
							let c: $crate::Vec<($crate::BenchmarkParameter, u32)> = components.iter()
								.enumerate()
								.map(|(_, (n, _, h))|
									if n == name {
										(*n, *component_value)
									} else {
										(*n, *h)
									}
								)
								.collect();

							execute_benchmark(c)?;
						}
/// This creates a test suite which runs the module's benchmarks.
///
/// When called in `pallet_example` as
///
/// ```rust,ignore
/// impl_benchmark_test_suite!(crate::tests::new_test_ext());
/// ```
///
/// It expands to the equivalent of:
///
/// ```rust,ignore
/// #[cfg(test)]
/// mod tests {
///     use super::*;
///     use crate::tests::new_test_ext;
///     use frame_support::assert_ok;
///
///     #[test]
///     fn test_benchmarks() {
///         new_test_ext().execute_with(|| {
///             assert_ok!(test_benchmark_accumulate_dummy());
///             assert_ok!(test_benchmark_set_dummy());
///             assert_ok!(test_benchmark_another_set_dummy());
///             assert_ok!(test_benchmark_sort_vector());
///         });
///     }
/// }
/// ```
///
/// ## Arguments
///
/// The first argument, `new_test_ext`, must be a function call which returns
/// either a `sp_io::TestExternalities`, or some other type with a similar
/// interface.
///
/// Note that this function call is _not_ evaluated at compile time, but is
/// instead copied textually into each appropriate invocation site.
///
/// There is an optional second argument, with keyword syntax: `benchmarks_path
/// = path_to_benchmarks_invocation`. In the typical case in which this macro is
/// in the same module as the `benchmarks!` invocation, you don't need to supply
/// this. However, if the `impl_benchmark_test_suite!` invocation is in a
/// different module than the `runtime_benchmarks!` invocation, then you should
/// provide the path to the module containing the `benchmarks!` invocation:
///
/// ```rust,ignore
/// mod benches {
///     runtime_benchmarks!{