Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

generation of real benchmark functions for benchmarking v2 #13224

Merged
merged 42 commits into from Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5214c62
function generation with _name working, need to modify signature
sam0x17 Jan 24, 2023
7b84838
WIP
sam0x17 Feb 4, 2023
c7110fb
support custom BenchmarkResult<T> type
sam0x17 Feb 9, 2023
ebd6206
full support for BenchmarkResult<T> on benchmark function defs
sam0x17 Feb 9, 2023
80fef06
support () return type for benchmark function defs that don't use ?
sam0x17 Feb 9, 2023
b812762
uncomment
sam0x17 Feb 9, 2023
e33b00a
fix where clause handling
sam0x17 Feb 9, 2023
da30b03
fix benchmark function call bodies
sam0x17 Feb 9, 2023
3e37c0d
proper parsing of return type
sam0x17 Feb 11, 2023
5de194d
add UI tests for bad return type
sam0x17 Feb 11, 2023
a0d93dc
fix detection of missing last_stmt with defined return type
sam0x17 Feb 11, 2023
4b20793
UI tests covering missing last_stmt
sam0x17 Feb 11, 2023
fc3ccfb
properly detect and complain about empty benchmark function defs
sam0x17 Feb 13, 2023
d409eda
fix missing Comma in Result<T, BenchmarkError> parsing + test
sam0x17 Feb 13, 2023
37f986b
add additional UI test
sam0x17 Feb 13, 2023
fe4f234
allow complex path for BenchmarkResult and BenchmarkError in fn defs
sam0x17 Feb 13, 2023
a773e7f
add UI tests covering complex path for BenchmarkResult, BenchmarkError
sam0x17 Feb 14, 2023
1261327
retain doc comments and attributes
sam0x17 Feb 14, 2023
a2c2274
also add attributes to struct
sam0x17 Feb 14, 2023
550f97a
add docs for benchmark function definition support
sam0x17 Feb 14, 2023
4c41f4e
fix imports on benchmark example
sam0x17 Feb 14, 2023
4e6bcb9
fix issue with unused variables in extrinsic call fn def
sam0x17 Feb 14, 2023
9c10c4c
fix up docs
sam0x17 Feb 15, 2023
2ff8eda
remove support for v2::BenchmarkResult because it was confusing
sam0x17 Feb 15, 2023
460ed3c
fix typo
sam0x17 Feb 15, 2023
16f2e4c
remove ability to use custom T for Result<T, BenchmarkError> in v2
sam0x17 Feb 17, 2023
14de958
use missing call error instead of empty_fn()
sam0x17 Feb 17, 2023
db8a68a
remove unneeded match statement
sam0x17 Feb 18, 2023
edd3dd7
Add a proper QED
sam0x17 Feb 20, 2023
da10ba1
fix other QED
sam0x17 Feb 20, 2023
d8b2e8c
cargo fmt
sam0x17 Feb 21, 2023
1e74d11
add an explicit error for non TypePath as return type
sam0x17 Feb 21, 2023
b3bdbd6
tweak error warning and add a UI test for non TypePath return
sam0x17 Feb 21, 2023
20bf0fc
remove comment
sam0x17 Feb 21, 2023
ef739b2
add docs about T and I generic params
sam0x17 Feb 21, 2023
0ce80e1
improve docs referring to section "below"
sam0x17 Feb 21, 2023
46e6883
pull out return type checking logic into its own function
sam0x17 Feb 21, 2023
27df864
pull out params parsing into its own function
sam0x17 Feb 21, 2023
fb05d87
pull out call_def parsing into its own function
sam0x17 Feb 21, 2023
abe495b
add doc comment for missing_call()
sam0x17 Feb 21, 2023
de6cc9d
replace spaces with tabs
sam0x17 Feb 21, 2023
60e9841
add a result-based example to the benchmarking examples
sam0x17 Feb 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion frame/balances/src/benchmarking.rs
Expand Up @@ -225,7 +225,7 @@ mod benchmarks {
}

