diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 9d03920269b28..70f3582633e1a 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2747,23 +2747,179 @@ 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 +/// +/// ``` +/// #![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(); +/// +/// #[extrinsic_call] +/// { +/// 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. +/// +/// ### `#[extrinsic_call]` +/// +/// Within the benchmark function body, an `#[extrinsic_call]` annotation is required. This +/// attribute 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`). The block syntax can contain any code but should have an extrinsic call +/// somewhere inside it. The one-line, `ExprCall` 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. +/// +/// The `#[extrinsic_call]` attribute also serves the purpose of designating the boundary +/// between the setup code portion of the benchmark (everything before the `#[extrinsic_call]`) +/// and the verification stage (everything after the item that `#[extrinsic_call]`) 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 +/// `#[extrinsic_call]` block, if you used a block) executed successfully. +/// +/// Note that `#[extrinsic_call]` is not a real attribute macro and is consumed by the outer +/// macro pattern as part of the enclosing benchmark function definition. This is why we are +/// able to use `#[extrinsic_call]` within a function definition even though this behavior has +/// not been stabilized yet—`#[extrinsic_call]` is parsed and consumed as part of the benchmark +/// definition parsing code so it never expands as its own macro. +/// +/// ### `#[extra]` +/// +/// The optional `#[extra]` attribute can be applied to benchmark function definitions. This +/// attribute follows the semantics and behavior it did in the old benchmarking syntax in +/// `frame_benchmarking`. +/// +/// ## `#[skip_meta]` +/// +/// The optional `#[skip_meta]` attribute can be applied to benchmark function definitions. +/// This attribute follows the semantics and behavior it did in the old benchmarking syntax in +/// `frame_benchmarking`. +/// +/// ## 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. +/// +/// ``` +/// #[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): +/// ``` +/// #[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, instance_benchmarks}; - // Used in #[benchmark] to ensure that benchmark function arguments implement [`ParamRange`]. + // 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;