diff --git a/Cargo.lock b/Cargo.lock index 7bbe5469212b2..6aafb0e175d98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2138,6 +2138,7 @@ version = "4.0.0-dev" dependencies = [ "array-bytes", "frame-support", + "frame-support-procedural", "frame-system", "linregress", "log", @@ -2155,6 +2156,7 @@ dependencies = [ "sp-runtime-interface", "sp-std", "sp-storage", + "static_assertions", ] [[package]] @@ -2351,7 +2353,6 @@ dependencies = [ "sp-std", "sp-tracing", "sp-weights", - "static_assertions", "tt-call", ] diff --git a/bin/node-template/pallets/template/src/benchmarking.rs b/bin/node-template/pallets/template/src/benchmarking.rs index d496a9fc89b1a..1790849970440 100644 --- a/bin/node-template/pallets/template/src/benchmarking.rs +++ b/bin/node-template/pallets/template/src/benchmarking.rs @@ -4,7 +4,7 @@ use super::*; #[allow(unused)] use crate::Pallet as Template; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; benchmarks! { diff --git a/frame/alliance/src/benchmarking.rs b/frame/alliance/src/benchmarking.rs index a34b76bd96ece..f312e032bbb0f 100644 --- a/frame/alliance/src/benchmarking.rs +++ b/frame/alliance/src/benchmarking.rs @@ -25,7 +25,7 @@ use sp_std::{ prelude::*, }; -use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet}; use frame_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin as SystemOrigin}; diff --git a/frame/assets/src/benchmarking.rs b/frame/assets/src/benchmarking.rs index 235d1d9374504..9acf69f1e4ef4 100644 --- a/frame/assets/src/benchmarking.rs +++ b/frame/assets/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{ +use frame_benchmarking::v1::{ account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, }; use frame_support::{ diff --git a/frame/babe/src/benchmarking.rs b/frame/babe/src/benchmarking.rs index 20002c97f8e56..70605e4cff34c 100644 --- a/frame/babe/src/benchmarking.rs +++ b/frame/babe/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; type Header = sp_runtime::generic::Header; diff --git a/frame/bags-list/src/benchmarks.rs b/frame/bags-list/src/benchmarks.rs index 1f66697cb6765..74ae5f31ac81f 100644 --- a/frame/bags-list/src/benchmarks.rs +++ b/frame/bags-list/src/benchmarks.rs @@ -19,13 +19,15 @@ use super::*; use crate::list::List; -use frame_benchmarking::{account, whitelist_account, whitelisted_caller}; +use frame_benchmarking::v1::{ + account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, +}; use frame_election_provider_support::ScoreProvider; use frame_support::{assert_ok, traits::Get}; use frame_system::RawOrigin as SystemOrigin; use sp_runtime::traits::One; -frame_benchmarking::benchmarks_instance_pallet! { +benchmarks_instance_pallet! { rebag_non_terminal { // An expensive case for rebag-ing (rebag a non-terminal node): // diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index f85ff43b715fe..d80aa3ec8e57e 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -22,8 +22,7 @@ use super::*; use crate::Pallet as Balances; -use frame_benchmarking::{account, impl_benchmark_test_suite, whitelisted_caller}; -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; use frame_system::RawOrigin; const SEED: u32 = 0; diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index b2a9621ebf250..4ff2077d7f7c5 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -20,6 +20,7 @@ paste = "1.0" scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "../support/procedural" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../primitives/application-crypto" } @@ -29,6 +30,7 @@ sp-runtime = { version = "7.0.0", default-features = false, path = "../../primit sp-runtime-interface = { version = "7.0.0", default-features = false, path = "../../primitives/runtime-interface" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } sp-storage = { version = "7.0.0", default-features = false, path = "../../primitives/storage" } +static_assertions = "1.1.0" [dev-dependencies] array-bytes = "4.1" diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index 525370e23385a..a3bf809f6b9ba 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -50,2005 +50,226 @@ pub use sp_std::{self, boxed::Box, prelude::Vec, str, vec}; pub use sp_storage::{well_known_keys, TrackedStorageKey}; pub use utils::*; -/// Whitelist the given account. -#[macro_export] -macro_rules! whitelist { - ($acc:ident) => { - frame_benchmarking::benchmarking::add_to_whitelist( - frame_system::Account::::hashed_key_for(&$acc).into(), - ); - }; -} +pub mod v1; +pub use v1::*; -/// 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); -/// } -/// ``` +/// Contains macros, structs, and traits associated with v2 of the pallet benchmarking syntax. /// -/// 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 parenthesized. +/// The [`v2::benchmarks`] and [`v2::instance_benchmarks`] macros can be used to designate a +/// module as a benchmarking module that can contain benchmarks and benchmark tests. The +/// `#[benchmarks]` variant will set up a regular, non-instance benchmarking module, and the +/// `#[instance_benchmarks]` variant will set up the module in instance benchmarking mode. /// -/// 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 expressions to be evaluated in a benchmark (including for example, -/// `on_initialize`). +/// Benchmarking modules should be gated behind a `#[cfg(feature = "runtime-benchmarks")]` +/// feature gate to ensure benchmarking code that is only compiled when the +/// `runtime-benchmarks` feature is enabled is not referenced. /// -/// Note that the ranges are *inclusive* on both sides. This is in contrast to ranges in Rust which -/// are left-inclusive right-exclusive. +/// The following is the general syntax for a benchmarks (or instance benchmarks) module: /// -/// 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. +/// ## General Syntax /// -/// Example: /// ```ignore -/// benchmarks! { -/// where_clause { where T::A: From } // Optional line to give additional bound on `T`. -/// -/// // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of -/// // size `l` -/// foo { -/// let caller = account::(b"caller", 0, benchmarks_seed); -/// let l in 1 .. MAX_LENGTH => initialize_l(l); -/// }: _(RuntimeOrigin::Signed(caller), vec![0u8; l]) -/// -/// // second dispatchable: bar; this is a root dispatchable and accepts a `u8` vector of size -/// // `l`. -/// // In this case, we explicitly name the call using `bar` instead of `_`. -/// bar { -/// let l in 1 .. MAX_LENGTH => initialize_l(l); -/// }: bar(RuntimeOrigin::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 -/// // of arms. -/// baz1 { -/// let caller = account::(b"caller", 0, benchmarks_seed); -/// let c = 0 .. 10 => setup_c(&caller, c); -/// }: baz(RuntimeOrigin::Signed(caller)) -/// -/// // this is a second benchmark of the baz dispatchable with a different setup. -/// baz2 { -/// let caller = account::(b"caller", 0, benchmarks_seed); -/// let c = 0 .. 10 => setup_c_in_some_other_way(&caller, c); -/// }: baz(RuntimeOrigin::Signed(caller)) -/// -/// // You may optionally specify the origin type if it can't be determined automatically like -/// // this. -/// baz3 { -/// let caller = account::(b"caller", 0, benchmarks_seed); -/// let l in 1 .. MAX_LENGTH => initialize_l(l); -/// }: baz(RuntimeOrigin::Signed(caller), vec![0u8; l]) -/// -/// // this is benchmarking some code that is not a dispatchable. -/// populate_a_set { -/// let x in 0 .. 10_000; -/// let mut m = Vec::::new(); -/// for i in 0..x { -/// m.insert(i); -/// } -/// }: { m.into_iter().collect::() } -/// } -/// ``` -/// -/// Test functions are automatically generated for each benchmark and are accessible to you when you -/// run `cargo test`. All tests are named `test_benchmark_`, implemented on the -/// Pallet struct, 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. -/// -/// It is also possible to generate one #[test] function per benchmark by calling the -/// `impl_benchmark_test_suite` macro inside the `benchmarks` block. The functions will be named -/// `bench_` and can be run via `cargo test`. -/// You will see one line of output per benchmark. This approach will give you more understandable -/// error messages and allows for parallel benchmark execution. -/// -/// 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 { -/// let x in 1 .. 10000; -/// let mut m = Vec::::new(); -/// for i in (0..x).rev() { -/// m.push(i); -/// } -/// }: { -/// m.sort(); -/// } verify { -/// ensure!(m[0] == 0, "You forgot to sort!") -/// } -/// ``` -/// -/// These `verify` blocks will not affect your benchmark results! -/// -/// You can construct benchmark by using the `impl_benchmark_test_suite` macro or -/// by manually implementing them like so: -/// -/// ```ignore -/// #[test] -/// fn test_benchmarks() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(Pallet::::test_benchmark_dummy()); -/// assert_err!(Pallet::::test_benchmark_other_name(), "Bad origin"); -/// assert_ok!(Pallet::::test_benchmark_sort_vector()); -/// assert_err!(Pallet::::test_benchmark_broken_benchmark(), "You forgot to sort!"); -/// }); -/// } -/// ``` -#[macro_export] -macro_rules! benchmarks { - ( - $( $rest:tt )* - ) => { - $crate::benchmarks_iter!( - { } - { } - { } - ( ) - ( ) - ( ) - ( ) - $( $rest )* - ); - } -} - -/// Same as [`benchmarks`] but for instantiable module. -/// -/// NOTE: For pallet declared with [`frame_support::pallet`], use [`benchmarks_instance_pallet`]. -#[macro_export] -macro_rules! benchmarks_instance { - ( - $( $rest:tt )* - ) => { - $crate::benchmarks_iter!( - { } - { I: Instance } - { } - ( ) - ( ) - ( ) - ( ) - $( $rest )* - ); - } -} - -/// Same as [`benchmarks`] but for instantiable pallet declared [`frame_support::pallet`]. -/// -/// NOTE: For pallet declared with `decl_module!`, use [`benchmarks_instance`]. -#[macro_export] -macro_rules! benchmarks_instance_pallet { - ( - $( $rest:tt )* - ) => { - $crate::benchmarks_iter!( - { } - { I: 'static } - { } - ( ) - ( ) - ( ) - ( ) - $( $rest )* - ); - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! benchmarks_iter { - // detect and extract `impl_benchmark_test_suite` call: - // - with a semi-colon - ( - { } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - impl_benchmark_test_suite!( - $bench_module:ident, - $new_test_ext:expr, - $test:path - $(, $( $args:tt )* )?); - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $bench_module, $new_test_ext, $test $(, $( $args )* )? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( $rest )* - } - }; - // - without a semicolon - ( - { } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - impl_benchmark_test_suite!( - $bench_module:ident, - $new_test_ext:expr, - $test:path - $(, $( $args:tt )* )?) - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $bench_module, $new_test_ext, $test $(, $( $args )* )? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( $rest )* - } - }; - // detect and extract where clause: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - where_clause { where $( $where_bound:tt )* } - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound)? } - { $( $where_bound )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( $rest )* - } - }; - // detect and extract `#[skip_meta]` tag: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - #[skip_meta] - $( #[ $($attributes:tt)+ ] )* - $name:ident - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* $name ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( #[ $( $attributes )+ ] )* - $name - $( $rest )* - } - }; - // detect and extract `#[extra]` tag: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - #[extra] - $( #[ $($attributes:tt)+ ] )* - $name:ident - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* $name ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( #[ $( $attributes )+ ] )* - $name - $( $rest )* - } - }; - // detect and extract `#[pov_mode = Mode { Pallet::Storage: Mode ... }]` tag: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $old_pov_name:ident: $( $old_storage:path = $old_pov_mode:ident )*; )* ) - #[pov_mode = $mode:ident $( { $( $storage:path: $pov_mode:ident )* } )?] - $( #[ $($attributes:tt)+ ] )* - $name:ident - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $name: ALL = $mode $($( $storage = $pov_mode )*)?; $( $old_pov_name: $( $old_storage = $old_pov_mode )*; )* ) - $( #[ $( $attributes )+ ] )* - $name - $( $rest )* - } - }; - // mutation arm: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) // This contains $( $( { $instance } )? $name:ident )* - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: _ $(< $origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) - verify $postcode:block - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { $( $code )* }: $name $(< $origin_type >)? ( $origin $( , $arg )* ) - verify $postcode - $( $rest )* - } - }; - // mutation arm: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) - verify $postcode:block - $( $rest:tt )* - ) => { - $crate::paste::paste! { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { - $( $code )* - let __call = Call::< - T - $( , $instance )? - >:: [< new_call_variant_ $dispatch >] ( - $($arg),* - ); - let __benchmarked_call_encoded = $crate::frame_support::codec::Encode::encode( - &__call - ); - }: { - let __call_decoded = < - Call - as $crate::frame_support::codec::Decode - >::decode(&mut &__benchmarked_call_encoded[..]) - .expect("call is encoded above, encoding must be correct"); - let __origin = $crate::to_origin!($origin $(, $origin_type)?); - as $crate::frame_support::traits::UnfilteredDispatchable - >::dispatch_bypass_filter(__call_decoded, __origin)?; - } - verify $postcode - $( $rest )* - } - } - }; - // iteration arm: - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: $eval:block - verify $postcode:block - $( $rest:tt )* - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { } - { $eval } - { $( $code )* } - $postcode - } - - #[cfg(test)] - $crate::impl_benchmark_test!( - { $( $where_clause )* } - { $( $instance: $instance_bound )? } - $name - ); - - $crate::benchmarks_iter!( - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* { $( $instance )? } $name ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $( $rest )* - ); - }; - // iteration-exit arm which generates a #[test] function for each case. - ( - { $bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - ) => { - $crate::selected_benchmark!( - { $( $where_clause)* } - { $( $instance: $instance_bound )? } - $( $names )* - ); - $crate::impl_benchmark!( - { $( $where_clause )* } - { $( $instance: $instance_bound )? } - ( $( $names )* ) - ( $( $names_extra ),* ) - ( $( $names_skip_meta ),* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - ); - $crate::impl_test_function!( - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - $bench_module, - $new_test_ext, - $test - $(, $( $args )* )? - ); - }; - // iteration-exit arm which doesn't generate a #[test] function for all cases. - ( - { } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - ) => { - $crate::selected_benchmark!( - { $( $where_clause)* } - { $( $instance: $instance_bound )? } - $( $names )* - ); - $crate::impl_benchmark!( - { $( $where_clause )* } - { $( $instance: $instance_bound )? } - ( $( $names )* ) - ( $( $names_extra ),* ) - ( $( $names_skip_meta ),* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - ); - }; - // add verify block to _() format - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: _ $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { $( $code )* }: _ $(<$origin_type>)? ( $origin $( , $arg )* ) - verify { } - $( $rest )* - } - }; - // add verify block to name() format - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) - $( $rest:tt )* - ) => { - $crate::benchmarks_iter! { - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { $( $code )* }: $dispatch $(<$origin_type>)? ( $origin $( , $arg )* ) - verify { } - $( $rest )* - } - }; - // add verify block to {} format - ( - { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } - { $( $instance:ident: $instance_bound:tt )? } - { $( $where_clause:tt )* } - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - $name:ident { $( $code:tt )* }: $(<$origin_type:ty>)? $eval:block - $( $rest:tt )* - ) => { - $crate::benchmarks_iter!( - { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } - { $( $instance: $instance_bound )? } - { $( $where_clause )* } - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - ( $( $pov_name: $( $storage = $pov_mode )*; )* ) - $name { $( $code )* }: $(<$origin_type>)? $eval - verify { } - $( $rest )* - ); - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! to_origin { - ($origin:expr) => { - $origin.into() - }; - ($origin:expr, $origin_type:ty) => { - <::RuntimeOrigin as From<$origin_type>>::from($origin) - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! benchmark_backend { - // parsing arms - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( PRE { $( $pre_parsed:tt )* } )* } - { $eval:block } - { - let $pre_id:tt : $pre_ty:ty = $pre_ex:expr; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { - $( PRE { $( $pre_parsed )* } )* - PRE { $pre_id , $pre_ty , $pre_ex } - } - { $eval } - { $( $rest )* } - $postcode - } - }; - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( $parsed:tt )* } - { $eval:block } - { - let $param:ident in ( $param_from:expr ) .. $param_to:expr => $param_instancer:expr; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { - $( $parsed )* - PARAM { $param , $param_from , $param_to , $param_instancer } - } - { $eval } - { $( $rest )* } - $postcode - } - }; - // mutation arm to look after a single tt for param_from. - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( $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: $instance_bound )? } - $name - { $( $where_clause )* } - { $( $parsed )* } - { $eval } - { - let $param in ( $param_from ) .. $param_to => $param_instancer; - $( $rest )* - } - $postcode - } - }; - // mutation arm to look after the default tail of `=> ()` - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( $parsed:tt )* } - { $eval:block } - { - let $param:ident in $param_from:tt .. $param_to:expr; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { $( $parsed )* } - { $eval } - { - let $param in $param_from .. $param_to => (); - $( $rest )* - } - $postcode - } - }; - // mutation arm to look after `let _ =` - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { $( $parsed:tt )* } - { $eval:block } - { - let $pre_id:tt = $pre_ex:expr; - $( $rest:tt )* - } - $postcode:block - ) => { - $crate::benchmark_backend! { - { $( $instance: $instance_bound )? } - $name - { $( $where_clause )* } - { $( $parsed )* } - { $eval } - { - let $pre_id : _ = $pre_ex; - $( $rest )* - } - $postcode - } - }; - // actioning arm - ( - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - { $( $where_clause:tt )* } - { - $( 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, $instance: $instance_bound )? > - $crate::BenchmarkingSetup for $name - where $( $where_clause )* - { - 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 Result<(), $crate::BenchmarkError>>, $crate::BenchmarkError> { - $( - // 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<(), $crate::BenchmarkError> { - $eval; - if verify { - $postcode; - } - Ok(()) - })) - } - } - }; -} - -// Creates #[test] functions for the given bench cases. -#[macro_export] -#[doc(hidden)] -macro_rules! impl_bench_case_tests { - ( - { $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr } - { $( $names_extra:tt )* } - $( { $( $bench_inst:ident )? } $bench:ident )* - ) - => { - $crate::impl_bench_name_tests!( - $module, $new_test_exec, $exec_name, $test, $extra, - { $( $names_extra )* }, - $( { $bench } )+ - ); - } -} - -// Creates a #[test] function for the given bench name. -#[macro_export] -#[doc(hidden)] -macro_rules! impl_bench_name_tests { - // recursion anchor - ( - $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr, - { $( $names_extra:tt )* }, - { $name:ident } - ) => { - $crate::paste::paste! { - #[test] - fn [] () { - $new_test_exec.$exec_name(|| { - // Skip all #[extra] benchmarks if $extra is false. - if !($extra) { - let disabled = $crate::vec![ $( stringify!($names_extra).as_ref() ),* ]; - if disabled.contains(&stringify!($name)) { - $crate::log::error!( - "INFO: extra benchmark skipped - {}", - stringify!($name), - ); - return (); - } - } - - // Same per-case logic as when all cases are run in the - // same function. - match std::panic::catch_unwind(|| { - $module::<$test>::[< test_benchmark_ $name >] () - }) { - Err(err) => { - panic!("{}: {:?}", stringify!($name), err); - }, - Ok(Err(err)) => { - match err { - $crate::BenchmarkError::Stop(err) => { - panic!("{}: {:?}", stringify!($name), err); - }, - $crate::BenchmarkError::Override(_) => { - // This is still considered a success condition. - $crate::log::error!( - "WARNING: benchmark error overrided - {}", - stringify!($name), - ); - }, - $crate::BenchmarkError::Skip => { - // This is considered a success condition. - $crate::log::error!( - "WARNING: benchmark error skipped - {}", - stringify!($name), - ); - }, - $crate::BenchmarkError::Weightless => { - // This is considered a success condition. - $crate::log::error!( - "WARNING: benchmark weightless skipped - {}", - stringify!($name), - ); - } - } - }, - Ok(Ok(())) => (), - } - }); - } - } - }; - // recursion tail - ( - $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr, - { $( $names_extra:tt )* }, - { $name:ident } $( { $rest:ident } )+ - ) => { - // car - $crate::impl_bench_name_tests!($module, $new_test_exec, $exec_name, $test, $extra, - { $( $names_extra )* }, { $name }); - // cdr - $crate::impl_bench_name_tests!($module, $new_test_exec, $exec_name, $test, $extra, - { $( $names_extra )* }, $( { $rest } )+); - }; -} - -// 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 { - ( - { $( $where_clause:tt )* } - { $( $instance:ident: $instance_bound:tt )? } - $( { $( $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, $instance: $instance_bound )? > - $crate::BenchmarkingSetup for SelectedBenchmark - where $( $where_clause )* - { - fn components(&self) -> $crate::Vec<($crate::BenchmarkParameter, u32, u32)> { - match self { - $( - Self::$bench => < - $bench as $crate::BenchmarkingSetup - >::components(&$bench), - )* - } - } - - fn instance( - &self, - components: &[($crate::BenchmarkParameter, u32)], - verify: bool - ) -> Result<$crate::Box Result<(), $crate::BenchmarkError>>, $crate::BenchmarkError> { - match self { - $( - Self::$bench => < - $bench as $crate::BenchmarkingSetup - >::instance(&$bench, components, verify), - )* - } - } - } - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! impl_benchmark { - ( - { $( $where_clause:tt )* } - { $( $instance:ident: $instance_bound:tt )? } - ( $( { $( $name_inst:ident )? } $name:ident )* ) - ( $( $name_extra:ident ),* ) - ( $( $name_skip_meta:ident ),* ) - ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) - ) => { - // We only need to implement benchmarks for the runtime-benchmarks feature or testing. - #[cfg(any(feature = "runtime-benchmarks", test))] - impl, $instance: $instance_bound )? > - $crate::Benchmarking for Pallet - where T: frame_system::Config, $( $where_clause )* - { - fn benchmarks(extra: bool) -> $crate::Vec<$crate::BenchmarkMetadata> { - $($crate::validate_pov_mode!( - $pov_name: $( $storage = $pov_mode )*; - );)* - let mut all_names = $crate::vec![ $( stringify!($name).as_ref() ),* ]; - if !extra { - let extra = [ $( stringify!($name_extra).as_ref() ),* ]; - all_names.retain(|x| !extra.contains(x)); - } - let pov_modes: $crate::Vec<($crate::Vec, $crate::Vec<($crate::Vec, $crate::Vec)>)> = $crate::vec![ - $( - (stringify!($pov_name).as_bytes().to_vec(), - $crate::vec![ - $( ( stringify!($storage).as_bytes().to_vec(), - stringify!($pov_mode).as_bytes().to_vec() ), )* - ]), - )* - ]; - all_names.into_iter().map(|benchmark| { - let selected_benchmark = match benchmark { - $( stringify!($name) => SelectedBenchmark::$name, )* - _ => panic!("all benchmarks should be selectable"), - }; - let name = benchmark.as_bytes().to_vec(); - let components = < - SelectedBenchmark as $crate::BenchmarkingSetup - >::components(&selected_benchmark); - - $crate::BenchmarkMetadata { - name: name.clone(), - components, - pov_modes: pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()).unwrap_or_default(), - } - }).collect::<$crate::Vec<_>>() - } - - fn run_benchmark( - extrinsic: &[u8], - c: &[($crate::BenchmarkParameter, u32)], - whitelist: &[$crate::TrackedStorageKey], - verify: bool, - internal_repeats: u32, - ) -> Result<$crate::Vec<$crate::BenchmarkResult>, $crate::BenchmarkError> { - // Map the input to the selected benchmark. - let extrinsic = $crate::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.".into()), - }; - - // Add whitelist to DB including whitelisted caller - let mut whitelist = whitelist.to_vec(); - let whitelisted_caller_key = - as $crate::frame_support::storage::StorageMap<_,_>>::hashed_key_for( - $crate::whitelisted_caller::() - ); - whitelist.push(whitelisted_caller_key.into()); - // Whitelist the transactional layer. - let transactional_layer_key = $crate::TrackedStorageKey::new( - $crate::frame_support::storage::transactional::TRANSACTION_LEVEL_KEY.into() - ); - whitelist.push(transactional_layer_key); - // Whitelist the `:extrinsic_index`. - let extrinsic_index = $crate::TrackedStorageKey::new( - $crate::well_known_keys::EXTRINSIC_INDEX.into() - ); - whitelist.push(extrinsic_index); - - $crate::benchmarking::set_whitelist(whitelist.clone()); - - let mut results: $crate::Vec<$crate::BenchmarkResult> = $crate::Vec::new(); - - // Always do at least one internal repeat... - for _ in 0 .. internal_repeats.max(1) { - // Always reset the state after the benchmark. - $crate::defer!($crate::benchmarking::wipe_db()); - - // Set up the externalities environment for the setup we want to - // benchmark. - let closure_to_benchmark = < - SelectedBenchmark as $crate::BenchmarkingSetup - >::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::::block_number()) { - frame_system::Pallet::::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(); - - // Access all whitelisted keys to get them into the proof recorder since the - // recorder does now have a whitelist. - for key in &whitelist { - $crate::frame_support::storage::unhashed::get_raw(&key.key); - } - - // Reset the read/write counter so we don't count operations in the setup process. - $crate::benchmarking::reset_read_write_count(); - - // Time the extrinsic logic. - $crate::log::trace!( - target: "benchmark", - "Start Benchmark: {} ({:?}) verify {}", - extrinsic, - c, - verify - ); - - 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::log::trace!( - target: "benchmark", - "End Benchmark: {} ns", elapsed_extrinsic - ); - let read_write_count = $crate::benchmarking::read_write_count(); - $crate::log::trace!( - target: "benchmark", - "Read/Write Count {:?}", read_write_count - ); - $crate::log::trace!( - target: "benchmark", - "Proof sizes: before {:?} after {:?} diff {}", &start_pov, &end_pov, &diff_pov - ); - - // Time the storage root recalculation. - let start_storage_root = $crate::benchmarking::current_time(); - $crate::storage_root($crate::StateVersion::V1); - let finish_storage_root = $crate::benchmarking::current_time(); - let elapsed_storage_root = finish_storage_root - start_storage_root; - - let skip_meta = [ $( stringify!($name_skip_meta).as_ref() ),* ]; - let read_and_written_keys = if skip_meta.contains(&extrinsic) { - $crate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] - } else { - $crate::benchmarking::get_read_and_written_keys() - }; - - results.push($crate::BenchmarkResult { - 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, - keys: read_and_written_keys, - }); - } - - return Ok(results); - } - } - - #[cfg(test)] - impl, $instance: $instance_bound )? > - Pallet - where T: frame_system::Config, $( $where_clause )* - { - /// 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. - #[allow(unused)] - fn test_bench_by_name(name: &[u8]) -> Result<(), $crate::BenchmarkError> { - let name = $crate::str::from_utf8(name) - .map_err(|_| -> $crate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; - match name { - $( stringify!($name) => { - $crate::paste::paste! { Self::[< test_benchmark_ $name >]() } - } )* - _ => Err("Could not find test for requested benchmark.".into()), - } - } - } - }; -} - -// 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. -// Instances each component with six values which can be controlled with the -// env variable `VALUES_PER_COMPONENT`. -#[macro_export] -#[doc(hidden)] -macro_rules! impl_benchmark_test { - ( - { $( $where_clause:tt )* } - { $( $instance:ident: $instance_bound:tt )? } - $name:ident - ) => { - $crate::paste::item! { - #[cfg(test)] - impl, $instance: $instance_bound )? > - Pallet - where T: frame_system::Config, $( $where_clause )* - { - #[allow(unused)] - fn [] () -> Result<(), $crate::BenchmarkError> { - let selected_benchmark = SelectedBenchmark::$name; - let components = < - SelectedBenchmark as $crate::BenchmarkingSetup - >::components(&selected_benchmark); - - let execute_benchmark = | - c: $crate::Vec<($crate::BenchmarkParameter, u32)> - | -> Result<(), $crate::BenchmarkError> { - // Always reset the state after the benchmark. - $crate::defer!($crate::benchmarking::wipe_db()); - - // Set up the benchmark, return execution + verification function. - let closure_to_verify = < - SelectedBenchmark as $crate::BenchmarkingSetup - >::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::::block_number()) { - frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Run execution + verification - closure_to_verify() - }; - - if components.is_empty() { - execute_benchmark(Default::default())?; - } else { - let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { - ev.parse().map_err(|_| { - $crate::BenchmarkError::Stop( - "Could not parse env var `VALUES_PER_COMPONENT` as u32." - ) - })? - } else { - 6 - }; - - if num_values < 2 { - return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); - } - - for (name, low, high) in components.clone().into_iter() { - // Test the lowest, highest (if its different from the lowest) - // and up to num_values-2 more equidistant values in between. - // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] - - let mut values = $crate::vec![low]; - let diff = (high - low).min(num_values - 1); - let slope = (high - low) as f32 / diff as f32; - - for i in 1..=diff { - let value = ((low as f32 + slope * i as f32) as u32) - .clamp(low, high); - values.push(value); - } - - for component_value in values { - // Select the max value for all the other components. - let c: $crate::Vec<($crate::BenchmarkParameter, u32)> = components - .iter() - .map(|(n, _, h)| - if *n == name { - (*n, component_value) - } else { - (*n, *h) - } - ) - .collect(); - - execute_benchmark(c)?; - } - } - } - Ok(()) - } - } - } - }; -} - -/// This creates a test suite which runs the module's benchmarks. +/// #![cfg(feature = "runtime-benchmarks")] /// -/// When called in `pallet_example_basic` as +/// use super::{mock_helpers::*, Pallet as MyPallet}; +/// use frame_benchmarking::v2::*; /// -/// ```rust,ignore -/// impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); -/// ``` -/// -/// It expands to the equivalent of: -/// -/// ```rust,ignore -/// #[cfg(test)] -/// mod tests { +/// #[benchmarks] +/// mod benchmarks { /// use super::*; -/// use crate::tests::{new_test_ext, Test}; -/// 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_sort_vector::()); -/// }); -/// } -/// } -/// ``` -/// -/// When called inside the `benchmarks` macro of the `pallet_example_basic` as -/// -/// ```rust,ignore -/// benchmarks! { -/// // Benchmarks omitted for brevity +/// #[benchmark] +/// fn bench_name_1(x: Linear<7, 1_000>, y: Linear<1_000, 100_0000>) { +/// // setup code +/// let z = x + y; +/// let caller = whitelisted_caller(); /// -/// impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); -/// } -/// ``` +/// #[extrinsic_call] +/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments); /// -/// It expands to the equivalent of: -/// -/// ```rust,ignore -/// #[cfg(test)] -/// mod benchmarking { -/// use super::*; -/// use crate::tests::{new_test_ext, Test}; -/// use frame_support::assert_ok; -/// -/// #[test] -/// fn bench_accumulate_dummy() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(test_benchmark_accumulate_dummy::()); -/// }) +/// // verification code +/// assert_eq!(MyPallet::::my_var(), z); /// } /// -/// #[test] -/// fn bench_set_dummy() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(test_benchmark_set_dummy::()); -/// }) -/// } +/// #[benchmark] +/// fn bench_name_2() { +/// // setup code +/// let caller = whitelisted_caller(); /// -/// #[test] -/// fn bench_sort_vector() { -/// new_test_ext().execute_with(|| { -/// assert_ok!(test_benchmark_sort_vector::()); -/// }) +/// #[block] +/// { +/// something(some, thing); +/// my_extrinsic(RawOrigin::Signed(caller), some, argument); +/// something_else(foo, bar); +/// } +/// +/// // verification code +/// assert_eq!(MyPallet::::something(), 37); /// } /// } /// ``` /// -/// ## Arguments +/// ## Benchmark Definitions /// -/// The first argument, `module`, must be the path to this crate's module. +/// Within a `#[benchmarks]` or `#[instance_benchmarks]` module, you can define individual +/// benchmarks using the `#[benchmark]` attribute, as shown in the example above. /// -/// The second argument, `new_test_ext`, must be a function call which returns either a -/// `sp_io::TestExternalities`, or some other type with a similar interface. +/// The `#[benchmark]` attribute expects a function definition with a blank return type and +/// zero or more arguments whose names are valid +/// [BenchmarkParameter](`crate::BenchmarkParameter`) parameters, such as `x`, `y`, `a`, `b`, +/// etc., and whose param types must implement [ParamRange](`v2::ParamRange`). At the moment +/// the only valid type that implements [ParamRange](`v2::ParamRange`) is +/// [Linear](`v2::Linear`). /// -/// Note that this function call is _not_ evaluated at compile time, but is instead copied textually -/// into each appropriate invocation site. +/// The valid syntax for defining a [Linear](`v2::Linear`)is `Linear` where `A`, and `B` +/// are valid integer literals (that fit in a `u32`), such that `B` >= `A`. /// -/// The third argument, `test`, must be the path to the runtime. The item to which this must refer -/// will generally take the form: +/// Note that the benchmark function definition does not actually expand as a function +/// definition, but rather is used to automatically create a number of impls and structs +/// required by the benchmarking engine. For this reason, the visibility of the function +/// definition as well as the return type are not used for any purpose and are discarded by the +/// expansion code. /// -/// ```rust,ignore -/// frame_support::construct_runtime!( -/// pub enum Test where ... -/// { ... } -/// ); -/// ``` +/// Also note that the `// setup code` and `// verification code` comments shown above are not +/// required and are included simply for demonstration purposes. /// -/// There is an optional fourth 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 `benchmarks!` -/// invocation, then you should provide the path to the module containing the `benchmarks!` -/// invocation: +/// ### `#[extrinsic_call]` and `#[block]` /// -/// ```rust,ignore -/// mod benches { -/// benchmarks!{ -/// ... -/// } -/// } +/// Within the benchmark function body, either an `#[extrinsic_call]` or a `#[block]` +/// annotation is required. These attributes should be attached to a block (shown in +/// `bench_name_2` above) or a one-line function call (shown in `bench_name_1` above, in `syn` +/// parlance this should be an `ExprCall`), respectively. /// -/// mod tests { -/// // because of macro syntax limitations, neither Pallet nor benches can be paths, but both have -/// // to be idents in the scope of `impl_benchmark_test_suite`. -/// use crate::{benches, Pallet}; +/// The `#[block]` syntax is broad and will benchmark any code contained within the block the +/// attribute is attached to. If `#[block]` is attached to something other than a block, a +/// compiler error will be emitted. /// -/// impl_benchmark_test_suite!(Pallet, new_test_ext(), Test, benchmarks_path = benches); +/// The one-line `#[extrinsic_call]` syntax must consist of a function call to an extrinsic, +/// where the first argument is the origin. If `#[extrinsic_call]` is attached to an item that +/// doesn't meet these requirements, a compiler error will be emitted. /// -/// // new_test_ext and the Test item are defined later in this module -/// } -/// ``` -/// -/// There is an optional fifth argument, with keyword syntax: `extra = true` or `extra = false`. -/// By default, this generates a test suite which iterates over all benchmarks, including those -/// marked with the `#[extra]` annotation. Setting `extra = false` excludes those. -/// -/// There is an optional sixth argument, with keyword syntax: `exec_name = custom_exec_name`. -/// By default, this macro uses `execute_with` for this parameter. This argument, if set, is subject -/// to these restrictions: -/// -/// - It must be the name of a method applied to the output of the `new_test_ext` argument. -/// - That method must have a signature capable of receiving a single argument of the form `impl -/// FnOnce()`. -// ## Notes (not for rustdoc) -// -// The biggest challenge for this macro is communicating the actual test functions to be run. We -// can't just build an array of function pointers to each test function and iterate over it, because -// the test functions are parameterized by the `Test` type. That's incompatible with -// monomorphization: if it were legal, then even if the compiler detected and monomorphized the -// functions into only the types of the callers, which implementation would the function pointer -// point to? There would need to be some kind of syntax for selecting the destination of the pointer -// according to a generic argument, and in general it would be a huge mess and not worth it. -// -// Instead, we're going to steal a trick from `fn run_benchmark`: generate a function which is -// itself parametrized by `Test`, which accepts a `&[u8]` parameter containing the name of the -// benchmark, and dispatches based on that to the appropriate real test implementation. Then, we can -// just iterate over the `Benchmarking::benchmarks` list to run the actual implementations. -#[macro_export] -macro_rules! impl_benchmark_test_suite { - ( - $bench_module:ident, - $new_test_ext:expr, - $test:path - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - () - () - () - $bench_module, - $new_test_ext, - $test - $(, $( $rest )* )? - ); - } -} - -/// Validates the passed `pov_mode`s. -/// -/// Checks that: -/// - a top-level `ignored` is exclusive -/// - all modes are valid -#[macro_export] -macro_rules! validate_pov_mode { - () => {}; - ( $_bench:ident: ; ) => { }; - ( $_bench:ident: $_car:path = Ignored ; ) => { }; - ( $bench:ident: $_car:path = Ignored $( $storage:path = $_pov_mode:ident )+; ) => { - compile_error!( - concat!(concat!("`pov_mode = Ignored` is exclusive. Please remove the attribute from keys: ", $( stringify!($storage) )+), " on benchmark '", stringify!($bench), "'")); - }; - ( $bench:ident: $car:path = Measured $( $storage:path = $pov_mode:ident )*; ) => { - $crate::validate_pov_mode!( - $bench: $( $storage = $pov_mode )*; - ); - }; - ( $bench:ident: $car:path = MaxEncodedLen $( $storage:path = $pov_mode:ident )*; ) => { - $crate::validate_pov_mode!( - $bench: $( $storage = $pov_mode )*; - ); - }; - ( $bench:ident: $key:path = $unknown:ident $( $_storage:path = $_pov_mode:ident )*; ) => { - compile_error!( - concat!("Unknown pov_mode '", stringify!($unknown) ,"' for benchmark '", stringify!($bench), "' on key '", stringify!($key), "'. Must be one of: Ignored, Measured, MaxEncodedLen") - ); - }; -} - -// Takes all arguments from `impl_benchmark_test_suite` and three additional arguments. -// -// Can be configured to generate one #[test] fn per bench case or -// one #[test] fn for all bench cases. -// This depends on whether or not the first argument contains a non-empty list of bench names. -#[macro_export] -#[doc(hidden)] -macro_rules! impl_test_function { - // user might or might not have set some keyword arguments; set the defaults - // - // The weird syntax indicates that `rest` comes only after a comma, which is otherwise optional - ( - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - - $bench_module:ident, - $new_test_ext:expr, - $test:path - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - @cases: - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - @selected: - $bench_module, - $new_test_ext, - $test, - benchmarks_path = super, - extra = true, - exec_name = execute_with, - @user: - $( $( $rest )* )? - ); - }; - // pick off the benchmarks_path keyword argument - ( - @cases: - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $old:ident, - extra = $extra:expr, - exec_name = $exec_name:ident, - @user: - benchmarks_path = $benchmarks_path:ident - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - @cases: - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - @selected: - $bench_module, - $new_test_ext, - $test, - benchmarks_path = $benchmarks_path, - extra = $extra, - exec_name = $exec_name, - @user: - $( $( $rest )* )? - ); - }; - // pick off the extra keyword argument - ( - @cases: - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $benchmarks_path:ident, - extra = $old:expr, - exec_name = $exec_name:ident, - @user: - extra = $extra:expr - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - @cases: - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - @selected: - $bench_module, - $new_test_ext, - $test, - benchmarks_path = $benchmarks_path, - extra = $extra, - exec_name = $exec_name, - @user: - $( $( $rest )* )? - ); - }; - // pick off the exec_name keyword argument - ( - @cases: - ( $( $names:tt )* ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $benchmarks_path:ident, - extra = $extra:expr, - exec_name = $old:ident, - @user: - exec_name = $exec_name:ident - $(, $( $rest:tt )* )? - ) => { - $crate::impl_test_function!( - @cases: - ( $( $names )* ) - ( $( $names_extra )* ) - ( $( $names_skip_meta )* ) - @selected: - $bench_module, - $new_test_ext, - $test, - benchmarks_path = $benchmarks_path, - extra = $extra, - exec_name = $exec_name, - @user: - $( $( $rest )* )? - ); - }; - // iteration-exit arm which generates a #[test] function for each case. - ( - @cases: - ( $( $names:tt )+ ) - ( $( $names_extra:tt )* ) - ( $( $names_skip_meta:tt )* ) - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $path_to_benchmarks_invocation:ident, - extra = $extra:expr, - exec_name = $exec_name:ident, - @user: - $(,)? - ) => { - $crate::impl_bench_case_tests!( - { $bench_module, $new_test_ext, $exec_name, $test, $extra } - { $( $names_extra:tt )* } - $($names)+ - ); - }; - // iteration-exit arm which generates one #[test] function for all cases. - ( - @cases: - () - () - () - @selected: - $bench_module:ident, - $new_test_ext:expr, - $test:path, - benchmarks_path = $path_to_benchmarks_invocation:ident, - extra = $extra:expr, - exec_name = $exec_name:ident, - @user: - $(,)? - ) => { - #[cfg(test)] - mod benchmark_tests { - use super::$bench_module; - - #[test] - fn test_benchmarks() { - $new_test_ext.$exec_name(|| { - use $crate::Benchmarking; - - let mut anything_failed = false; - println!("failing benchmark tests:"); - for benchmark_metadata in $bench_module::<$test>::benchmarks($extra) { - let benchmark_name = &benchmark_metadata.name; - match std::panic::catch_unwind(|| { - $bench_module::<$test>::test_bench_by_name(benchmark_name) - }) { - Err(err) => { - println!( - "{}: {:?}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - err, - ); - anything_failed = true; - }, - Ok(Err(err)) => { - match err { - $crate::BenchmarkError::Stop(err) => { - println!( - "{}: {:?}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - err, - ); - anything_failed = true; - }, - $crate::BenchmarkError::Override(_) => { - // This is still considered a success condition. - $crate::log::error!( - "WARNING: benchmark error overrided - {}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - ); - }, - $crate::BenchmarkError::Skip => { - // This is considered a success condition. - $crate::log::error!( - "WARNING: benchmark error skipped - {}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - ); - } - $crate::BenchmarkError::Weightless => { - // This is considered a success condition. - $crate::log::error!( - "WARNING: benchmark weightless skipped - {}", - $crate::str::from_utf8(benchmark_name) - .expect("benchmark name is always a valid string!"), - ); - } - } - }, - Ok(Ok(())) => (), - } - } - assert!(!anything_failed); - }); - } - } - }; -} - -/// show error message and debugging info for the case of an error happening -/// during a benchmark -pub fn show_benchmark_debug_info( - instance_string: &[u8], - benchmark: &[u8], - components: &[(BenchmarkParameter, u32)], - verify: &bool, - error_message: &str, -) -> sp_runtime::RuntimeString { - sp_runtime::format_runtime_string!( - "\n* Pallet: {}\n\ - * Benchmark: {}\n\ - * Components: {:?}\n\ - * Verify: {:?}\n\ - * Error message: {}", - sp_std::str::from_utf8(instance_string) - .expect("it's all just strings ran through the wasm interface. qed"), - sp_std::str::from_utf8(benchmark) - .expect("it's all just strings ran through the wasm interface. qed"), - components, - verify, - error_message, - ) -} - -/// This macro adds pallet benchmarks to a `Vec` object. -/// -/// First create an object that holds in the input parameters for the benchmark: +/// As a short-hand, you may substitute the name of the extrinsic call with `_`, such as the +/// following: /// /// ```ignore -/// let params = (&config, &whitelist); +/// #[extrinsic_call] +/// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); /// ``` /// -/// The `whitelist` is a parameter you pass to control the DB read/write tracking. -/// We use a vector of [TrackedStorageKey](./struct.TrackedStorageKey.html), which is a simple -/// struct used to set if a key has been read or written to. +/// The underscore will be substituted with the name of the benchmark (i.e. the name of the +/// function in the benchmark function definition). /// -/// For values that should be skipped entirely, we can just pass `key.into()`. For example: +/// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves +/// the purpose of designating the boundary between the setup code portion of the benchmark +/// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification +/// stage (everything after the item that the `#[extrinsic_call]` or `#[block]` attribute is +/// attached to). The setup code section should contain any code that needs to execute before +/// the measured portion of the benchmark executes. The verification section is where you can +/// perform assertions to verify that the extrinsic call (or whatever is happening in your +/// block, if you used the `#[block]` syntax) executed successfully. /// -/// ``` -/// use frame_benchmarking::TrackedStorageKey; -/// let whitelist: Vec = vec![ -/// // Block Number -/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"), -/// // Total Issuance -/// array_bytes::hex_into_unchecked("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80"), -/// // Execution Phase -/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a"), -/// // Event Count -/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850"), -/// ]; -/// ``` +/// Note that neither `#[extrinsic_call]` nor `#[block]` are real attribute macros and are +/// instead consumed by the outer macro pattern as part of the enclosing benchmark function +/// definition. This is why we are able to use `#[extrinsic_call]` and `#[block]` within a +/// function definition even though this behavior has not been stabilized +/// yet—`#[extrinsic_call]` and `#[block]` are parsed and consumed as part of the benchmark +/// definition parsing code, so they never expand as their own attribute macros. /// -/// Then define a mutable local variable to hold your `BenchmarkBatch` object: +/// ### Optional Attributes /// -/// ```ignore -/// let mut batches = Vec::::new(); -/// ```` +/// The keywords `extra` and `skip_meta` can be provided as optional arguments to the +/// `#[benchmark]` attribute, i.e. `#[benchmark(extra, skip_meta)]`. Including either of these +/// will enable the `extra` or `skip_meta` option, respectively. These options enable the same +/// behavior they did in the old benchmarking syntax in `frame_benchmarking`, namely: /// -/// Then add the pallets you want to benchmark to this object, using their crate name and generated -/// module struct: +/// #### `extra` /// -/// ```ignore -/// add_benchmark!(params, batches, pallet_balances, Balances); -/// add_benchmark!(params, batches, pallet_session, SessionBench::); -/// add_benchmark!(params, batches, frame_system, SystemBench::); -/// ... -/// ``` +/// Specifies that this benchmark should not normally run. To run benchmarks marked with +/// `extra`, you will need to invoke the `frame-benchmarking-cli` with `--extra`. /// -/// At the end of `dispatch_benchmark`, you should return this batches object. +/// #### `skip_meta` /// -/// In the case where you have multiple instances of a pallet that you need to separately benchmark, -/// the name of your module struct will be used as a suffix to your outputted weight file. For -/// example: +/// Specifies that the benchmarking framework should not analyze the storage keys that +/// benchmarked code read or wrote. This useful to suppress the prints in the form of unknown +/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis +/// of all accesses, not just ones without metadata. /// -/// ```ignore -/// add_benchmark!(params, batches, pallet_balances, Balances); // pallet_balances.rs -/// add_benchmark!(params, batches, pallet_collective, Council); // pallet_collective_council.rs -/// add_benchmark!(params, batches, pallet_collective, TechnicalCommittee); // pallet_collective_technical_committee.rs -/// ``` +/// ## Where Clause /// -/// You can manipulate this suffixed string by using a type alias if needed. For example: +/// Some pallets require a where clause specifying constraints on their generics to make +/// writing benchmarks feasible. To accomodate this situation, you can provide such a where +/// clause as the (only) argument to the `#[benchmarks]` or `#[instance_benchmarks]` attribute +/// macros. Below is an example of this taken from the `message-queue` pallet. /// /// ```ignore -/// type Council2 = TechnicalCommittee; -/// add_benchmark!(params, batches, pallet_collective, Council2); // pallet_collective_council_2.rs +/// #[benchmarks( +/// where +/// <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, +/// ::Size: From, +/// )] +/// mod benchmarks { +/// use super::*; +/// // ... +/// } /// ``` -#[macro_export] -macro_rules! add_benchmark { - ( $params:ident, $batches:ident, $name:path, $( $location:tt )* ) => ( - let name_string = stringify!($name).as_bytes(); - let instance_string = stringify!( $( $location )* ).as_bytes(); - let (config, whitelist) = $params; - let $crate::BenchmarkConfig { - pallet, - benchmark, - selected_components, - verify, - internal_repeats, - } = config; - if &pallet[..] == &name_string[..] { - let benchmark_result = $( $location )*::run_benchmark( - &benchmark[..], - &selected_components[..], - whitelist, - *verify, - *internal_repeats, - ); - - let final_results = match benchmark_result { - Ok(results) => Some(results), - Err($crate::BenchmarkError::Override(mut result)) => { - // Insert override warning as the first storage key. - $crate::log::error!( - "WARNING: benchmark error overrided - {}", - $crate::str::from_utf8(benchmark) - .expect("benchmark name is always a valid string!") - ); - result.keys.insert(0, - (b"Benchmark Override".to_vec(), 0, 0, false) - ); - Some($crate::vec![result]) - }, - Err($crate::BenchmarkError::Stop(e)) => { - $crate::show_benchmark_debug_info( - instance_string, - benchmark, - selected_components, - verify, - e, - ); - return Err(e.into()); - }, - Err($crate::BenchmarkError::Skip) => { - $crate::log::error!( - "WARNING: benchmark error skipped - {}", - $crate::str::from_utf8(benchmark) - .expect("benchmark name is always a valid string!") - ); - None - }, - Err($crate::BenchmarkError::Weightless) => { - $crate::log::error!( - "WARNING: benchmark weightless skipped - {}", - $crate::str::from_utf8(benchmark) - .expect("benchmark name is always a valid string!") - ); - Some(vec![$crate::BenchmarkResult { - components: selected_components.clone(), - .. Default::default() - }]) - } - }; - - if let Some(final_results) = final_results { - $batches.push($crate::BenchmarkBatch { - pallet: name_string.to_vec(), - instance: instance_string.to_vec(), - benchmark: benchmark.clone(), - results: final_results, - }); - } - } - ) -} - -/// Callback for `define_benchmarks` to call `add_benchmark`. -#[macro_export] -macro_rules! cb_add_benchmarks { - // anchor - ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] ) => { - $crate::add_benchmark!( $params, $batches, $name, $( $location )* ); - }; - // recursion tail - ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { - $crate::cb_add_benchmarks!( $params, $batches, [ $name, $( $location )* ] ); - $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); - } -} - -/// This macro allows users to easily generate a list of benchmarks for the pallets configured -/// in the runtime. -/// -/// To use this macro, first create a an object to store the list: /// -/// ```ignore -/// let mut list = Vec::::new(); -/// ``` +/// ## Benchmark Tests /// -/// Then pass this `list` to the macro, along with the `extra` boolean, the pallet crate, and -/// pallet struct: +/// Benchmark tests can be generated using the old syntax in `frame_benchmarking`, +/// including the `frame_benchmarking::impl_benchmark_test_suite` macro. /// +/// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module): /// ```ignore -/// list_benchmark!(list, extra, pallet_balances, Balances); -/// list_benchmark!(list, extra, pallet_session, SessionBench::); -/// list_benchmark!(list, extra, frame_system, SystemBench::); +/// #[benchmarks] +/// mod benchmarks { +/// use super::*; +/// // ... +/// impl_benchmark_test_suite!( +/// MessageQueue, +/// crate::mock::new_test_ext::(), +/// crate::integration_test::Test +/// ); +/// } /// ``` -/// -/// This should match what exists with the `add_benchmark!` macro. -#[macro_export] -macro_rules! list_benchmark { - ( $list:ident, $extra:ident, $name:path, $( $location:tt )* ) => ( - let pallet_string = stringify!($name).as_bytes(); - let instance_string = stringify!( $( $location )* ).as_bytes(); - let benchmarks = $( $location )*::benchmarks($extra); - let pallet_benchmarks = BenchmarkList { - pallet: pallet_string.to_vec(), - instance: instance_string.to_vec(), - benchmarks: benchmarks.to_vec(), - }; - $list.push(pallet_benchmarks) - ) -} - -/// Callback for `define_benchmarks` to call `list_benchmark`. -#[macro_export] -macro_rules! cb_list_benchmarks { - // anchor - ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] ) => { - $crate::list_benchmark!( $list, $extra, $name, $( $location )* ); - }; - // recursion tail - ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { - $crate::cb_list_benchmarks!( $list, $extra, [ $name, $( $location )* ] ); - $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); +pub mod v2 { + pub use super::*; + pub use frame_support_procedural::{ + benchmark, benchmarks, block, extrinsic_call, instance_benchmarks, + }; + + // Used in #[benchmark] implementation to ensure that benchmark function arguments + // implement [`ParamRange`]. + #[doc(hidden)] + pub use static_assertions::assert_impl_all; + + /// Used by the new benchmarking code to specify that a benchmarking variable is linear + /// over some specified range, i.e. `Linear<0, 1_000>` means that the corresponding variable + /// is allowed to range from `0` to `1000`, inclusive. + /// + /// See [`v2`] for more info. + pub struct Linear; + + /// Trait that must be implemented by all structs that can be used as parameter range types + /// in the new benchmarking code (i.e. `Linear<0, 1_000>`). Right now there is just + /// [`Linear`] but this could later be extended to support additional non-linear parameter + /// ranges. + /// + /// See [`v2`] for more info. + pub trait ParamRange { + /// Represents the (inclusive) starting number of this `ParamRange`. + fn start(&self) -> u32; + + /// Represents the (inclusive) ending number of this `ParamRange`. + fn end(&self) -> u32; } -} -/// Defines pallet configs that `add_benchmarks` and `list_benchmarks` use. -/// Should be preferred instead of having a repetitive list of configs -/// in `add_benchmark` and `list_benchmark`. -#[macro_export] -macro_rules! define_benchmarks { - ( $([ $names:path, $( $locations:tt )* ])* ) => { - /// Calls `list_benchmark` with all configs from `define_benchmarks` - /// and passes the first two parameters on. - /// - /// Use as: - /// ```ignore - /// list_benchmarks!(list, extra); - /// ``` - #[macro_export] - macro_rules! list_benchmarks { - ( $list:ident, $extra:ident ) => { - $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); - } + impl ParamRange for Linear { + fn start(&self) -> u32 { + A } - /// Calls `add_benchmark` with all configs from `define_benchmarks` - /// and passes the first two parameters on. - /// - /// Use as: - /// ```ignore - /// add_benchmarks!(params, batches); - /// ``` - #[macro_export] - macro_rules! add_benchmarks { - ( $params:ident, $batches:ident ) => { - $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); - } + fn end(&self) -> u32 { + B } } } diff --git a/frame/benchmarking/src/v1.rs b/frame/benchmarking/src/v1.rs new file mode 100644 index 0000000000000..089083f85b982 --- /dev/null +++ b/frame/benchmarking/src/v1.rs @@ -0,0 +1,2043 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Macros for benchmarking a FRAME runtime. + +pub use super::*; + +/// Whitelist the given account. +#[macro_export] +macro_rules! whitelist { + ($acc:ident) => { + frame_benchmarking::benchmarking::add_to_whitelist( + frame_system::Account::::hashed_key_for(&$acc).into(), + ); + }; +} + +/// 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 parenthesized. +/// +/// 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 expressions 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 +/// benchmarks! { +/// where_clause { where T::A: From } // Optional line to give additional bound on `T`. +/// +/// // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of +/// // size `l` +/// foo { +/// let caller = account::(b"caller", 0, benchmarks_seed); +/// let l in 1 .. MAX_LENGTH => initialize_l(l); +/// }: _(RuntimeOrigin::Signed(caller), vec![0u8; l]) +/// +/// // second dispatchable: bar; this is a root dispatchable and accepts a `u8` vector of size +/// // `l`. +/// // In this case, we explicitly name the call using `bar` instead of `_`. +/// bar { +/// let l in 1 .. MAX_LENGTH => initialize_l(l); +/// }: bar(RuntimeOrigin::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 +/// // of arms. +/// baz1 { +/// let caller = account::(b"caller", 0, benchmarks_seed); +/// let c = 0 .. 10 => setup_c(&caller, c); +/// }: baz(RuntimeOrigin::Signed(caller)) +/// +/// // this is a second benchmark of the baz dispatchable with a different setup. +/// baz2 { +/// let caller = account::(b"caller", 0, benchmarks_seed); +/// let c = 0 .. 10 => setup_c_in_some_other_way(&caller, c); +/// }: baz(RuntimeOrigin::Signed(caller)) +/// +/// // You may optionally specify the origin type if it can't be determined automatically like +/// // this. +/// baz3 { +/// let caller = account::(b"caller", 0, benchmarks_seed); +/// let l in 1 .. MAX_LENGTH => initialize_l(l); +/// }: baz(RuntimeOrigin::Signed(caller), vec![0u8; l]) +/// +/// // this is benchmarking some code that is not a dispatchable. +/// populate_a_set { +/// let x in 0 .. 10_000; +/// let mut m = Vec::::new(); +/// for i in 0..x { +/// m.insert(i); +/// } +/// }: { m.into_iter().collect::() } +/// } +/// ``` +/// +/// Test functions are automatically generated for each benchmark and are accessible to you when you +/// run `cargo test`. All tests are named `test_benchmark_`, implemented on the +/// Pallet struct, 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. +/// +/// It is also possible to generate one #[test] function per benchmark by calling the +/// `impl_benchmark_test_suite` macro inside the `benchmarks` block. The functions will be named +/// `bench_` and can be run via `cargo test`. +/// You will see one line of output per benchmark. This approach will give you more understandable +/// error messages and allows for parallel benchmark execution. +/// +/// 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 { +/// let x in 1 .. 10000; +/// let mut m = Vec::::new(); +/// for i in (0..x).rev() { +/// m.push(i); +/// } +/// }: { +/// m.sort(); +/// } verify { +/// ensure!(m[0] == 0, "You forgot to sort!") +/// } +/// ``` +/// +/// These `verify` blocks will not affect your benchmark results! +/// +/// You can construct benchmark by using the `impl_benchmark_test_suite` macro or +/// by manually implementing them like so: +/// +/// ```ignore +/// #[test] +/// fn test_benchmarks() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(Pallet::::test_benchmark_dummy()); +/// assert_err!(Pallet::::test_benchmark_other_name(), "Bad origin"); +/// assert_ok!(Pallet::::test_benchmark_sort_vector()); +/// assert_err!(Pallet::::test_benchmark_broken_benchmark(), "You forgot to sort!"); +/// }); +/// } +/// ``` +#[macro_export] +macro_rules! benchmarks { + ( + $( $rest:tt )* + ) => { + $crate::benchmarks_iter!( + { } + { } + { } + ( ) + ( ) + ( ) + ( ) + $( $rest )* + ); + } +} + +/// Same as [`benchmarks`] but for instantiable module. +/// +/// NOTE: For pallet declared with [`frame_support::pallet`], use [`benchmarks_instance_pallet`]. +#[macro_export] +macro_rules! benchmarks_instance { + ( + $( $rest:tt )* + ) => { + $crate::benchmarks_iter!( + { } + { I: Instance } + { } + ( ) + ( ) + ( ) + ( ) + $( $rest )* + ); + } +} + +/// Same as [`benchmarks`] but for instantiable pallet declared [`frame_support::pallet`]. +/// +/// NOTE: For pallet declared with `decl_module!`, use [`benchmarks_instance`]. +#[macro_export] +macro_rules! benchmarks_instance_pallet { + ( + $( $rest:tt )* + ) => { + $crate::benchmarks_iter!( + { } + { I: 'static } + { } + ( ) + ( ) + ( ) + ( ) + $( $rest )* + ); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! benchmarks_iter { + // detect and extract `impl_benchmark_test_suite` call: + // - with a semi-colon + ( + { } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + impl_benchmark_test_suite!( + $bench_module:ident, + $new_test_ext:expr, + $test:path + $(, $( $args:tt )* )?); + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $bench_module, $new_test_ext, $test $(, $( $args )* )? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( $rest )* + } + }; + // - without a semicolon + ( + { } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + impl_benchmark_test_suite!( + $bench_module:ident, + $new_test_ext:expr, + $test:path + $(, $( $args:tt )* )?) + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $bench_module, $new_test_ext, $test $(, $( $args )* )? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( $rest )* + } + }; + // detect and extract where clause: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + where_clause { where $( $where_bound:tt )* } + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound)? } + { $( $where_bound )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( $rest )* + } + }; + // detect and extract `#[skip_meta]` tag: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + #[skip_meta] + $( #[ $($attributes:tt)+ ] )* + $name:ident + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* $name ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* + $name + $( $rest )* + } + }; + // detect and extract `#[extra]` tag: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + #[extra] + $( #[ $($attributes:tt)+ ] )* + $name:ident + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* $name ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* + $name + $( $rest )* + } + }; + // detect and extract `#[pov_mode = Mode { Pallet::Storage: Mode ... }]` tag: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $old_pov_name:ident: $( $old_storage:path = $old_pov_mode:ident )*; )* ) + #[pov_mode = $mode:ident $( { $( $storage:path: $pov_mode:ident )* } )?] + $( #[ $($attributes:tt)+ ] )* + $name:ident + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $name: ALL = $mode $($( $storage = $pov_mode )*)?; $( $old_pov_name: $( $old_storage = $old_pov_mode )*; )* ) + $( #[ $( $attributes )+ ] )* + $name + $( $rest )* + } + }; + // mutation arm: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) // This contains $( $( { $instance } )? $name:ident )* + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: _ $(< $origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) + verify $postcode:block + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { $( $code )* }: $name $(< $origin_type >)? ( $origin $( , $arg )* ) + verify $postcode + $( $rest )* + } + }; + // mutation arm: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) + verify $postcode:block + $( $rest:tt )* + ) => { + $crate::paste::paste! { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { + $( $code )* + let __call = Call::< + T + $( , $instance )? + >:: [< new_call_variant_ $dispatch >] ( + $($arg),* + ); + let __benchmarked_call_encoded = $crate::frame_support::codec::Encode::encode( + &__call + ); + }: { + let __call_decoded = < + Call + as $crate::frame_support::codec::Decode + >::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = $crate::to_origin!($origin $(, $origin_type)?); + as $crate::frame_support::traits::UnfilteredDispatchable + >::dispatch_bypass_filter(__call_decoded, __origin)?; + } + verify $postcode + $( $rest )* + } + } + }; + // iteration arm: + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: $eval:block + verify $postcode:block + $( $rest:tt )* + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { } + { $eval } + { $( $code )* } + $postcode + } + + #[cfg(test)] + $crate::impl_benchmark_test!( + { $( $where_clause )* } + { $( $instance: $instance_bound )? } + $name + ); + + $crate::benchmarks_iter!( + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* { $( $instance )? } $name ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $( $rest )* + ); + }; + // iteration-exit arm which generates a #[test] function for each case. + ( + { $bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + ) => { + $crate::selected_benchmark!( + { $( $where_clause)* } + { $( $instance: $instance_bound )? } + $( $names )* + ); + $crate::impl_benchmark!( + { $( $where_clause )* } + { $( $instance: $instance_bound )? } + ( $( $names )* ) + ( $( $names_extra ),* ) + ( $( $names_skip_meta ),* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + ); + $crate::impl_test_function!( + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + $bench_module, + $new_test_ext, + $test + $(, $( $args )* )? + ); + }; + // iteration-exit arm which doesn't generate a #[test] function for all cases. + ( + { } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + ) => { + $crate::selected_benchmark!( + { $( $where_clause)* } + { $( $instance: $instance_bound )? } + $( $names )* + ); + $crate::impl_benchmark!( + { $( $where_clause )* } + { $( $instance: $instance_bound )? } + ( $( $names )* ) + ( $( $names_extra ),* ) + ( $( $names_skip_meta ),* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + ); + }; + // add verify block to _() format + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: _ $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { $( $code )* }: _ $(<$origin_type>)? ( $origin $( , $arg )* ) + verify { } + $( $rest )* + } + }; + // add verify block to name() format + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: $dispatch:ident $(<$origin_type:ty>)? ( $origin:expr $( , $arg:expr )* ) + $( $rest:tt )* + ) => { + $crate::benchmarks_iter! { + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { $( $code )* }: $dispatch $(<$origin_type>)? ( $origin $( , $arg )* ) + verify { } + $( $rest )* + } + }; + // add verify block to {} format + ( + { $($bench_module:ident, $new_test_ext:expr, $test:path $(, $( $args:tt )* )?)? } + { $( $instance:ident: $instance_bound:tt )? } + { $( $where_clause:tt )* } + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + $name:ident { $( $code:tt )* }: $(<$origin_type:ty>)? $eval:block + $( $rest:tt )* + ) => { + $crate::benchmarks_iter!( + { $($bench_module, $new_test_ext, $test $(, $( $args )* )?)? } + { $( $instance: $instance_bound )? } + { $( $where_clause )* } + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + ( $( $pov_name: $( $storage = $pov_mode )*; )* ) + $name { $( $code )* }: $(<$origin_type>)? $eval + verify { } + $( $rest )* + ); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! to_origin { + ($origin:expr) => { + $origin.into() + }; + ($origin:expr, $origin_type:ty) => { + <::RuntimeOrigin as From<$origin_type>>::from($origin) + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! benchmark_backend { + // parsing arms + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( PRE { $( $pre_parsed:tt )* } )* } + { $eval:block } + { + let $pre_id:tt : $pre_ty:ty = $pre_ex:expr; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { + $( PRE { $( $pre_parsed )* } )* + PRE { $pre_id , $pre_ty , $pre_ex } + } + { $eval } + { $( $rest )* } + $postcode + } + }; + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( $parsed:tt )* } + { $eval:block } + { + let $param:ident in ( $param_from:expr ) .. $param_to:expr => $param_instancer:expr; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { + $( $parsed )* + PARAM { $param , $param_from , $param_to , $param_instancer } + } + { $eval } + { $( $rest )* } + $postcode + } + }; + // mutation arm to look after a single tt for param_from. + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( $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: $instance_bound )? } + $name + { $( $where_clause )* } + { $( $parsed )* } + { $eval } + { + let $param in ( $param_from ) .. $param_to => $param_instancer; + $( $rest )* + } + $postcode + } + }; + // mutation arm to look after the default tail of `=> ()` + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( $parsed:tt )* } + { $eval:block } + { + let $param:ident in $param_from:tt .. $param_to:expr; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { $( $parsed )* } + { $eval } + { + let $param in $param_from .. $param_to => (); + $( $rest )* + } + $postcode + } + }; + // mutation arm to look after `let _ =` + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { $( $parsed:tt )* } + { $eval:block } + { + let $pre_id:tt = $pre_ex:expr; + $( $rest:tt )* + } + $postcode:block + ) => { + $crate::benchmark_backend! { + { $( $instance: $instance_bound )? } + $name + { $( $where_clause )* } + { $( $parsed )* } + { $eval } + { + let $pre_id : _ = $pre_ex; + $( $rest )* + } + $postcode + } + }; + // actioning arm + ( + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + { $( $where_clause:tt )* } + { + $( 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, $instance: $instance_bound )? > + $crate::BenchmarkingSetup for $name + where $( $where_clause )* + { + 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 Result<(), $crate::BenchmarkError>>, $crate::BenchmarkError> { + $( + // 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<(), $crate::BenchmarkError> { + $eval; + if verify { + $postcode; + } + Ok(()) + })) + } + } + }; +} + +// Creates #[test] functions for the given bench cases. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_bench_case_tests { + ( + { $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr } + { $( $names_extra:tt )* } + $( { $( $bench_inst:ident )? } $bench:ident )* + ) + => { + $crate::impl_bench_name_tests!( + $module, $new_test_exec, $exec_name, $test, $extra, + { $( $names_extra )* }, + $( { $bench } )+ + ); + } +} + +// Creates a #[test] function for the given bench name. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_bench_name_tests { + // recursion anchor + ( + $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr, + { $( $names_extra:tt )* }, + { $name:ident } + ) => { + $crate::paste::paste! { + #[test] + fn [] () { + $new_test_exec.$exec_name(|| { + // Skip all #[extra] benchmarks if $extra is false. + if !($extra) { + let disabled = $crate::vec![ $( stringify!($names_extra).as_ref() ),* ]; + if disabled.contains(&stringify!($name)) { + $crate::log::error!( + "INFO: extra benchmark skipped - {}", + stringify!($name), + ); + return (); + } + } + + // Same per-case logic as when all cases are run in the + // same function. + match std::panic::catch_unwind(|| { + $module::<$test>::[< test_benchmark_ $name >] () + }) { + Err(err) => { + panic!("{}: {:?}", stringify!($name), err); + }, + Ok(Err(err)) => { + match err { + $crate::BenchmarkError::Stop(err) => { + panic!("{}: {:?}", stringify!($name), err); + }, + $crate::BenchmarkError::Override(_) => { + // This is still considered a success condition. + $crate::log::error!( + "WARNING: benchmark error overrided - {}", + stringify!($name), + ); + }, + $crate::BenchmarkError::Skip => { + // This is considered a success condition. + $crate::log::error!( + "WARNING: benchmark error skipped - {}", + stringify!($name), + ); + }, + $crate::BenchmarkError::Weightless => { + // This is considered a success condition. + $crate::log::error!( + "WARNING: benchmark weightless skipped - {}", + stringify!($name), + ); + } + } + }, + Ok(Ok(())) => (), + } + }); + } + } + }; + // recursion tail + ( + $module:ident, $new_test_exec:expr, $exec_name:ident, $test:path, $extra:expr, + { $( $names_extra:tt )* }, + { $name:ident } $( { $rest:ident } )+ + ) => { + // car + $crate::impl_bench_name_tests!($module, $new_test_exec, $exec_name, $test, $extra, + { $( $names_extra )* }, { $name }); + // cdr + $crate::impl_bench_name_tests!($module, $new_test_exec, $exec_name, $test, $extra, + { $( $names_extra )* }, $( { $rest } )+); + }; +} + +// 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 { + ( + { $( $where_clause:tt )* } + { $( $instance:ident: $instance_bound:tt )? } + $( { $( $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, $instance: $instance_bound )? > + $crate::BenchmarkingSetup for SelectedBenchmark + where $( $where_clause )* + { + fn components(&self) -> $crate::Vec<($crate::BenchmarkParameter, u32, u32)> { + match self { + $( + Self::$bench => < + $bench as $crate::BenchmarkingSetup + >::components(&$bench), + )* + } + } + + fn instance( + &self, + components: &[($crate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<$crate::Box Result<(), $crate::BenchmarkError>>, $crate::BenchmarkError> { + match self { + $( + Self::$bench => < + $bench as $crate::BenchmarkingSetup + >::instance(&$bench, components, verify), + )* + } + } + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! impl_benchmark { + ( + { $( $where_clause:tt )* } + { $( $instance:ident: $instance_bound:tt )? } + ( $( { $( $name_inst:ident )? } $name:ident )* ) + ( $( $name_extra:ident ),* ) + ( $( $name_skip_meta:ident ),* ) + ( $( $pov_name:ident: $( $storage:path = $pov_mode:ident )*; )* ) + ) => { + // We only need to implement benchmarks for the runtime-benchmarks feature or testing. + #[cfg(any(feature = "runtime-benchmarks", test))] + impl, $instance: $instance_bound )? > + $crate::Benchmarking for Pallet + where T: frame_system::Config, $( $where_clause )* + { + fn benchmarks(extra: bool) -> $crate::Vec<$crate::BenchmarkMetadata> { + $($crate::validate_pov_mode!( + $pov_name: $( $storage = $pov_mode )*; + );)* + let mut all_names = $crate::vec![ $( stringify!($name).as_ref() ),* ]; + if !extra { + let extra = [ $( stringify!($name_extra).as_ref() ),* ]; + all_names.retain(|x| !extra.contains(x)); + } + let pov_modes: $crate::Vec<($crate::Vec, $crate::Vec<($crate::Vec, $crate::Vec)>)> = $crate::vec![ + $( + (stringify!($pov_name).as_bytes().to_vec(), + $crate::vec![ + $( ( stringify!($storage).as_bytes().to_vec(), + stringify!($pov_mode).as_bytes().to_vec() ), )* + ]), + )* + ]; + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + $( stringify!($name) => SelectedBenchmark::$name, )* + _ => panic!("all benchmarks should be selectable"), + }; + let name = benchmark.as_bytes().to_vec(); + let components = < + SelectedBenchmark as $crate::BenchmarkingSetup + >::components(&selected_benchmark); + + $crate::BenchmarkMetadata { + name: name.clone(), + components, + pov_modes: pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()).unwrap_or_default(), + } + }).collect::<$crate::Vec<_>>() + } + + fn run_benchmark( + extrinsic: &[u8], + c: &[($crate::BenchmarkParameter, u32)], + whitelist: &[$crate::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<$crate::Vec<$crate::BenchmarkResult>, $crate::BenchmarkError> { + // Map the input to the selected benchmark. + let extrinsic = $crate::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.".into()), + }; + + // Add whitelist to DB including whitelisted caller + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = + as $crate::frame_support::storage::StorageMap<_,_>>::hashed_key_for( + $crate::whitelisted_caller::() + ); + whitelist.push(whitelisted_caller_key.into()); + // Whitelist the transactional layer. + let transactional_layer_key = $crate::TrackedStorageKey::new( + $crate::frame_support::storage::transactional::TRANSACTION_LEVEL_KEY.into() + ); + whitelist.push(transactional_layer_key); + // Whitelist the `:extrinsic_index`. + let extrinsic_index = $crate::TrackedStorageKey::new( + $crate::well_known_keys::EXTRINSIC_INDEX.into() + ); + whitelist.push(extrinsic_index); + + $crate::benchmarking::set_whitelist(whitelist.clone()); + + let mut results: $crate::Vec<$crate::BenchmarkResult> = $crate::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + $crate::defer!($crate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as $crate::BenchmarkingSetup + >::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::::block_number()) { + frame_system::Pallet::::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(); + + // Access all whitelisted keys to get them into the proof recorder since the + // recorder does now have a whitelist. + for key in &whitelist { + $crate::frame_support::storage::unhashed::get_raw(&key.key); + } + + // Reset the read/write counter so we don't count operations in the setup process. + $crate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + $crate::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?}) verify {}", + extrinsic, + c, + verify + ); + + 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::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = $crate::benchmarking::read_write_count(); + $crate::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + $crate::log::trace!( + target: "benchmark", + "Proof sizes: before {:?} after {:?} diff {}", &start_pov, &end_pov, &diff_pov + ); + + // Time the storage root recalculation. + let start_storage_root = $crate::benchmarking::current_time(); + $crate::storage_root($crate::StateVersion::V1); + let finish_storage_root = $crate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ $( stringify!($name_skip_meta).as_ref() ),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + $crate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + $crate::benchmarking::get_read_and_written_keys() + }; + + results.push($crate::BenchmarkResult { + 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, + keys: read_and_written_keys, + }); + } + + return Ok(results); + } + } + + #[cfg(test)] + impl, $instance: $instance_bound )? > + Pallet + where T: frame_system::Config, $( $where_clause )* + { + /// 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. + #[allow(unused)] + fn test_bench_by_name(name: &[u8]) -> Result<(), $crate::BenchmarkError> { + let name = $crate::str::from_utf8(name) + .map_err(|_| -> $crate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + $( stringify!($name) => { + $crate::paste::paste! { Self::[< test_benchmark_ $name >]() } + } )* + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } + }; +} + +// 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. +// Instances each component with six values which can be controlled with the +// env variable `VALUES_PER_COMPONENT`. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_benchmark_test { + ( + { $( $where_clause:tt )* } + { $( $instance:ident: $instance_bound:tt )? } + $name:ident + ) => { + $crate::paste::item! { + #[cfg(test)] + impl, $instance: $instance_bound )? > + Pallet + where T: frame_system::Config, $( $where_clause )* + { + #[allow(unused)] + fn [] () -> Result<(), $crate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::$name; + let components = < + SelectedBenchmark as $crate::BenchmarkingSetup + >::components(&selected_benchmark); + + let execute_benchmark = | + c: $crate::Vec<($crate::BenchmarkParameter, u32)> + | -> Result<(), $crate::BenchmarkError> { + // Always reset the state after the benchmark. + $crate::defer!($crate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as $crate::BenchmarkingSetup + >::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::::block_number()) { + frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Run execution + verification + closure_to_verify() + }; + + if components.is_empty() { + execute_benchmark(Default::default())?; + } else { + let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { + ev.parse().map_err(|_| { + $crate::BenchmarkError::Stop( + "Could not parse env var `VALUES_PER_COMPONENT` as u32." + ) + })? + } else { + 6 + }; + + if num_values < 2 { + return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); + } + + for (name, low, high) in components.clone().into_iter() { + // Test the lowest, highest (if its different from the lowest) + // and up to num_values-2 more equidistant values in between. + // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + + let mut values = $crate::vec![low]; + let diff = (high - low).min(num_values - 1); + let slope = (high - low) as f32 / diff as f32; + + for i in 1..=diff { + let value = ((low as f32 + slope * i as f32) as u32) + .clamp(low, high); + values.push(value); + } + + for component_value in values { + // Select the max value for all the other components. + let c: $crate::Vec<($crate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + Ok(()) + } + } + } + }; +} + +/// This creates a test suite which runs the module's benchmarks. +/// +/// When called in `pallet_example_basic` as +/// +/// ```rust,ignore +/// impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); +/// ``` +/// +/// It expands to the equivalent of: +/// +/// ```rust,ignore +/// #[cfg(test)] +/// mod tests { +/// use super::*; +/// use crate::tests::{new_test_ext, Test}; +/// 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_sort_vector::()); +/// }); +/// } +/// } +/// ``` +/// +/// When called inside the `benchmarks` macro of the `pallet_example_basic` as +/// +/// ```rust,ignore +/// benchmarks! { +/// // Benchmarks omitted for brevity +/// +/// impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); +/// } +/// ``` +/// +/// It expands to the equivalent of: +/// +/// ```rust,ignore +/// #[cfg(test)] +/// mod benchmarking { +/// use super::*; +/// use crate::tests::{new_test_ext, Test}; +/// use frame_support::assert_ok; +/// +/// #[test] +/// fn bench_accumulate_dummy() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(test_benchmark_accumulate_dummy::()); +/// }) +/// } +/// +/// #[test] +/// fn bench_set_dummy() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(test_benchmark_set_dummy::()); +/// }) +/// } +/// +/// #[test] +/// fn bench_sort_vector() { +/// new_test_ext().execute_with(|| { +/// assert_ok!(test_benchmark_sort_vector::()); +/// }) +/// } +/// } +/// ``` +/// +/// ## Arguments +/// +/// The first argument, `module`, must be the path to this crate's module. +/// +/// The second 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. +/// +/// The third argument, `test`, must be the path to the runtime. The item to which this must refer +/// will generally take the form: +/// +/// ```rust,ignore +/// frame_support::construct_runtime!( +/// pub enum Test where ... +/// { ... } +/// ); +/// ``` +/// +/// There is an optional fourth 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 `benchmarks!` +/// invocation, then you should provide the path to the module containing the `benchmarks!` +/// invocation: +/// +/// ```rust,ignore +/// mod benches { +/// benchmarks!{ +/// ... +/// } +/// } +/// +/// mod tests { +/// // because of macro syntax limitations, neither Pallet nor benches can be paths, but both have +/// // to be idents in the scope of `impl_benchmark_test_suite`. +/// use crate::{benches, Pallet}; +/// +/// impl_benchmark_test_suite!(Pallet, new_test_ext(), Test, benchmarks_path = benches); +/// +/// // new_test_ext and the Test item are defined later in this module +/// } +/// ``` +/// +/// There is an optional fifth argument, with keyword syntax: `extra = true` or `extra = false`. +/// By default, this generates a test suite which iterates over all benchmarks, including those +/// marked with the `#[extra]` annotation. Setting `extra = false` excludes those. +/// +/// There is an optional sixth argument, with keyword syntax: `exec_name = custom_exec_name`. +/// By default, this macro uses `execute_with` for this parameter. This argument, if set, is subject +/// to these restrictions: +/// +/// - It must be the name of a method applied to the output of the `new_test_ext` argument. +/// - That method must have a signature capable of receiving a single argument of the form `impl +/// FnOnce()`. +// ## Notes (not for rustdoc) +// +// The biggest challenge for this macro is communicating the actual test functions to be run. We +// can't just build an array of function pointers to each test function and iterate over it, because +// the test functions are parameterized by the `Test` type. That's incompatible with +// monomorphization: if it were legal, then even if the compiler detected and monomorphized the +// functions into only the types of the callers, which implementation would the function pointer +// point to? There would need to be some kind of syntax for selecting the destination of the pointer +// according to a generic argument, and in general it would be a huge mess and not worth it. +// +// Instead, we're going to steal a trick from `fn run_benchmark`: generate a function which is +// itself parametrized by `Test`, which accepts a `&[u8]` parameter containing the name of the +// benchmark, and dispatches based on that to the appropriate real test implementation. Then, we can +// just iterate over the `Benchmarking::benchmarks` list to run the actual implementations. +#[macro_export] +macro_rules! impl_benchmark_test_suite { + ( + $bench_module:ident, + $new_test_ext:expr, + $test:path + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + () + () + () + $bench_module, + $new_test_ext, + $test + $(, $( $rest )* )? + ); + } +} + +/// Validates the passed `pov_mode`s. +/// +/// Checks that: +/// - a top-level `ignored` is exclusive +/// - all modes are valid +#[macro_export] +macro_rules! validate_pov_mode { + () => {}; + ( $_bench:ident: ; ) => { }; + ( $_bench:ident: $_car:path = Ignored ; ) => { }; + ( $bench:ident: $_car:path = Ignored $( $storage:path = $_pov_mode:ident )+; ) => { + compile_error!( + concat!(concat!("`pov_mode = Ignored` is exclusive. Please remove the attribute from keys: ", $( stringify!($storage) )+), " on benchmark '", stringify!($bench), "'")); + }; + ( $bench:ident: $car:path = Measured $( $storage:path = $pov_mode:ident )*; ) => { + $crate::validate_pov_mode!( + $bench: $( $storage = $pov_mode )*; + ); + }; + ( $bench:ident: $car:path = MaxEncodedLen $( $storage:path = $pov_mode:ident )*; ) => { + $crate::validate_pov_mode!( + $bench: $( $storage = $pov_mode )*; + ); + }; + ( $bench:ident: $key:path = $unknown:ident $( $_storage:path = $_pov_mode:ident )*; ) => { + compile_error!( + concat!("Unknown pov_mode '", stringify!($unknown) ,"' for benchmark '", stringify!($bench), "' on key '", stringify!($key), "'. Must be one of: Ignored, Measured, MaxEncodedLen") + ); + }; +} + +// Takes all arguments from `impl_benchmark_test_suite` and three additional arguments. +// +// Can be configured to generate one #[test] fn per bench case or +// one #[test] fn for all bench cases. +// This depends on whether or not the first argument contains a non-empty list of bench names. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_test_function { + // user might or might not have set some keyword arguments; set the defaults + // + // The weird syntax indicates that `rest` comes only after a comma, which is otherwise optional + ( + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + + $bench_module:ident, + $new_test_ext:expr, + $test:path + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + @cases: + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + @selected: + $bench_module, + $new_test_ext, + $test, + benchmarks_path = super, + extra = true, + exec_name = execute_with, + @user: + $( $( $rest )* )? + ); + }; + // pick off the benchmarks_path keyword argument + ( + @cases: + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $old:ident, + extra = $extra:expr, + exec_name = $exec_name:ident, + @user: + benchmarks_path = $benchmarks_path:ident + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + @cases: + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + @selected: + $bench_module, + $new_test_ext, + $test, + benchmarks_path = $benchmarks_path, + extra = $extra, + exec_name = $exec_name, + @user: + $( $( $rest )* )? + ); + }; + // pick off the extra keyword argument + ( + @cases: + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $benchmarks_path:ident, + extra = $old:expr, + exec_name = $exec_name:ident, + @user: + extra = $extra:expr + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + @cases: + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + @selected: + $bench_module, + $new_test_ext, + $test, + benchmarks_path = $benchmarks_path, + extra = $extra, + exec_name = $exec_name, + @user: + $( $( $rest )* )? + ); + }; + // pick off the exec_name keyword argument + ( + @cases: + ( $( $names:tt )* ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $benchmarks_path:ident, + extra = $extra:expr, + exec_name = $old:ident, + @user: + exec_name = $exec_name:ident + $(, $( $rest:tt )* )? + ) => { + $crate::impl_test_function!( + @cases: + ( $( $names )* ) + ( $( $names_extra )* ) + ( $( $names_skip_meta )* ) + @selected: + $bench_module, + $new_test_ext, + $test, + benchmarks_path = $benchmarks_path, + extra = $extra, + exec_name = $exec_name, + @user: + $( $( $rest )* )? + ); + }; + // iteration-exit arm which generates a #[test] function for each case. + ( + @cases: + ( $( $names:tt )+ ) + ( $( $names_extra:tt )* ) + ( $( $names_skip_meta:tt )* ) + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $path_to_benchmarks_invocation:ident, + extra = $extra:expr, + exec_name = $exec_name:ident, + @user: + $(,)? + ) => { + $crate::impl_bench_case_tests!( + { $bench_module, $new_test_ext, $exec_name, $test, $extra } + { $( $names_extra:tt )* } + $($names)+ + ); + }; + // iteration-exit arm which generates one #[test] function for all cases. + ( + @cases: + () + () + () + @selected: + $bench_module:ident, + $new_test_ext:expr, + $test:path, + benchmarks_path = $path_to_benchmarks_invocation:ident, + extra = $extra:expr, + exec_name = $exec_name:ident, + @user: + $(,)? + ) => { + #[cfg(test)] + mod benchmark_tests { + use super::$bench_module; + + #[test] + fn test_benchmarks() { + $new_test_ext.$exec_name(|| { + use $crate::Benchmarking; + + let mut anything_failed = false; + println!("failing benchmark tests:"); + for benchmark_metadata in $bench_module::<$test>::benchmarks($extra) { + let benchmark_name = &benchmark_metadata.name; + match std::panic::catch_unwind(|| { + $bench_module::<$test>::test_bench_by_name(benchmark_name) + }) { + Err(err) => { + println!( + "{}: {:?}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + err, + ); + anything_failed = true; + }, + Ok(Err(err)) => { + match err { + $crate::BenchmarkError::Stop(err) => { + println!( + "{}: {:?}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + err, + ); + anything_failed = true; + }, + $crate::BenchmarkError::Override(_) => { + // This is still considered a success condition. + $crate::log::error!( + "WARNING: benchmark error overrided - {}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + ); + }, + $crate::BenchmarkError::Skip => { + // This is considered a success condition. + $crate::log::error!( + "WARNING: benchmark error skipped - {}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + ); + } + $crate::BenchmarkError::Weightless => { + // This is considered a success condition. + $crate::log::error!( + "WARNING: benchmark weightless skipped - {}", + $crate::str::from_utf8(benchmark_name) + .expect("benchmark name is always a valid string!"), + ); + } + } + }, + Ok(Ok(())) => (), + } + } + assert!(!anything_failed); + }); + } + } + }; +} + +/// show error message and debugging info for the case of an error happening +/// during a benchmark +pub fn show_benchmark_debug_info( + instance_string: &[u8], + benchmark: &[u8], + components: &[(BenchmarkParameter, u32)], + verify: &bool, + error_message: &str, +) -> sp_runtime::RuntimeString { + sp_runtime::format_runtime_string!( + "\n* Pallet: {}\n\ + * Benchmark: {}\n\ + * Components: {:?}\n\ + * Verify: {:?}\n\ + * Error message: {}", + sp_std::str::from_utf8(instance_string) + .expect("it's all just strings ran through the wasm interface. qed"), + sp_std::str::from_utf8(benchmark) + .expect("it's all just strings ran through the wasm interface. qed"), + components, + verify, + error_message, + ) +} + +/// This macro adds pallet benchmarks to a `Vec` object. +/// +/// First create an object that holds in the input parameters for the benchmark: +/// +/// ```ignore +/// let params = (&config, &whitelist); +/// ``` +/// +/// The `whitelist` is a parameter you pass to control the DB read/write tracking. +/// We use a vector of [TrackedStorageKey](./struct.TrackedStorageKey.html), which is a simple +/// struct used to set if a key has been read or written to. +/// +/// For values that should be skipped entirely, we can just pass `key.into()`. For example: +/// +/// ``` +/// use frame_benchmarking::TrackedStorageKey; +/// let whitelist: Vec = vec![ +/// // Block Number +/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"), +/// // Total Issuance +/// array_bytes::hex_into_unchecked("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80"), +/// // Execution Phase +/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a"), +/// // Event Count +/// array_bytes::hex_into_unchecked("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850"), +/// ]; +/// ``` +/// +/// Then define a mutable local variable to hold your `BenchmarkBatch` object: +/// +/// ```ignore +/// let mut batches = Vec::::new(); +/// ```` +/// +/// Then add the pallets you want to benchmark to this object, using their crate name and generated +/// module struct: +/// +/// ```ignore +/// add_benchmark!(params, batches, pallet_balances, Balances); +/// add_benchmark!(params, batches, pallet_session, SessionBench::); +/// add_benchmark!(params, batches, frame_system, SystemBench::); +/// ... +/// ``` +/// +/// At the end of `dispatch_benchmark`, you should return this batches object. +/// +/// In the case where you have multiple instances of a pallet that you need to separately benchmark, +/// the name of your module struct will be used as a suffix to your outputted weight file. For +/// example: +/// +/// ```ignore +/// add_benchmark!(params, batches, pallet_balances, Balances); // pallet_balances.rs +/// add_benchmark!(params, batches, pallet_collective, Council); // pallet_collective_council.rs +/// add_benchmark!(params, batches, pallet_collective, TechnicalCommittee); // pallet_collective_technical_committee.rs +/// ``` +/// +/// You can manipulate this suffixed string by using a type alias if needed. For example: +/// +/// ```ignore +/// type Council2 = TechnicalCommittee; +/// add_benchmark!(params, batches, pallet_collective, Council2); // pallet_collective_council_2.rs +/// ``` +#[macro_export] +macro_rules! add_benchmark { + ( $params:ident, $batches:ident, $name:path, $( $location:tt )* ) => ( + let name_string = stringify!($name).as_bytes(); + let instance_string = stringify!( $( $location )* ).as_bytes(); + let (config, whitelist) = $params; + let $crate::BenchmarkConfig { + pallet, + benchmark, + selected_components, + verify, + internal_repeats, + } = config; + if &pallet[..] == &name_string[..] { + let benchmark_result = $( $location )*::run_benchmark( + &benchmark[..], + &selected_components[..], + whitelist, + *verify, + *internal_repeats, + ); + + let final_results = match benchmark_result { + Ok(results) => Some(results), + Err($crate::BenchmarkError::Override(mut result)) => { + // Insert override warning as the first storage key. + $crate::log::error!( + "WARNING: benchmark error overrided - {}", + $crate::str::from_utf8(benchmark) + .expect("benchmark name is always a valid string!") + ); + result.keys.insert(0, + (b"Benchmark Override".to_vec(), 0, 0, false) + ); + Some($crate::vec![result]) + }, + Err($crate::BenchmarkError::Stop(e)) => { + $crate::show_benchmark_debug_info( + instance_string, + benchmark, + selected_components, + verify, + e, + ); + return Err(e.into()); + }, + Err($crate::BenchmarkError::Skip) => { + $crate::log::error!( + "WARNING: benchmark error skipped - {}", + $crate::str::from_utf8(benchmark) + .expect("benchmark name is always a valid string!") + ); + None + }, + Err($crate::BenchmarkError::Weightless) => { + $crate::log::error!( + "WARNING: benchmark weightless skipped - {}", + $crate::str::from_utf8(benchmark) + .expect("benchmark name is always a valid string!") + ); + Some(vec![$crate::BenchmarkResult { + components: selected_components.clone(), + .. Default::default() + }]) + } + }; + + if let Some(final_results) = final_results { + $batches.push($crate::BenchmarkBatch { + pallet: name_string.to_vec(), + instance: instance_string.to_vec(), + benchmark: benchmark.clone(), + results: final_results, + }); + } + } + ) +} + +/// Callback for `define_benchmarks` to call `add_benchmark`. +#[macro_export] +macro_rules! cb_add_benchmarks { + // anchor + ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] ) => { + $crate::add_benchmark!( $params, $batches, $name, $( $location )* ); + }; + // recursion tail + ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { + $crate::cb_add_benchmarks!( $params, $batches, [ $name, $( $location )* ] ); + $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); + } +} + +/// This macro allows users to easily generate a list of benchmarks for the pallets configured +/// in the runtime. +/// +/// To use this macro, first create a an object to store the list: +/// +/// ```ignore +/// let mut list = Vec::::new(); +/// ``` +/// +/// Then pass this `list` to the macro, along with the `extra` boolean, the pallet crate, and +/// pallet struct: +/// +/// ```ignore +/// list_benchmark!(list, extra, pallet_balances, Balances); +/// list_benchmark!(list, extra, pallet_session, SessionBench::); +/// list_benchmark!(list, extra, frame_system, SystemBench::); +/// ``` +/// +/// This should match what exists with the `add_benchmark!` macro. +#[macro_export] +macro_rules! list_benchmark { + ( $list:ident, $extra:ident, $name:path, $( $location:tt )* ) => ( + let pallet_string = stringify!($name).as_bytes(); + let instance_string = stringify!( $( $location )* ).as_bytes(); + let benchmarks = $( $location )*::benchmarks($extra); + let pallet_benchmarks = BenchmarkList { + pallet: pallet_string.to_vec(), + instance: instance_string.to_vec(), + benchmarks: benchmarks.to_vec(), + }; + $list.push(pallet_benchmarks) + ) +} + +/// Callback for `define_benchmarks` to call `list_benchmark`. +#[macro_export] +macro_rules! cb_list_benchmarks { + // anchor + ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] ) => { + $crate::list_benchmark!( $list, $extra, $name, $( $location )* ); + }; + // recursion tail + ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { + $crate::cb_list_benchmarks!( $list, $extra, [ $name, $( $location )* ] ); + $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); + } +} + +/// Defines pallet configs that `add_benchmarks` and `list_benchmarks` use. +/// Should be preferred instead of having a repetitive list of configs +/// in `add_benchmark` and `list_benchmark`. +#[macro_export] +macro_rules! define_benchmarks { + ( $([ $names:path, $( $locations:tt )* ])* ) => { + /// Calls `list_benchmark` with all configs from `define_benchmarks` + /// and passes the first two parameters on. + /// + /// Use as: + /// ```ignore + /// list_benchmarks!(list, extra); + /// ``` + #[macro_export] + macro_rules! list_benchmarks { + ( $list:ident, $extra:ident ) => { + $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); + } + } + + /// Calls `add_benchmark` with all configs from `define_benchmarks` + /// and passes the first two parameters on. + /// + /// Use as: + /// ```ignore + /// add_benchmarks!(params, batches); + /// ``` + #[macro_export] + macro_rules! add_benchmarks { + ( $params:ident, $batches:ident ) => { + $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); + } + } + } +} + +pub use add_benchmark; +pub use benchmark_backend; +pub use benchmarks; +pub use benchmarks_instance; +pub use benchmarks_instance_pallet; +pub use benchmarks_iter; +pub use cb_add_benchmarks; +pub use cb_list_benchmarks; +pub use define_benchmarks; +pub use impl_bench_case_tests; +pub use impl_bench_name_tests; +pub use impl_benchmark; +pub use impl_benchmark_test; +pub use impl_benchmark_test_suite; +pub use impl_test_function; +pub use list_benchmark; +pub use selected_benchmark; +pub use to_origin; +pub use whitelist; diff --git a/frame/bounties/src/benchmarking.rs b/frame/bounties/src/benchmarking.rs index adadb3411ac65..a43372978e5ca 100644 --- a/frame/bounties/src/benchmarking.rs +++ b/frame/bounties/src/benchmarking.rs @@ -21,7 +21,9 @@ use super::*; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller, BenchmarkError}; +use frame_benchmarking::v1::{ + account, benchmarks_instance_pallet, whitelisted_caller, BenchmarkError, +}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/child-bounties/src/benchmarking.rs b/frame/child-bounties/src/benchmarking.rs index 04a8583f286f1..e7f18c1645823 100644 --- a/frame/child-bounties/src/benchmarking.rs +++ b/frame/child-bounties/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller, BenchmarkError}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError}; use frame_system::RawOrigin; use crate::Pallet as ChildBounties; diff --git a/frame/collective/src/benchmarking.rs b/frame/collective/src/benchmarking.rs index 75a724623002e..5c9e55862d1a4 100644 --- a/frame/collective/src/benchmarking.rs +++ b/frame/collective/src/benchmarking.rs @@ -23,7 +23,7 @@ use crate::Pallet as Collective; use sp_runtime::traits::Bounded; use sp_std::mem::size_of; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_system::{Call as SystemCall, Pallet as System, RawOrigin as SystemOrigin}; const SEED: u32 = 0; diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 2bd807dfb758f..708d92766d488 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -37,7 +37,7 @@ use crate::{ Pallet as Contracts, *, }; use codec::{Encode, MaxEncodedLen}; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::weights::Weight; use frame_system::RawOrigin; use sp_runtime::{ diff --git a/frame/conviction-voting/src/benchmarking.rs b/frame/conviction-voting/src/benchmarking.rs index 117bb7fe22989..8c168a34a739d 100644 --- a/frame/conviction-voting/src/benchmarking.rs +++ b/frame/conviction-voting/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use assert_matches::assert_matches; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist_account}; use frame_support::{ dispatch::RawOrigin, traits::{fungible, Currency, Get}, diff --git a/frame/democracy/src/benchmarking.rs b/frame/democracy/src/benchmarking.rs index 424192e2521da..66d9e2a007afa 100644 --- a/frame/democracy/src/benchmarking.rs +++ b/frame/democracy/src/benchmarking.rs @@ -19,7 +19,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks, whitelist_account}; use frame_support::{ assert_noop, assert_ok, traits::{Currency, EnsureOrigin, Get, OnInitialize, UnfilteredDispatchable}, diff --git a/frame/election-provider-support/benchmarking/src/lib.rs b/frame/election-provider-support/benchmarking/src/lib.rs index 547e35bed36e8..5323513da98d5 100644 --- a/frame/election-provider-support/benchmarking/src/lib.rs +++ b/frame/election-provider-support/benchmarking/src/lib.rs @@ -22,7 +22,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::Decode; -use frame_benchmarking::{benchmarks, Vec}; +use frame_benchmarking::v1::{benchmarks, Vec}; use frame_election_provider_support::{NposSolver, PhragMMS, SequentialPhragmen}; pub struct Pallet(frame_system::Pallet); diff --git a/frame/elections-phragmen/src/benchmarking.rs b/frame/elections-phragmen/src/benchmarking.rs index 53157deaa961b..1fc500a8e3464 100644 --- a/frame/elections-phragmen/src/benchmarking.rs +++ b/frame/elections-phragmen/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelist, BenchmarkError, BenchmarkResult}; +use frame_benchmarking::v1::{account, benchmarks, whitelist, BenchmarkError, BenchmarkResult}; use frame_support::{dispatch::DispatchResultWithPostInfo, traits::OnInitialize}; use frame_system::RawOrigin; diff --git a/frame/examples/basic/src/benchmarking.rs b/frame/examples/basic/src/benchmarking.rs index 699e56685fbe9..a9e2013b0446c 100644 --- a/frame/examples/basic/src/benchmarking.rs +++ b/frame/examples/basic/src/benchmarking.rs @@ -21,8 +21,11 @@ #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; -use frame_support::benchmarking::{benchmarks, Linear}; +use frame_benchmarking::v1::{ + impl_benchmark_test_suite, + v2::{benchmarks, Linear}, + whitelisted_caller, +}; use frame_system::RawOrigin; // To actually run this benchmark on pallet-example-basic, we need to put this pallet into the diff --git a/frame/fast-unstake/src/benchmarking.rs b/frame/fast-unstake/src/benchmarking.rs index de85502f7b411..77488b6447c61 100644 --- a/frame/fast-unstake/src/benchmarking.rs +++ b/frame/fast-unstake/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{types::*, Pallet as FastUnstake, *}; -use frame_benchmarking::{benchmarks, whitelist_account}; +use frame_benchmarking::v1::{benchmarks, whitelist_account}; use frame_support::{ assert_ok, traits::{Currency, EnsureOrigin, Get, Hooks}, diff --git a/frame/grandpa/src/benchmarking.rs b/frame/grandpa/src/benchmarking.rs index 1240859149920..6eee12ad98dbf 100644 --- a/frame/grandpa/src/benchmarking.rs +++ b/frame/grandpa/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Benchmarks for the GRANDPA pallet. use super::{Pallet as Grandpa, *}; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; use frame_system::RawOrigin; use sp_core::H256; diff --git a/frame/identity/src/benchmarking.rs b/frame/identity/src/benchmarking.rs index 3584eb954b399..356bcb2e80174 100644 --- a/frame/identity/src/benchmarking.rs +++ b/frame/identity/src/benchmarking.rs @@ -22,7 +22,7 @@ use super::*; use crate::Pallet as Identity; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::{ ensure, traits::{EnsureOrigin, Get}, diff --git a/frame/im-online/src/benchmarking.rs b/frame/im-online/src/benchmarking.rs index edc21043c34de..e4d1a64651408 100644 --- a/frame/im-online/src/benchmarking.rs +++ b/frame/im-online/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; use frame_support::{traits::UnfilteredDispatchable, WeakBoundedVec}; use frame_system::RawOrigin; use sp_core::{offchain::OpaqueMultiaddr, OpaquePeerId}; diff --git a/frame/indices/src/benchmarking.rs b/frame/indices/src/benchmarking.rs index f462f22284d40..ce1ea9f4d39f3 100644 --- a/frame/indices/src/benchmarking.rs +++ b/frame/indices/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/lottery/src/benchmarking.rs b/frame/lottery/src/benchmarking.rs index fba722a07fabd..658dd25a98a27 100644 --- a/frame/lottery/src/benchmarking.rs +++ b/frame/lottery/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::{ storage::bounded_vec::BoundedVec, traits::{EnsureOrigin, OnInitialize}, diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 934abb996df80..6d973a15fe032 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -362,7 +362,7 @@ impl, I: 'static> SortedMembers for Pallet { #[cfg(feature = "runtime-benchmarks")] mod benchmark { use super::{Pallet as Membership, *}; - use frame_benchmarking::{account, benchmarks_instance_pallet, whitelist}; + use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist}; use frame_support::{assert_ok, traits::EnsureOrigin}; use frame_system::RawOrigin; diff --git a/frame/merkle-mountain-range/src/benchmarking.rs b/frame/merkle-mountain-range/src/benchmarking.rs index d24364a55f9e6..7731c0e505c97 100644 --- a/frame/merkle-mountain-range/src/benchmarking.rs +++ b/frame/merkle-mountain-range/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::benchmarks_instance_pallet; +use frame_benchmarking::v1::benchmarks_instance_pallet; use frame_support::traits::OnInitialize; benchmarks_instance_pallet! { diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 9651c81e5e66d..97ca71ce9d7d2 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -22,8 +22,8 @@ use super::{mock_helpers::*, Pallet as MessageQueue, *}; -use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; -use frame_support::{benchmarking::*, traits::Get}; +use frame_benchmarking::v2::*; +use frame_support::traits::Get; use frame_system::RawOrigin; use sp_std::prelude::*; diff --git a/frame/multisig/src/benchmarking.rs b/frame/multisig/src/benchmarking.rs index d5faf9ae8ac1a..8993908b25acf 100644 --- a/frame/multisig/src/benchmarking.rs +++ b/frame/multisig/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{account, benchmarks}; +use frame_benchmarking::v1::{account, benchmarks}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 6517445da672d..17ff28b822814 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use enumflags2::{BitFlag, BitFlags}; -use frame_benchmarking::{ +use frame_benchmarking::v1::{ account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, }; use frame_support::{ diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index b45c982bd150a..b01d414c4b251 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{nonfungible::Inspect, Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 9b063539152b7..32708ff53e01c 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -23,7 +23,9 @@ #[cfg(test)] mod mock; -use frame_benchmarking::{account, frame_support::traits::Currency, vec, whitelist_account, Vec}; +use frame_benchmarking::v1::{ + account, frame_support::traits::Currency, vec, whitelist_account, Vec, +}; use frame_election_provider_support::SortedListProvider; use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index 7e586a96bbe3e..b71321906cbdd 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -24,7 +24,7 @@ mod mock; use sp_std::{prelude::*, vec}; -use frame_benchmarking::{account, benchmarks}; +use frame_benchmarking::v1::{account, benchmarks}; use frame_support::traits::{Currency, Get, ValidatorSet, ValidatorSetWithIdentification}; use frame_system::{Config as SystemConfig, Pallet as System, RawOrigin}; diff --git a/frame/preimage/src/benchmarking.rs b/frame/preimage/src/benchmarking.rs index 8a61d7d780bfd..f9526b67d4bc8 100644 --- a/frame/preimage/src/benchmarking.rs +++ b/frame/preimage/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Preimage pallet benchmarking. use super::*; -use frame_benchmarking::{account, benchmarks, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks, whitelist_account}; use frame_support::assert_ok; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/proxy/src/benchmarking.rs b/frame/proxy/src/benchmarking.rs index 58c0cb73011df..ef3df02dcc776 100644 --- a/frame/proxy/src/benchmarking.rs +++ b/frame/proxy/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use crate::Pallet as Proxy; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/ranked-collective/src/benchmarking.rs b/frame/ranked-collective/src/benchmarking.rs index eb629b330abb2..48df7f1f1e6ab 100644 --- a/frame/ranked-collective/src/benchmarking.rs +++ b/frame/ranked-collective/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; #[allow(unused_imports)] use crate::Pallet as RankedCollective; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_support::{assert_ok, dispatch::UnfilteredDispatchable}; use frame_system::RawOrigin as SystemOrigin; diff --git a/frame/recovery/src/benchmarking.rs b/frame/recovery/src/benchmarking.rs index 870543d9bd290..48ca7dad766bb 100644 --- a/frame/recovery/src/benchmarking.rs +++ b/frame/recovery/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use crate::Pallet; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; diff --git a/frame/referenda/src/benchmarking.rs b/frame/referenda/src/benchmarking.rs index e57b5f9859e5b..db5299e0d5bb2 100644 --- a/frame/referenda/src/benchmarking.rs +++ b/frame/referenda/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use crate::Pallet as Referenda; use assert_matches::assert_matches; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelist_account}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist_account}; use frame_support::{ assert_ok, dispatch::UnfilteredDispatchable, diff --git a/frame/remark/src/benchmarking.rs b/frame/remark/src/benchmarking.rs index c0db8d5d3d59b..ee93cb24a3f34 100644 --- a/frame/remark/src/benchmarking.rs +++ b/frame/remark/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; use frame_system::{EventRecord, Pallet as System, RawOrigin}; use sp_std::*; diff --git a/frame/scheduler/src/benchmarking.rs b/frame/scheduler/src/benchmarking.rs index 8e2876290be55..9ec82f5c8447e 100644 --- a/frame/scheduler/src/benchmarking.rs +++ b/frame/scheduler/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Scheduler pallet benchmarking. use super::*; -use frame_benchmarking::{account, benchmarks}; +use frame_benchmarking::v1::{account, benchmarks}; use frame_support::{ ensure, traits::{schedule::Priority, BoundedInline}, diff --git a/frame/session/benchmarking/src/lib.rs b/frame/session/benchmarking/src/lib.rs index 9e478ada53cf2..a6c25f5e10549 100644 --- a/frame/session/benchmarking/src/lib.rs +++ b/frame/session/benchmarking/src/lib.rs @@ -26,7 +26,7 @@ mod mock; use sp_runtime::traits::{One, StaticLookup, TrailingZeroInput}; use sp_std::{prelude::*, vec}; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; use frame_support::{ codec::Decode, traits::{Get, KeyOwnerProofSystem, OnInitialize}, diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index b3d32b26ec1f7..a4366bbd8aa9a 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -35,7 +35,7 @@ use sp_runtime::{ use sp_staking::SessionIndex; use sp_std::prelude::*; -pub use frame_benchmarking::{ +pub use frame_benchmarking::v1::{ account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, }; use frame_system::RawOrigin; diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index ae2fc358974b6..4f62ae42ef78f 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -27,7 +27,6 @@ sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../pri sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../primitives/staking" } sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" } -static_assertions = "1.1.0" tt-call = "1.0.8" frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "./procedural" } paste = "1.0" diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index f3bd5c2cfe6bf..fa7fd44928659 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -638,7 +638,7 @@ fn expand_benchmark( Ok(ident) => ident, Err(err) => return err.to_compile_error().into(), }; - let home = quote!(#krate::frame_support::benchmarking); + let home = quote!(#krate::v2); let codec = quote!(#krate::frame_support::codec); let traits = quote!(#krate::frame_support::traits); let setup_stmts = benchmark_def.setup_stmts; diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 9bdbbd1f27bb2..8fa46b48d005f 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -483,7 +483,7 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { /// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will /// designate that module as a benchmarking module. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { match benchmark::benchmarks(attr, tokens, false) { @@ -495,7 +495,7 @@ pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { /// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will /// designate that module as an instance benchmarking module. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { match benchmark::benchmarks(attr, tokens, true) { @@ -508,7 +508,7 @@ pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStrea /// attached to a function definition containing an `#[extrinsic_call]` or `#[block]` /// attribute. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { quote!(compile_error!( @@ -521,7 +521,7 @@ pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { /// used as a boundary designating where the benchmark setup code ends, and the benchmark /// verification code begins. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { quote!(compile_error!( @@ -534,7 +534,7 @@ pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream /// enclosing benchmark function, This attribute is also used as a boundary designating where /// the benchmark setup code ends, and the benchmark verification code begins. /// -/// See `frame_support::benchmarking` for more info. +/// See `frame_benchmarking::v2` for more info. #[proc_macro_attribute] pub fn block(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { quote!(compile_error!( diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 40bc878cff365..902893972f0b1 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2745,228 +2745,5 @@ pub mod pallet_macros { }; } -/// Contains macros, structs, and traits associated with v2 of the pallet benchmarking syntax. -/// This module contains macros, structs, and traits associated with v2 of the pallet -/// benchmarking syntax. -/// -/// The [`benchmarking::benchmarks`] and [`benchmarking::instance_benchmarks`] macros can be -/// used to designate a module as a benchmarking module that can contain benchmarks and -/// benchmark tests. The `#[benchmarks]` variant will set up a regular, non-instance -/// benchmarking module, and the `#[instance_benchmarks]` variant will set up the module in -/// instance benchmarking mode. -/// -/// Benchmarking modules should be gated behind a `#[cfg(feature = "runtime-benchmarks")]` -/// feature gate to ensure benchmarking code that is only compiled when the -/// `runtime-benchmarks` feature is enabled is not referenced. -/// -/// The following is the general syntax for a benchmarks (or instance benchmarks) module: -/// -/// ## General Syntax -/// -/// ```ignore -/// #![cfg(feature = "runtime-benchmarks")] -/// -/// use super::{mock_helpers::*, Pallet as MyPallet}; -/// use frame_support::benchmarking::*; -/// use frame_benchmarking::whitelisted_caller; -/// -/// #[benchmarks] -/// mod benchmarks { -/// use super::*; -/// -/// #[benchmark] -/// fn bench_name_1(x: Linear<7, 1_000>, y: Linear<1_000, 100_0000>) { -/// // setup code -/// let z = x + y; -/// let caller = whitelisted_caller(); -/// -/// #[extrinsic_call] -/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments); -/// -/// // verification code -/// assert_eq!(MyPallet::::my_var(), z); -/// } -/// -/// #[benchmark] -/// fn bench_name_2() { -/// // setup code -/// let caller = whitelisted_caller(); -/// -/// #[block] -/// { -/// something(some, thing); -/// my_extrinsic(RawOrigin::Signed(caller), some, argument); -/// something_else(foo, bar); -/// } -/// -/// // verification code -/// assert_eq!(MyPallet::::something(), 37); -/// } -/// } -/// ``` -/// -/// ## Benchmark Definitions -/// -/// Within a `#[benchmarks]` or `#[instance_benchmarks]` module, you can define individual -/// benchmarks using the `#[benchmark]` attribute, as shown in the example above. -/// -/// The `#[benchmark]` attribute expects a function definition with a blank return type and -/// zero or more arguments whose names are valid `frame_benchmarking::BenchmarkParamater` -/// parameters, such as `x`, `y`, `a`, `b`, etc., and whose param types must implement -/// [`benchmarking::ParamRange`]. At the moment the only valid type that implements -/// [`benchmarking::ParamRange`] is [`benchmarking::Linear`]. -/// -/// The valid syntax for defining a `Linear` is `Linear` where `A`, and `B` are -/// valid integer literals (that fit in a `u32`), such that `B` >= `A`. -/// -/// Note that the benchmark function definition does not actually expand as a function -/// definition, but rather is used to automatically create a number of impls and structs -/// required by the benchmarking engine. For this reason, the visibility of the function -/// definition as well as the return type are not used for any purpose and are discarded by the -/// expansion code. -/// -/// Also note that the `// setup code` and `// verification code` comments shown above are not -/// required and are included simply for demonstration purposes. -/// -/// ### `#[extrinsic_call]` and `#[block]` -/// -/// Within the benchmark function body, either an `#[extrinsic_call]` or a `#[block]` -/// annotation is required. These attributes should be attached to a block (shown in -/// `bench_name_2` above) or a one-line function call (shown in `bench_name_1` above, in `syn` -/// parlance this should be an `ExprCall`), respectively. -/// -/// The `#[block]` syntax is broad and will benchmark any code contained within the block the -/// attribute is attached to. If `#[block]` is attached to something other than a block, a -/// compiler error will be emitted. -/// -/// The one-line `#[extrinsic_call]` syntax must consist of a function call to an extrinsic, -/// where the first argument is the origin. If `#[extrinsic_call]` is attached to an item that -/// doesn't meet these requirements, a compiler error will be emitted. -/// -/// As a short-hand, you may substitute the name of the extrinsic call with `_`, such as the -/// following: -/// -/// ```ignore -/// #[extrinsic_call] -/// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); -/// ``` -/// -/// The underscore will be substituted with the name of the benchmark (i.e. the name of the -/// function in the benchmark function definition). -/// -/// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves -/// the purpose of designating the boundary between the setup code portion of the benchmark -/// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification -/// stage (everything after the item that the `#[extrinsic_call]` or `#[block]` attribute is -/// attached to). The setup code section should contain any code that needs to execute before -/// the measured portion of the benchmark executes. The verification section is where you can -/// perform assertions to verify that the extrinsic call (or whatever is happening in your -/// block, if you used the `#[block]` syntax) executed successfully. -/// -/// Note that neither `#[extrinsic_call]` nor `#[block]` are real attribute macros and are -/// instead consumed by the outer macro pattern as part of the enclosing benchmark function -/// definition. This is why we are able to use `#[extrinsic_call]` and `#[block]` within a -/// function definition even though this behavior has not been stabilized -/// yet—`#[extrinsic_call]` and `#[block]` are parsed and consumed as part of the benchmark -/// definition parsing code, so they never expand as their own attribute macros. -/// -/// ### Optional Attributes -/// -/// The keywords `extra` and `skip_meta` can be provided as optional arguments to the -/// `#[benchmark]` attribute, i.e. `#[benchmark(extra, skip_meta)]`. Including either of these -/// will enable the `extra` or `skip_meta` option, respectively. These options enable the same -/// behavior they did in the old benchmarking syntax in `frame_benchmarking`, namely: -/// -/// #### `extra` -/// -/// Specifies that this benchmark should not normally run. To run benchmarks marked with -/// `extra`, you will need to invoke the `frame-benchmarking-cli` with `--extra`. -/// -/// #### `skip_meta` -/// -/// Specifies that the benchmarking framework should not analyze the storage keys that -/// benchmarked code read or wrote. This useful to suppress the prints in the form of unknown -/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis -/// of all accesses, not just ones without metadata. -/// -/// ## Where Clause -/// -/// Some pallets require a where clause specifying constraints on their generics to make -/// writing benchmarks feasible. To accomodate this situation, you can provide such a where -/// clause as the (only) argument to the `#[benchmarks]` or `#[instance_benchmarks]` attribute -/// macros. Below is an example of this taken from the `message-queue` pallet. -/// -/// ```ignore -/// #[benchmarks( -/// where -/// <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, -/// ::Size: From, -/// )] -/// mod benchmarks { -/// use super::*; -/// // ... -/// } -/// ``` -/// -/// ## Benchmark Tests -/// -/// Benchmark tests can be generated using the old syntax in `frame_benchmarking`, -/// including the `frame_benchmarking::impl_benchmark_test_suite` macro. -/// -/// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module): -/// ```ignore -/// #[benchmarks] -/// mod benchmarks { -/// use super::*; -/// // ... -/// impl_benchmark_test_suite!( -/// MessageQueue, -/// crate::mock::new_test_ext::(), -/// crate::integration_test::Test -/// ); -/// } -/// ``` -pub mod benchmarking { - pub use frame_support_procedural::{ - benchmark, benchmarks, block, extrinsic_call, instance_benchmarks, - }; - - // Used in #[benchmark] implementation to ensure that benchmark function arguments - // implement [`ParamRange`]. - #[doc(hidden)] - pub use static_assertions::assert_impl_all; - - /// Used by the new benchmarking code to specify that a benchmarking variable is linear - /// over some specified range, i.e. `Linear<0, 1_000>` means that the corresponding variable - /// is allowed to range from `0` to `1000`, inclusive. - /// - /// See [`frame_support::benchmarking`] for more info. - pub struct Linear; - - /// Trait that must be implemented by all structs that can be used as parameter range types - /// in the new benchmarking code (i.e. `Linear<0, 1_000>`). Right now there is just - /// [`Linear`] but this could later be extended to support additional non-linear parameter - /// ranges. - /// - /// See [`frame_support::benchmarking`] for more info. - pub trait ParamRange { - /// Represents the (inclusive) starting number of this [`ParamRange`]. - fn start(&self) -> u32; - - /// Represents the (inclusive) ending number of this [`ParamRange`] - fn end(&self) -> u32; - } - - impl ParamRange for Linear { - fn start(&self) -> u32 { - return A - } - - fn end(&self) -> u32 { - return B - } - } -} - // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.rs b/frame/support/test/tests/benchmark_ui/bad_param_name.rs index ad4db4f0a4ba4..657e481a9430a 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_name.rs +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs index 50e4dc6fd4609..f970126d12e7e 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs @@ -1,9 +1,9 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; #[benchmarks] -mod benches { +mod benchmarks { #[benchmark] fn bench(xx: Linear<1, 2>) { #[block] diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs index 0270582b3b85b..9970f32301672 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.rs b/frame/support/test/tests/benchmark_ui/bad_param_range.rs index aabb9fa7403a1..993f2a0004103 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_range.rs +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/bad_params.rs b/frame/support/test/tests/benchmark_ui/bad_params.rs index a0c9236982c62..5049f2eae2c2e 100644 --- a/frame/support/test/tests/benchmark_ui/bad_params.rs +++ b/frame/support/test/tests/benchmark_ui/bad_params.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/dup_block.rs b/frame/support/test/tests/benchmark_ui/dup_block.rs index 4c4a8fc11d30a..2c2ef9db9a45c 100644 --- a/frame/support/test/tests/benchmark_ui/dup_block.rs +++ b/frame/support/test/tests/benchmark_ui/dup_block.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs index 1a91b7c16d6a5..4d135d1a04f52 100644 --- a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs +++ b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/extra_extra.rs b/frame/support/test/tests/benchmark_ui/extra_extra.rs index 021106c7afc85..1aa6c9ecb7526 100644 --- a/frame/support/test/tests/benchmark_ui/extra_extra.rs +++ b/frame/support/test/tests/benchmark_ui/extra_extra.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs index 1940f4cf1f040..3418c7af73748 100644 --- a/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs +++ b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs index 4cb6bfc34c58e..ce360ee7577f5 100644 --- a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[extrinsic_call] mod stuff {} diff --git a/frame/support/test/tests/benchmark_ui/missing_call.rs b/frame/support/test/tests/benchmark_ui/missing_call.rs index 74370493542c4..bc04101dd384a 100644 --- a/frame/support/test/tests/benchmark_ui/missing_call.rs +++ b/frame/support/test/tests/benchmark_ui/missing_call.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.rs b/frame/support/test/tests/benchmark_ui/missing_origin.rs index aad91bc79f825..2aaed756b9a46 100644 --- a/frame/support/test/tests/benchmark_ui/missing_origin.rs +++ b/frame/support/test/tests/benchmark_ui/missing_origin.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs index 5c84d7f76d874..450ce4f9c50da 100644 --- a/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs +++ b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; use frame_support_test::Config; #[benchmarks] diff --git a/frame/support/test/tests/benchmark_ui/unrecognized_option.rs b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs index 4c2cea139f80c..18cae4d5d5c8e 100644 --- a/frame/support/test/tests/benchmark_ui/unrecognized_option.rs +++ b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs @@ -1,4 +1,4 @@ -use frame_support::benchmarking::*; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use frame_support_test::Config; diff --git a/frame/system/benchmarking/src/lib.rs b/frame/system/benchmarking/src/lib.rs index 0f7603fe1dd9f..87d3c7fc7044b 100644 --- a/frame/system/benchmarking/src/lib.rs +++ b/frame/system/benchmarking/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::Encode; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; use frame_support::{dispatch::DispatchClass, storage, traits::Get}; use frame_system::{Call, Pallet as System, RawOrigin}; use sp_core::storage::well_known_keys; diff --git a/frame/timestamp/src/benchmarking.rs b/frame/timestamp/src/benchmarking.rs index 8974eb95692af..3a9297fda805e 100644 --- a/frame/timestamp/src/benchmarking.rs +++ b/frame/timestamp/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, TrackedStorageKey}; +use frame_benchmarking::v1::{benchmarks, TrackedStorageKey}; use frame_support::{ensure, traits::OnFinalize}; use frame_system::RawOrigin; diff --git a/frame/tips/src/benchmarking.rs b/frame/tips/src/benchmarking.rs index 312424e5799ec..8ffc53b45e4ca 100644 --- a/frame/tips/src/benchmarking.rs +++ b/frame/tips/src/benchmarking.rs @@ -19,7 +19,7 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_support::ensure; use frame_system::RawOrigin; use sp_runtime::traits::Saturating; diff --git a/frame/transaction-storage/src/benchmarking.rs b/frame/transaction-storage/src/benchmarking.rs index c7fbd00fb565d..736d2c40d0b39 100644 --- a/frame/transaction-storage/src/benchmarking.rs +++ b/frame/transaction-storage/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, Get, OnFinalize, OnInitialize}; use frame_system::{EventRecord, Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, One, Zero}; diff --git a/frame/treasury/src/benchmarking.rs b/frame/treasury/src/benchmarking.rs index d718a5fb89521..eb44744506bf4 100644 --- a/frame/treasury/src/benchmarking.rs +++ b/frame/treasury/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::{Pallet as Treasury, *}; -use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet}; use frame_support::{ dispatch::UnfilteredDispatchable, ensure, diff --git a/frame/uniques/src/benchmarking.rs b/frame/uniques/src/benchmarking.rs index ab34558f95eb3..5ecb093a5d010 100644 --- a/frame/uniques/src/benchmarking.rs +++ b/frame/uniques/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{ +use frame_benchmarking::v1::{ account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, }; use frame_support::{ diff --git a/frame/utility/src/benchmarking.rs b/frame/utility/src/benchmarking.rs index 07bc14951cb3b..f381b1e5f9e61 100644 --- a/frame/utility/src/benchmarking.rs +++ b/frame/utility/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_system::RawOrigin; const SEED: u32 = 0; diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index dde5fe3ac7561..eb0d596b8a38b 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -19,7 +19,7 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; use frame_support::assert_ok; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul}; diff --git a/frame/whitelist/src/benchmarking.rs b/frame/whitelist/src/benchmarking.rs index 569b13c12da31..e64842b34df59 100644 --- a/frame/whitelist/src/benchmarking.rs +++ b/frame/whitelist/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::benchmarks; +use frame_benchmarking::v1::benchmarks; use frame_support::{ensure, traits::EnsureOrigin}; #[cfg(test)]