#[benchmark]
fn force_unreserve() {
fn force_unreserve() -> Result<(), BenchmarkError> {
let user: T::AccountId = account("user", 0, SEED);
let user_lookup = T::Lookup::unlookup(user.clone());

Expand All @@ -244,6 +244,8 @@ mod benchmarks {

assert!(Balances::<T, I>::reserved_balance(&user).is_zero());
assert_eq!(Balances::<T, I>::free_balance(&user), balance);

Ok(())
}

impl_benchmark_test_suite! {
Expand Down
108 changes: 91 additions & 17 deletions frame/benchmarking/src/lib.rs
Expand Up @@ -114,21 +114,22 @@ pub use v1::*;
/// 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
/// [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`).
///
/// The valid syntax for defining a [Linear](`v2::Linear`)is `Linear<A, B>` where `A`, and `B`
/// The `#[benchmark]` attribute expects a function definition with a blank return type (or a
/// return type compatible with `Result<(), BenchmarkError>`, as discussed below) and zero or
sam0x17 marked this conversation as resolved.
Show resolved Hide resolved
/// 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`).
///
/// The valid syntax for defining a [Linear](`v2::Linear`) is `Linear<A, B>` 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.
/// Anywhere within a benchmark function you may use the generic `T: Config` parameter as well
/// as `I` in the case of an `#[instance_benchmarks]` module. You should not add these to the
/// function signature as this will be handled automatically for you based on whether this is a
/// `#[benchmarks]` or `#[instance_benchmarks]` module and whatever [where clause](#where-clause)
/// you have defined for the the module. You should not manually add any generics to the
/// signature of your benchmark function.
///
/// Also note that the `// setup code` and `// verification code` comments shown above are not
/// required and are included simply for demonstration purposes.
Expand Down Expand Up @@ -189,10 +190,10 @@ pub use v1::*;
///
/// #### `skip_meta`
///
/// Specifies that the benchmarking framework should not analyze the storage keys that
/// Specifies that the benchmarking framework should not analyze the storage keys that the
/// 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.
/// 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
///
Expand Down Expand Up @@ -231,6 +232,79 @@ pub use v1::*;
/// );
/// }
/// ```
///
/// ## Benchmark Function Generation
///
/// The benchmark function definition that you provide is used to automatically create a number
/// of impls and structs required by the benchmarking engine. Additionally, a benchmark
/// function is also generated that resembles the function definition you provide, with a few
/// modifications:
/// 1. The function name is transformed from i.e. `original_name` to `_original_name` so as not
/// to collide with the struct `original_name` that is created for some of the benchmarking
/// engine impls.
/// 2. Appropriate `T: Config` and `I` (if this is an instance benchmark) generics are added to
/// the function automatically during expansion, so you should not add these manually on
/// your function definition (but you may make use of `T` and `I` anywhere within your
/// benchmark function, in any of the three sections (setup, call, verification).
KiChjang marked this conversation as resolved.
Show resolved Hide resolved
/// 3. Arguments such as `u: Linear<10, 100>` are converted to `u: u32` to make the function
/// directly callable.
/// 4. A `verify: bool` param is added as the last argument. Specifying `true` will result in
/// the verification section of your function executing, while a value of `false` will skip
/// verification.
/// 5. If you specify a return type on the function definition, it must conform to the [rules
/// below](#support-for-result-benchmarkerror-and-the--operator), and the last statement of
/// the function definition must resolve to something compatible with `Result<(),
/// BenchmarkError>`.
///
/// The reason we generate an actual function as part of the expansion is to allow the compiler
/// to enforce several constraints that would otherwise be difficult to enforce and to reduce
/// developer confusion (especially regarding the use of the `?` operator, as covered below).
///
/// Note that any attributes, comments, and doc comments attached to your benchmark function
/// definition are also carried over onto the resulting benchmark function and the struct for
/// that benchmark. As a result you should be careful about what attributes you attach here as
/// they will be replicated in multiple places.
///
/// ### Support for `Result<(), BenchmarkError>` and the `?` operator
///
/// You may optionally specify `Result<(), BenchmarkError>` as the return type of your
/// benchmark function definition. If you do so, you must return a compatible `Result<(),
/// BenchmarkError>` as the *last statement* of your benchmark function definition. You may
/// also use the `?` operator throughout your benchmark function definition if you choose to
/// follow this route. See the example below:
///
/// ```ignore
/// #![cfg(feature = "runtime-benchmarks")]
sam0x17 marked this conversation as resolved.
Show resolved Hide resolved
///
/// use super::{mock_helpers::*, Pallet as MyPallet};
/// use frame_benchmarking::v2::*;
///
/// #[benchmarks]
/// mod benchmarks {
/// use super::*;
///
/// #[benchmark]
/// fn bench_name(x: Linear<5, 25>) -> Result<(), BenchmarkError> {
/// // setup code
/// let z = x + 4;
/// let caller = whitelisted_caller();
///
/// // note we can make use of the ? operator here because of the return type
/// something(z)?;
///
/// #[extrinsic_call]
/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments);
///
/// // verification code
/// assert_eq!(MyPallet::<T>::my_var(), z);
///
/// // we must return a valid `Result<(), BenchmarkError>` as the last line of our benchmark
/// // function definition. This line is not included as part of the verification code that
/// // appears above it.
/// Ok(())
/// }
/// }
/// ```
pub mod v2 {
pub use super::*;
pub use frame_support_procedural::{
Expand All @@ -240,7 +314,7 @@ pub mod v2 {
// Used in #[benchmark] implementation to ensure that benchmark function arguments
// implement [`ParamRange`].
#[doc(hidden)]
pub use static_assertions::assert_impl_all;
pub use static_assertions::{assert_impl_all, assert_type_eq_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
Expand Down
20 changes: 14 additions & 6 deletions frame/examples/basic/src/benchmarking.rs
Expand Up @@ -21,11 +21,7 @@
#![cfg(feature = "runtime-benchmarks")]

use crate::*;
use frame_benchmarking::v1::{
impl_benchmark_test_suite,
v2::{benchmarks, Linear},
whitelisted_caller,
};
use frame_benchmarking::v2::*;
sam0x17 marked this conversation as resolved.
Show resolved Hide resolved
use frame_system::RawOrigin;

// To actually run this benchmark on pallet-example-basic, we need to put this pallet into the
Expand Down Expand Up @@ -55,19 +51,31 @@ mod benchmarks {
assert_eq!(Pallet::<T>::dummy(), Some(value))
}

// An example method that returns a Result that can be called within a benchmark
fn example_result_method() -> Result<(), BenchmarkError> {
Ok(())
}

// This will measure the execution time of `accumulate_dummy`.
// The benchmark execution phase is shorthanded. When the name of the benchmark case is the same
// as the extrinsic call. `_(...)` is used to represent the extrinsic name.
// The benchmark verification phase is omitted.
#[benchmark]
fn accumulate_dummy() {
fn accumulate_dummy() -> Result<(), BenchmarkError> {
let value = 1000u32.into();
// The caller account is whitelisted for DB reads/write by the benchmarking macro.
let caller: T::AccountId = whitelisted_caller();

// an example of calling something result-based within a benchmark using the ? operator
// this necessitates specifying the `Result<(), BenchmarkError>` return type
example_result_method()?;

// You can use `_` if the name of the Call matches the benchmark name.
#[extrinsic_call]
_(RawOrigin::Signed(caller), value);

// need this to be compatible with the return type
Ok(())
}

/// You can write helper functions in here since its a normal Rust module.
Expand Down