From 159de4075e24164b584de2228765a52951803dd6 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 13 Dec 2022 13:26:44 -0500 Subject: [PATCH 001/146] add stub for new benchmark macro --- frame/support/procedural/src/benchmark.rs | 6 ++++++ frame/support/procedural/src/lib.rs | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 frame/support/procedural/src/benchmark.rs diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs new file mode 100644 index 0000000000000..ed5dbc201613d --- /dev/null +++ b/frame/support/procedural/src/benchmark.rs @@ -0,0 +1,6 @@ +use proc_macro::TokenStream; +use quote::quote; + +pub fn benchmark(_tokens: TokenStream) -> TokenStream { + quote!("").into() +} diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 41dbc4ee9592c..a8734107ddb07 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -19,6 +19,7 @@ #![recursion_limit = "512"] +mod benchmark; mod clone_no_bound; mod construct_runtime; mod crate_version; @@ -479,6 +480,11 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) } +#[proc_macro_attribute] +pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { + benchmark::benchmark(tokens) +} + /// Execute the annotated function in a new storage transaction. /// /// The return type of the annotated function must be `Result`. All changes to storage performed From fabc0cdc13939efdce0ceec3b4e6006cde34b40e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 14 Dec 2022 14:21:24 -0500 Subject: [PATCH 002/146] benchmark syntax --- frame/balances/src/benchmarking.rs | 11 +++++++++++ frame/support/src/lib.rs | 1 + 2 files changed, 12 insertions(+) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 206adba0f044b..f09c85ee9dcfe 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -31,6 +31,17 @@ const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; +#[frame_support::benchmark] +fn bench_name(x: LinearComponent<0, MAX_X>, y: LinearComponent<0, MAX_Y>) { + // Setup code + let z = x + y; + let caller = whitelisted_caller(); + // The extrinsic call. + extrinsic_name(z, other_arguments); + // Post condition verification + assert_eq!(MyPallet::::my_var(), == z); +} + benchmarks_instance_pallet! { // Benchmark `transfer` extrinsic with the worst possible conditions: // * Transfer will kill the sender account. diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 902893972f0b1..c5e02a0e26c46 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2747,3 +2747,4 @@ pub mod pallet_macros { // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); +pub use frame_support_procedural::benchmark; From 04d652c3ed003ece92423ce34a7adc6f0ac0f077 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 14 Dec 2022 14:39:35 -0500 Subject: [PATCH 003/146] add #[extrinsic call] separator --- frame/balances/src/benchmarking.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index f09c85ee9dcfe..1509174d20f9b 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -37,6 +37,7 @@ fn bench_name(x: LinearComponent<0, MAX_X>, y: LinearComponent<0, MAX_Y>) { let z = x + y; let caller = whitelisted_caller(); // The extrinsic call. + #[extrinsic_call] extrinsic_name(z, other_arguments); // Post condition verification assert_eq!(MyPallet::::my_var(), == z); From c6c8eaecd7c2170269a46a178e62c9470d0fe6c4 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 14 Dec 2022 14:52:39 -0500 Subject: [PATCH 004/146] parse #[benchmark] item as a function --- frame/support/procedural/src/benchmark.rs | 4 +++- frame/support/procedural/src/lib.rs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index ed5dbc201613d..497c501dd6bed 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,6 +1,8 @@ use proc_macro::TokenStream; use quote::quote; +use syn::{parse_macro_input, ItemFn}; -pub fn benchmark(_tokens: TokenStream) -> TokenStream { +pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { + let _item_fn = parse_macro_input!(tokens as ItemFn); quote!("").into() } diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index a8734107ddb07..19919c3a3951f 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -481,8 +481,8 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { } #[proc_macro_attribute] -pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { - benchmark::benchmark(tokens) +pub fn benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStream { + benchmark::benchmark(attrs, tokens) } /// Execute the annotated function in a new storage transaction. From 940a1d9c43b94b877e527272ed84fa305cf0033c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 15 Dec 2022 15:31:46 -0500 Subject: [PATCH 005/146] proper emission of error when #[extrinsic_call] annotation is missing --- frame/balances/src/benchmarking.rs | 12 ----- frame/balances/src/lib.rs | 12 +++++ frame/support/procedural/src/benchmark.rs | 61 +++++++++++++++++++++-- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 1509174d20f9b..206adba0f044b 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -31,18 +31,6 @@ const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; -#[frame_support::benchmark] -fn bench_name(x: LinearComponent<0, MAX_X>, y: LinearComponent<0, MAX_Y>) { - // Setup code - let z = x + y; - let caller = whitelisted_caller(); - // The extrinsic call. - #[extrinsic_call] - extrinsic_name(z, other_arguments); - // Post condition verification - assert_eq!(MyPallet::::my_var(), == z); -} - benchmarks_instance_pallet! { // Benchmark `transfer` extrinsic with the worst possible conditions: // * Transfer will kill the sender account. diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 99d77a3e73361..bdb2390023ca5 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -2207,3 +2207,15 @@ where Self::update_locks(who, &locks[..]); } } + +#[frame_support::benchmark] +fn bench_name(x: LinearComponent<0, MAX_X>, y: LinearComponent<0, MAX_Y>) { + // Setup code + let z = x + y; + let caller = whitelisted_caller(); + // The extrinsic call. + #[extrinsic_call] + extrinsic_name(z, other_arguments); + // Post condition verification + assert_eq!(MyPallet::::my_var(), == z); +} diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 497c501dd6bed..2b09358e0494a 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,8 +1,61 @@ use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, ItemFn}; +use quote::{quote, ToTokens}; +use syn::{parse_macro_input, spanned::Spanned, Expr, ExprCall, ItemFn, Stmt}; + +mod keywords { + syn::custom_keyword!(extrinsic_call); +} + +fn emit_error + Clone, S: Into>(item: &T, message: S) -> TokenStream { + let item = Into::::into(item.clone()); + let message = Into::::into(message); + let span = proc_macro2::TokenStream::from(item).span(); + return syn::Error::new(span, message).to_compile_error().into() +} + +struct BenchmarkDef { + setup_stmts: Vec, + extrinsic_call: ExprCall, + verify_stmts: Vec, +} + +impl BenchmarkDef { + pub fn from(item_fn: &ItemFn) -> Option { + let mut i = 0; + for child in &item_fn.block.stmts { + if let Stmt::Semi(Expr::Call(fn_call), _) = child { + for attr in &fn_call.attrs { + if let Some(segment) = attr.path.segments.last() { + if let Ok(_) = syn::parse::( + segment.ident.to_token_stream().into(), + ) { + //let setup_stmts = &item_fn.block.stmts[0..i]; + return Some(BenchmarkDef { + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + extrinsic_call: fn_call.clone(), + verify_stmts: Vec::from( + &item_fn.block.stmts[i..item_fn.block.stmts.len()], + ), + }) + } + } + } + } + i += 1; + } + return None + } +} pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { - let _item_fn = parse_macro_input!(tokens as ItemFn); - quote!("").into() + let item_fn = parse_macro_input!(tokens as ItemFn); + if let Some(_benchmark_def) = BenchmarkDef::from(&item_fn) { + // todo + } else { + return emit_error( + &item_fn.block.to_token_stream(), + "Missing #[extrinsic_call] annotation in benchmark function body.", + ) + } + return quote!().into() } From ece2c1d1aa2d66464c2116aef09b543a3dc5a9ba Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 15 Dec 2022 15:33:04 -0500 Subject: [PATCH 006/146] clean up --- frame/support/procedural/src/benchmark.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 2b09358e0494a..c1ec2ea6a6144 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -10,7 +10,7 @@ fn emit_error + Clone, S: Into>(item: &T, message: let item = Into::::into(item.clone()); let message = Into::::into(message); let span = proc_macro2::TokenStream::from(item).span(); - return syn::Error::new(span, message).to_compile_error().into() + return syn::Error::new(span, message).to_compile_error().into(); } struct BenchmarkDef { @@ -29,21 +29,20 @@ impl BenchmarkDef { if let Ok(_) = syn::parse::( segment.ident.to_token_stream().into(), ) { - //let setup_stmts = &item_fn.block.stmts[0..i]; return Some(BenchmarkDef { setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), extrinsic_call: fn_call.clone(), verify_stmts: Vec::from( &item_fn.block.stmts[i..item_fn.block.stmts.len()], ), - }) + }); } } } } i += 1; } - return None + return None; } } @@ -55,7 +54,7 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { return emit_error( &item_fn.block.to_token_stream(), "Missing #[extrinsic_call] annotation in benchmark function body.", - ) + ); } - return quote!().into() + return quote!().into(); } From 7d0a4679b4813f5b3bd5c58bec38f42c12d82446 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 16 Dec 2022 12:40:29 -0500 Subject: [PATCH 007/146] enclosing module via benchmarks! { } working --- frame/balances/src/lib.rs | 12 -------- frame/grandpa/src/benchmarking_alt.rs | 20 +++++++++++++ frame/grandpa/src/lib.rs | 1 + frame/support/procedural/src/benchmark.rs | 36 +++++++++++++++++++++-- frame/support/procedural/src/lib.rs | 5 ++++ frame/support/src/lib.rs | 3 +- 6 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 frame/grandpa/src/benchmarking_alt.rs diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index bdb2390023ca5..99d77a3e73361 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -2207,15 +2207,3 @@ where Self::update_locks(who, &locks[..]); } } - -#[frame_support::benchmark] -fn bench_name(x: LinearComponent<0, MAX_X>, y: LinearComponent<0, MAX_Y>) { - // Setup code - let z = x + y; - let caller = whitelisted_caller(); - // The extrinsic call. - #[extrinsic_call] - extrinsic_name(z, other_arguments); - // Post condition verification - assert_eq!(MyPallet::::my_var(), == z); -} diff --git a/frame/grandpa/src/benchmarking_alt.rs b/frame/grandpa/src/benchmarking_alt.rs new file mode 100644 index 0000000000000..b010db49423e2 --- /dev/null +++ b/frame/grandpa/src/benchmarking_alt.rs @@ -0,0 +1,20 @@ +#![cfg(any(feature = "runtime-benchmarks", test))] + +use super::{Pallet as Grandpa, *}; +use frame_benchmarking::benchmarks; +use frame_system::RawOrigin; +use sp_core::H256; + +frame_support::benchmarks! { + #[frame_support::benchmark] + fn bench_name(x: LinearComponent<0, MAX_X>, y: LinearComponent<0, MAX_Y>) { + // Setup code + let z = x + y; + let caller = whitelisted_caller(); + // The extrinsic call. + #[extrinsic_call] + extrinsic_name(z, other_arguments); + // Post condition verification + assert_eq!(MyPallet::::my_var(), == z); + } +} diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index aa09b445c6bdd..1948fd4d23bd5 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -58,6 +58,7 @@ pub mod migrations; #[cfg(any(feature = "runtime-benchmarks", test))] mod benchmarking; +mod benchmarking_alt; #[cfg(all(feature = "std", test))] mod mock; #[cfg(all(feature = "std", test))] diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index c1ec2ea6a6144..8d4e81cc2bbe7 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,6 +1,11 @@ use proc_macro::TokenStream; use quote::{quote, ToTokens}; -use syn::{parse_macro_input, spanned::Spanned, Expr, ExprCall, ItemFn, Stmt}; +use syn::{ + parse::{Parse, ParseStream}, + parse_macro_input, + spanned::Spanned, + Block, Expr, ExprCall, Item, ItemFn, ItemMod, Stmt, +}; mod keywords { syn::custom_keyword!(extrinsic_call); @@ -46,10 +51,37 @@ impl BenchmarkDef { } } +struct BareBlock { + stmts: Vec, +} + +impl Parse for BareBlock { + fn parse(input: ParseStream) -> syn::Result { + match Block::parse_within(input) { + Ok(stmts) => Ok(BareBlock { stmts }), + Err(e) => Err(e), + } + } +} + +pub fn benchmarks(tokens: TokenStream) -> TokenStream { + let block = parse_macro_input!(tokens as BareBlock); + let contents = block.stmts; + quote! { + #[cfg(any(feature = "runtime-benchmarks", test))] + mod benchmarking { + #(#contents) + * + } + } + .into() +} + pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let item_fn = parse_macro_input!(tokens as ItemFn); if let Some(_benchmark_def) = BenchmarkDef::from(&item_fn) { - // todo + println!("benchmark def found!"); + // todo } else { return emit_error( &item_fn.block.to_token_stream(), diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 19919c3a3951f..9e777e284e222 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -480,6 +480,11 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) } +#[proc_macro] +pub fn benchmarks(tokens: TokenStream) -> TokenStream { + benchmark::benchmarks(tokens) +} + #[proc_macro_attribute] pub fn benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStream { benchmark::benchmark(attrs, tokens) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index c5e02a0e26c46..035fabeaee856 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -694,7 +694,7 @@ pub use frame_support_procedural::crate_to_crate_version; #[macro_export] macro_rules! fail { ( $y:expr ) => {{ - return Err($y.into()) + return Err($y.into()); }}; } @@ -2748,3 +2748,4 @@ pub mod pallet_macros { // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); pub use frame_support_procedural::benchmark; +pub use frame_support_procedural::benchmarks; From ad8260cef994e52d718491f3c95c1d0988868ed3 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 16 Dec 2022 13:04:40 -0500 Subject: [PATCH 008/146] use an attribute macro on the module instead of benchmarks! { } --- frame/grandpa/src/benchmarking_alt.rs | 3 ++- frame/support/procedural/src/benchmark.rs | 17 +++++++++++++---- frame/support/procedural/src/lib.rs | 6 +++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/frame/grandpa/src/benchmarking_alt.rs b/frame/grandpa/src/benchmarking_alt.rs index b010db49423e2..06cea05424265 100644 --- a/frame/grandpa/src/benchmarking_alt.rs +++ b/frame/grandpa/src/benchmarking_alt.rs @@ -5,7 +5,8 @@ use frame_benchmarking::benchmarks; use frame_system::RawOrigin; use sp_core::H256; -frame_support::benchmarks! { +#[frame_support::benchmarks] +mod my_benchmarks { #[frame_support::benchmark] fn bench_name(x: LinearComponent<0, MAX_X>, y: LinearComponent<0, MAX_Y>) { // Setup code diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 8d4e81cc2bbe7..b76fbf58d0aca 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -64,12 +64,21 @@ impl Parse for BareBlock { } } -pub fn benchmarks(tokens: TokenStream) -> TokenStream { - let block = parse_macro_input!(tokens as BareBlock); - let contents = block.stmts; +pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { + let item_mod = parse_macro_input!(tokens as ItemMod); + let contents = match item_mod.content { + Some(content) => content.1, + None => { + return emit_error( + &item_mod.to_token_stream(), + "#[frame_support::benchmarks] can only be applied to a non-empty module.", + ) + }, + }; + let mod_ident = item_mod.ident; quote! { #[cfg(any(feature = "runtime-benchmarks", test))] - mod benchmarking { + mod #mod_ident { #(#contents) * } diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 9e777e284e222..851cb078c6dfc 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -480,9 +480,9 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) } -#[proc_macro] -pub fn benchmarks(tokens: TokenStream) -> TokenStream { - benchmark::benchmarks(tokens) +#[proc_macro_attribute] +pub fn benchmarks(attrs: TokenStream, tokens: TokenStream) -> TokenStream { + benchmark::benchmarks(attrs, tokens) } #[proc_macro_attribute] From 7ab18a96e1a70318a8b539249cd9ad8edfbc69d7 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 16 Dec 2022 13:10:41 -0500 Subject: [PATCH 009/146] cargo fmt --- frame/support/procedural/src/benchmark.rs | 15 +++++++-------- frame/support/src/lib.rs | 6 +++--- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index b76fbf58d0aca..b87f65713f305 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -15,7 +15,7 @@ fn emit_error + Clone, S: Into>(item: &T, message: let item = Into::::into(item.clone()); let message = Into::::into(message); let span = proc_macro2::TokenStream::from(item).span(); - return syn::Error::new(span, message).to_compile_error().into(); + return syn::Error::new(span, message).to_compile_error().into() } struct BenchmarkDef { @@ -40,14 +40,14 @@ impl BenchmarkDef { verify_stmts: Vec::from( &item_fn.block.stmts[i..item_fn.block.stmts.len()], ), - }); + }) } } } } i += 1; } - return None; + return None } } @@ -68,12 +68,11 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let item_mod = parse_macro_input!(tokens as ItemMod); let contents = match item_mod.content { Some(content) => content.1, - None => { + None => return emit_error( &item_mod.to_token_stream(), "#[frame_support::benchmarks] can only be applied to a non-empty module.", - ) - }, + ), }; let mod_ident = item_mod.ident; quote! { @@ -95,7 +94,7 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { return emit_error( &item_fn.block.to_token_stream(), "Missing #[extrinsic_call] annotation in benchmark function body.", - ); + ) } - return quote!().into(); + return quote!().into() } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 035fabeaee856..d6a88075ba02c 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -694,7 +694,7 @@ pub use frame_support_procedural::crate_to_crate_version; #[macro_export] macro_rules! fail { ( $y:expr ) => {{ - return Err($y.into()); + return Err($y.into()) }}; } @@ -2747,5 +2747,5 @@ pub mod pallet_macros { // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); -pub use frame_support_procedural::benchmark; -pub use frame_support_procedural::benchmarks; + +pub use frame_support_procedural::{benchmark, benchmarks}; From d4efeaf7896ebceecc49e5e57da2d01f42a3420e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 16 Dec 2022 14:56:19 -0500 Subject: [PATCH 010/146] working component implementation --- frame/grandpa/src/benchmarking_alt.rs | 50 ++++++++++++++++------- frame/support/procedural/src/benchmark.rs | 21 +++++++++- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/frame/grandpa/src/benchmarking_alt.rs b/frame/grandpa/src/benchmarking_alt.rs index 06cea05424265..f90a81ec5276c 100644 --- a/frame/grandpa/src/benchmarking_alt.rs +++ b/frame/grandpa/src/benchmarking_alt.rs @@ -1,21 +1,43 @@ #![cfg(any(feature = "runtime-benchmarks", test))] use super::{Pallet as Grandpa, *}; -use frame_benchmarking::benchmarks; use frame_system::RawOrigin; use sp_core::H256; -#[frame_support::benchmarks] -mod my_benchmarks { - #[frame_support::benchmark] - fn bench_name(x: LinearComponent<0, MAX_X>, y: LinearComponent<0, MAX_Y>) { - // Setup code - let z = x + y; - let caller = whitelisted_caller(); - // The extrinsic call. - #[extrinsic_call] - extrinsic_name(z, other_arguments); - // Post condition verification - assert_eq!(MyPallet::::my_var(), == z); - } +#[frame_support::benchmark] +fn bench_name(x: LinearComponent<0, 1>) { + // NOTE: generated with the test below `test_generate_equivocation_report_blob`. + // the output should be deterministic since the keys we use are static. + // with the current benchmark setup it is not possible to generate this + // programatically from the benchmark setup. + const EQUIVOCATION_PROOF_BLOB: [u8; 257] = [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 136, 220, 52, 23, + 213, 5, 142, 196, 180, 80, 62, 12, 18, 234, 26, 10, 137, 190, 32, + 15, 233, 137, 34, 66, 61, 67, 52, 1, 79, 166, 176, 238, 207, 48, + 195, 55, 171, 225, 252, 130, 161, 56, 151, 29, 193, 32, 25, 157, + 249, 39, 80, 193, 214, 96, 167, 147, 25, 130, 45, 42, 64, 208, 182, + 164, 10, 0, 0, 0, 0, 0, 0, 0, 234, 236, 231, 45, 70, 171, 135, 246, + 136, 153, 38, 167, 91, 134, 150, 242, 215, 83, 56, 238, 16, 119, 55, + 170, 32, 69, 255, 248, 164, 20, 57, 50, 122, 115, 135, 96, 80, 203, + 131, 232, 73, 23, 149, 86, 174, 59, 193, 92, 121, 76, 154, 211, 44, + 96, 10, 84, 159, 133, 211, 56, 103, 0, 59, 2, 96, 20, 69, 2, 32, + 179, 16, 184, 108, 76, 215, 64, 195, 78, 143, 73, 177, 139, 20, 144, + 98, 231, 41, 117, 255, 220, 115, 41, 59, 27, 75, 56, 10, 0, 0, 0, 0, + 0, 0, 0, 128, 179, 250, 48, 211, 76, 10, 70, 74, 230, 219, 139, 96, + 78, 88, 112, 33, 170, 44, 184, 59, 200, 155, 143, 128, 40, 222, 179, + 210, 190, 84, 16, 182, 21, 34, 94, 28, 193, 163, 226, 51, 251, 134, + 233, 187, 121, 63, 157, 240, 165, 203, 92, 16, 146, 120, 190, 229, + 251, 129, 29, 45, 32, 29, 6 + ]; + + let equivocation_proof1: sp_finality_grandpa::EquivocationProof = + Decode::decode(&mut &EQUIVOCATION_PROOF_BLOB[..]).unwrap(); + + let equivocation_proof2 = equivocation_proof1.clone(); + + #[extrinsic_call] + sp_finality_grandpa::check_equivocation_proof(equivocation_proof1); + + // Post condition verification + assert!(sp_finality_grandpa::check_equivocation_proof(equivocation_proof2)); } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index b87f65713f305..06469ab8cf0d7 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -96,5 +96,24 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { "Missing #[extrinsic_call] annotation in benchmark function body.", ) } - return quote!().into() + let name = item_fn.sig.ident; + let krate = quote!(::frame_benchmarking); + let params = vec![quote!(x, 0, 1)]; + quote! { + #[allow(non_camel_case_types)] + struct #name; + + #[allow(unused_variables)] + impl ::frame_benchmarking::BenchmarkingSetup + for #name { + fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { + #krate::vec! [ + #( + (#krate::BenchmarkParameter::#params) + ),* + ] + } + } + } + .into() } From 968a756e8e6b604f3833d28370eb1e51a16668f1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 16 Dec 2022 16:02:24 -0500 Subject: [PATCH 011/146] WIP --- frame/grandpa/src/benchmarking_alt.rs | 56 ++++++++---------- frame/support/procedural/src/benchmark.rs | 72 +++++++++++++++++++---- 2 files changed, 86 insertions(+), 42 deletions(-) diff --git a/frame/grandpa/src/benchmarking_alt.rs b/frame/grandpa/src/benchmarking_alt.rs index f90a81ec5276c..c24137dfc4fd3 100644 --- a/frame/grandpa/src/benchmarking_alt.rs +++ b/frame/grandpa/src/benchmarking_alt.rs @@ -6,38 +6,34 @@ use sp_core::H256; #[frame_support::benchmark] fn bench_name(x: LinearComponent<0, 1>) { - // NOTE: generated with the test below `test_generate_equivocation_report_blob`. - // the output should be deterministic since the keys we use are static. - // with the current benchmark setup it is not possible to generate this - // programatically from the benchmark setup. - const EQUIVOCATION_PROOF_BLOB: [u8; 257] = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 136, 220, 52, 23, - 213, 5, 142, 196, 180, 80, 62, 12, 18, 234, 26, 10, 137, 190, 32, - 15, 233, 137, 34, 66, 61, 67, 52, 1, 79, 166, 176, 238, 207, 48, - 195, 55, 171, 225, 252, 130, 161, 56, 151, 29, 193, 32, 25, 157, - 249, 39, 80, 193, 214, 96, 167, 147, 25, 130, 45, 42, 64, 208, 182, - 164, 10, 0, 0, 0, 0, 0, 0, 0, 234, 236, 231, 45, 70, 171, 135, 246, - 136, 153, 38, 167, 91, 134, 150, 242, 215, 83, 56, 238, 16, 119, 55, - 170, 32, 69, 255, 248, 164, 20, 57, 50, 122, 115, 135, 96, 80, 203, - 131, 232, 73, 23, 149, 86, 174, 59, 193, 92, 121, 76, 154, 211, 44, - 96, 10, 84, 159, 133, 211, 56, 103, 0, 59, 2, 96, 20, 69, 2, 32, - 179, 16, 184, 108, 76, 215, 64, 195, 78, 143, 73, 177, 139, 20, 144, - 98, 231, 41, 117, 255, 220, 115, 41, 59, 27, 75, 56, 10, 0, 0, 0, 0, - 0, 0, 0, 128, 179, 250, 48, 211, 76, 10, 70, 74, 230, 219, 139, 96, - 78, 88, 112, 33, 170, 44, 184, 59, 200, 155, 143, 128, 40, 222, 179, - 210, 190, 84, 16, 182, 21, 34, 94, 28, 193, 163, 226, 51, 251, 134, - 233, 187, 121, 63, 157, 240, 165, 203, 92, 16, 146, 120, 190, 229, - 251, 129, 29, 45, 32, 29, 6 - ]; + // NOTE: generated with the test below `test_generate_equivocation_report_blob`. + // the output should be deterministic since the keys we use are static. + // with the current benchmark setup it is not possible to generate this + // programatically from the benchmark setup. + const EQUIVOCATION_PROOF_BLOB: [u8; 257] = [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 136, 220, 52, 23, 213, 5, 142, 196, 180, + 80, 62, 12, 18, 234, 26, 10, 137, 190, 32, 15, 233, 137, 34, 66, 61, 67, 52, 1, 79, 166, + 176, 238, 207, 48, 195, 55, 171, 225, 252, 130, 161, 56, 151, 29, 193, 32, 25, 157, 249, + 39, 80, 193, 214, 96, 167, 147, 25, 130, 45, 42, 64, 208, 182, 164, 10, 0, 0, 0, 0, 0, 0, + 0, 234, 236, 231, 45, 70, 171, 135, 246, 136, 153, 38, 167, 91, 134, 150, 242, 215, 83, 56, + 238, 16, 119, 55, 170, 32, 69, 255, 248, 164, 20, 57, 50, 122, 115, 135, 96, 80, 203, 131, + 232, 73, 23, 149, 86, 174, 59, 193, 92, 121, 76, 154, 211, 44, 96, 10, 84, 159, 133, 211, + 56, 103, 0, 59, 2, 96, 20, 69, 2, 32, 179, 16, 184, 108, 76, 215, 64, 195, 78, 143, 73, + 177, 139, 20, 144, 98, 231, 41, 117, 255, 220, 115, 41, 59, 27, 75, 56, 10, 0, 0, 0, 0, 0, + 0, 0, 128, 179, 250, 48, 211, 76, 10, 70, 74, 230, 219, 139, 96, 78, 88, 112, 33, 170, 44, + 184, 59, 200, 155, 143, 128, 40, 222, 179, 210, 190, 84, 16, 182, 21, 34, 94, 28, 193, 163, + 226, 51, 251, 134, 233, 187, 121, 63, 157, 240, 165, 203, 92, 16, 146, 120, 190, 229, 251, + 129, 29, 45, 32, 29, 6, + ]; - let equivocation_proof1: sp_finality_grandpa::EquivocationProof = - Decode::decode(&mut &EQUIVOCATION_PROOF_BLOB[..]).unwrap(); + let equivocation_proof1: sp_finality_grandpa::EquivocationProof = + Decode::decode(&mut &EQUIVOCATION_PROOF_BLOB[..]).unwrap(); - let equivocation_proof2 = equivocation_proof1.clone(); + let equivocation_proof2 = equivocation_proof1.clone(); - #[extrinsic_call] - sp_finality_grandpa::check_equivocation_proof(equivocation_proof1); + #[extrinsic_call] + sp_finality_grandpa::check_equivocation_proof(equivocation_proof1); - // Post condition verification - assert!(sp_finality_grandpa::check_equivocation_proof(equivocation_proof2)); + // Post condition verification + assert!(sp_finality_grandpa::check_equivocation_proof(equivocation_proof2)); } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 06469ab8cf0d7..2cf8741736e24 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -4,6 +4,7 @@ use syn::{ parse::{Parse, ParseStream}, parse_macro_input, spanned::Spanned, + token::Token, Block, Expr, ExprCall, Item, ItemFn, ItemMod, Stmt, }; @@ -20,7 +21,8 @@ fn emit_error + Clone, S: Into>(item: &T, message: struct BenchmarkDef { setup_stmts: Vec, - extrinsic_call: ExprCall, + extrinsic_call_stmt: Stmt, + extrinsic_call_fn: ExprCall, verify_stmts: Vec, } @@ -28,15 +30,22 @@ impl BenchmarkDef { pub fn from(item_fn: &ItemFn) -> Option { let mut i = 0; for child in &item_fn.block.stmts { - if let Stmt::Semi(Expr::Call(fn_call), _) = child { + if let Stmt::Semi(Expr::Call(fn_call), token) = child { + let i = 0; for attr in &fn_call.attrs { if let Some(segment) = attr.path.segments.last() { if let Ok(_) = syn::parse::( segment.ident.to_token_stream().into(), ) { + let mut fn_call_copy = fn_call.clone(); + fn_call_copy.attrs.pop(); // consume #[extrinsic call] return Some(BenchmarkDef { setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - extrinsic_call: fn_call.clone(), + extrinsic_call_stmt: Stmt::Semi( + Expr::Call(fn_call.clone()), + token.clone(), + ), + extrinsic_call_fn: fn_call.clone(), verify_stmts: Vec::from( &item_fn.block.stmts[i..item_fn.block.stmts.len()], ), @@ -87,18 +96,21 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let item_fn = parse_macro_input!(tokens as ItemFn); - if let Some(_benchmark_def) = BenchmarkDef::from(&item_fn) { - println!("benchmark def found!"); - // todo - } else { - return emit_error( - &item_fn.block.to_token_stream(), - "Missing #[extrinsic_call] annotation in benchmark function body.", - ) - } + let mut benchmark_def = match BenchmarkDef::from(&item_fn) { + Some(def) => def, + None => + return emit_error( + &item_fn.block.to_token_stream(), + "Missing #[extrinsic_call] annotation in benchmark function body.", + ), + }; let name = item_fn.sig.ident; let krate = quote!(::frame_benchmarking); + let setup_stmts = benchmark_def.setup_stmts; + let extrinsic_call_stmt = benchmark_def.extrinsic_call_stmt; + let verify_stmts = benchmark_def.verify_stmts; let params = vec![quote!(x, 0, 1)]; + let param_names = vec![quote!(x)]; quote! { #[allow(non_camel_case_types)] struct #name; @@ -113,6 +125,42 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { ),* ] } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<#krate::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { + #( + // prepare instance #param_names + let #param_names = components.iter() + .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) + .ok_or("Could not find component during benchmark preparation.")? + .1; + )* + + // TODO: figure out parameter parsing: + // $( + // let $pre_id : $pre_ty = $pre_ex; + // )* + // $( $param_instancer ; )* + // $( $post )* + + // benchmark setup code (stuff before #[extrinsic_call]) + #( + #setup_stmts + )* + + Ok(#krate::Box::new(move || -> Result<(), #krate::BenchmarkError> { + #extrinsic_call_stmt + if verify { + #( + #verify_stmts + )* + } + Ok(()) + })) + } } } .into() From 9ae168bc65904ad826822a4c35584676f55d24dd Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 16 Dec 2022 22:01:06 -0500 Subject: [PATCH 012/146] working --- frame/support/procedural/src/benchmark.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 2cf8741736e24..963a8da741f35 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -28,30 +28,31 @@ struct BenchmarkDef { impl BenchmarkDef { pub fn from(item_fn: &ItemFn) -> Option { - let mut i = 0; + let mut i = 0; // index of child for child in &item_fn.block.stmts { if let Stmt::Semi(Expr::Call(fn_call), token) = child { - let i = 0; + let mut k = 0; // index of attr for attr in &fn_call.attrs { if let Some(segment) = attr.path.segments.last() { if let Ok(_) = syn::parse::( segment.ident.to_token_stream().into(), ) { let mut fn_call_copy = fn_call.clone(); - fn_call_copy.attrs.pop(); // consume #[extrinsic call] + fn_call_copy.attrs.remove(k); // consume #[extrinsic call] return Some(BenchmarkDef { setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), extrinsic_call_stmt: Stmt::Semi( - Expr::Call(fn_call.clone()), + Expr::Call(fn_call_copy.clone()), token.clone(), ), - extrinsic_call_fn: fn_call.clone(), + extrinsic_call_fn: fn_call_copy, verify_stmts: Vec::from( - &item_fn.block.stmts[i..item_fn.block.stmts.len()], + &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], ), }) } } + k += 1; } } i += 1; From d976fbf80780c4d3f4021ab9fe353347618160f9 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 18 Dec 2022 00:48:29 -0500 Subject: [PATCH 013/146] add syntax for Linear --- frame/grandpa/src/benchmarking_alt.rs | 5 +++-- frame/support/src/lib.rs | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frame/grandpa/src/benchmarking_alt.rs b/frame/grandpa/src/benchmarking_alt.rs index c24137dfc4fd3..f475ec730dc69 100644 --- a/frame/grandpa/src/benchmarking_alt.rs +++ b/frame/grandpa/src/benchmarking_alt.rs @@ -1,11 +1,12 @@ #![cfg(any(feature = "runtime-benchmarks", test))] use super::{Pallet as Grandpa, *}; +use frame_support::{benchmark, Linear}; use frame_system::RawOrigin; use sp_core::H256; -#[frame_support::benchmark] -fn bench_name(x: LinearComponent<0, 1>) { +#[benchmark] +fn bench_name(x: Linear<0, 1>) { // NOTE: generated with the test below `test_generate_equivocation_report_blob`. // the output should be deterministic since the keys we use are static. // with the current benchmark setup it is not possible to generate this diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index d6a88075ba02c..7680a066e7b27 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2749,3 +2749,5 @@ pub mod pallet_macros { sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); pub use frame_support_procedural::{benchmark, benchmarks}; + +pub struct Linear; From 9fd9dc9608e3139d7c32db32cfb5e9b7cde3bb33 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 18 Dec 2022 02:36:30 -0500 Subject: [PATCH 014/146] parsing of param ranges (still need to build tuple though) --- Cargo.lock | 1 + frame/support/Cargo.toml | 1 + frame/support/procedural/src/benchmark.rs | 29 ++++++++++++++++++++--- frame/support/src/lib.rs | 21 ++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68c144b3f177e..ec5d86b977116 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2314,6 +2314,7 @@ dependencies = [ "sp-std", "sp-tracing", "sp-weights", + "static_assertions", "tt-call", ] diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 4945b5ab915f9..3fcb16207453f 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -27,6 +27,7 @@ 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 963a8da741f35..6128547e27e28 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -4,8 +4,7 @@ use syn::{ parse::{Parse, ParseStream}, parse_macro_input, spanned::Spanned, - token::Token, - Block, Expr, ExprCall, Item, ItemFn, ItemMod, Stmt, + Block, Expr, ExprCall, FnArg, ItemFn, ItemMod, Pat, Stmt, Type, }; mod keywords { @@ -20,6 +19,7 @@ fn emit_error + Clone, S: Into>(item: &T, message: } struct BenchmarkDef { + params: Vec<(String, u32, u32)>, setup_stmts: Vec, extrinsic_call_stmt: Stmt, extrinsic_call_fn: ExprCall, @@ -29,7 +29,26 @@ struct BenchmarkDef { impl BenchmarkDef { pub fn from(item_fn: &ItemFn) -> Option { let mut i = 0; // index of child + for arg in &item_fn.sig.inputs { + // parse params such as "x: Linear<0, 1>" + let mut name: Option = None; + let mut typ: Option<&Type> = None; + if let FnArg::Typed(arg) = arg { + if let Pat::Ident(ident) = &*arg.pat { + name = Some(ident.ident.to_token_stream().to_string()); + } + typ = Some(&*arg.ty); + } + if let (Some(name), Some(typ)) = (name, typ) { + // test + println!("name: {:?}", name); + println!("type: {:?}", typ); + } else { + } + } for child in &item_fn.block.stmts { + // find #[extrinsic_call] annotation and build up the setup, call, and verify + // blocks based on the location of this annotation if let Stmt::Semi(Expr::Call(fn_call), token) = child { let mut k = 0; // index of attr for attr in &fn_call.attrs { @@ -40,6 +59,7 @@ impl BenchmarkDef { let mut fn_call_copy = fn_call.clone(); fn_call_copy.attrs.remove(k); // consume #[extrinsic call] return Some(BenchmarkDef { + params: Vec::new(), setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), extrinsic_call_stmt: Stmt::Semi( Expr::Call(fn_call_copy.clone()), @@ -97,7 +117,7 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let item_fn = parse_macro_input!(tokens as ItemFn); - let mut benchmark_def = match BenchmarkDef::from(&item_fn) { + let benchmark_def = match BenchmarkDef::from(&item_fn) { Some(def) => def, None => return emit_error( @@ -113,6 +133,9 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let params = vec![quote!(x, 0, 1)]; let param_names = vec![quote!(x)]; quote! { + use ::frame_support::assert_impl_all; + assert_impl_all!(::frame_support::Linear<0, 1>: ::frame_support::ParamRange); + #[allow(non_camel_case_types)] struct #name; diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 7680a066e7b27..a5eb37f9d4809 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -83,6 +83,12 @@ pub mod migrations; pub mod traits; pub mod weights; +#[macro_use] +extern crate static_assertions; + +#[doc(hidden)] +pub use static_assertions::assert_impl_all; + #[doc(hidden)] pub mod unsigned { #[doc(hidden)] @@ -2751,3 +2757,18 @@ sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); pub use frame_support_procedural::{benchmark, benchmarks}; pub struct Linear; + +pub trait ParamRange { + fn start(&self) -> u32; + fn end(&self) -> u32; +} + +impl ParamRange for Linear { + fn start(&self) -> u32 { + return A + } + + fn end(&self) -> u32 { + return B + } +} From ca6c377409764a325e33fdd77c185e1a36ec8e33 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 19 Dec 2022 15:55:28 -0500 Subject: [PATCH 015/146] params parsing WIP --- frame/support/procedural/src/benchmark.rs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 6128547e27e28..6089c08d75f36 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -19,7 +19,8 @@ fn emit_error + Clone, S: Into>(item: &T, message: } struct BenchmarkDef { - params: Vec<(String, u32, u32)>, + // name, typ, A, B + params: Vec<(String, String, u32, u32)>, setup_stmts: Vec, extrinsic_call_stmt: Stmt, extrinsic_call_fn: ExprCall, @@ -29,6 +30,7 @@ struct BenchmarkDef { impl BenchmarkDef { pub fn from(item_fn: &ItemFn) -> Option { let mut i = 0; // index of child + let params: Vec<(String, String, u32, u32)> = Vec::new(); for arg in &item_fn.sig.inputs { // parse params such as "x: Linear<0, 1>" let mut name: Option = None; @@ -59,7 +61,7 @@ impl BenchmarkDef { let mut fn_call_copy = fn_call.clone(); fn_call_copy.attrs.remove(k); // consume #[extrinsic call] return Some(BenchmarkDef { - params: Vec::new(), + params, setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), extrinsic_call_stmt: Stmt::Semi( Expr::Call(fn_call_copy.clone()), @@ -81,19 +83,6 @@ impl BenchmarkDef { } } -struct BareBlock { - stmts: Vec, -} - -impl Parse for BareBlock { - fn parse(input: ParseStream) -> syn::Result { - match Block::parse_within(input) { - Ok(stmts) => Ok(BareBlock { stmts }), - Err(e) => Err(e), - } - } -} - pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let item_mod = parse_macro_input!(tokens as ItemMod); let contents = match item_mod.content { From 09b1f32bd201c94cb2dd2c51c28bd921efc6ef26 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 20 Dec 2022 01:22:25 -0500 Subject: [PATCH 016/146] clean up (don't need extrinsic call name) --- frame/support/procedural/src/benchmark.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 6089c08d75f36..77531c14f62d4 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,11 +1,6 @@ use proc_macro::TokenStream; use quote::{quote, ToTokens}; -use syn::{ - parse::{Parse, ParseStream}, - parse_macro_input, - spanned::Spanned, - Block, Expr, ExprCall, FnArg, ItemFn, ItemMod, Pat, Stmt, Type, -}; +use syn::{parse_macro_input, spanned::Spanned, Expr, FnArg, ItemFn, ItemMod, Pat, Stmt, Type}; mod keywords { syn::custom_keyword!(extrinsic_call); @@ -23,7 +18,6 @@ struct BenchmarkDef { params: Vec<(String, String, u32, u32)>, setup_stmts: Vec, extrinsic_call_stmt: Stmt, - extrinsic_call_fn: ExprCall, verify_stmts: Vec, } @@ -64,10 +58,9 @@ impl BenchmarkDef { params, setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), extrinsic_call_stmt: Stmt::Semi( - Expr::Call(fn_call_copy.clone()), + Expr::Call(fn_call_copy), token.clone(), ), - extrinsic_call_fn: fn_call_copy, verify_stmts: Vec::from( &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], ), @@ -116,14 +109,14 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { }; let name = item_fn.sig.ident; let krate = quote!(::frame_benchmarking); + let support = quote!(::frame_support); let setup_stmts = benchmark_def.setup_stmts; let extrinsic_call_stmt = benchmark_def.extrinsic_call_stmt; let verify_stmts = benchmark_def.verify_stmts; let params = vec![quote!(x, 0, 1)]; let param_names = vec![quote!(x)]; quote! { - use ::frame_support::assert_impl_all; - assert_impl_all!(::frame_support::Linear<0, 1>: ::frame_support::ParamRange); + #support::assert_impl_all!(#support::Linear<0, 1>: #support::ParamRange); #[allow(non_camel_case_types)] struct #name; From b767e9f2ec546eb7172177e23169e01f19cd3aa2 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 20 Dec 2022 01:54:07 -0500 Subject: [PATCH 017/146] use proper Result syntax for BenchmarkDef parsing --- frame/support/procedural/src/benchmark.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 77531c14f62d4..b379fd450bb84 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,6 +1,9 @@ use proc_macro::TokenStream; use quote::{quote, ToTokens}; -use syn::{parse_macro_input, spanned::Spanned, Expr, FnArg, ItemFn, ItemMod, Pat, Stmt, Type}; +use syn::{ + parse_macro_input, spanned::Spanned, Error, Expr, FnArg, ItemFn, ItemMod, Pat, Result, Stmt, + Type, +}; mod keywords { syn::custom_keyword!(extrinsic_call); @@ -22,7 +25,7 @@ struct BenchmarkDef { } impl BenchmarkDef { - pub fn from(item_fn: &ItemFn) -> Option { + pub fn from(item_fn: &ItemFn) -> Result { let mut i = 0; // index of child let params: Vec<(String, String, u32, u32)> = Vec::new(); for arg in &item_fn.sig.inputs { @@ -54,7 +57,7 @@ impl BenchmarkDef { ) { let mut fn_call_copy = fn_call.clone(); fn_call_copy.attrs.remove(k); // consume #[extrinsic call] - return Some(BenchmarkDef { + return Ok(BenchmarkDef { params, setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), extrinsic_call_stmt: Stmt::Semi( @@ -72,7 +75,10 @@ impl BenchmarkDef { } i += 1; } - return None + return Err(Error::new( + item_fn.block.brace_token.span, + "Missing #[extrinsic_call] annotation in benchmark function body.", + )) } } @@ -100,12 +106,8 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let item_fn = parse_macro_input!(tokens as ItemFn); let benchmark_def = match BenchmarkDef::from(&item_fn) { - Some(def) => def, - None => - return emit_error( - &item_fn.block.to_token_stream(), - "Missing #[extrinsic_call] annotation in benchmark function body.", - ), + Ok(def) => def, + Err(err) => return err.to_compile_error().into(), }; let name = item_fn.sig.ident; let krate = quote!(::frame_benchmarking); From d36210add41f82cff32342e93373f4d2d1b390e1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 20 Dec 2022 02:55:40 -0500 Subject: [PATCH 018/146] proper parsing of Linear<0, 1> style args --- Cargo.lock | 12 +++++ frame/support/procedural/Cargo.toml | 1 + frame/support/procedural/src/benchmark.rs | 58 +++++++++++++++++++---- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec5d86b977116..bbf7f63aef1b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1602,6 +1602,17 @@ dependencies = [ "rusticata-macros", ] +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_builder" version = "0.11.2" @@ -2324,6 +2335,7 @@ version = "4.0.0-dev" dependencies = [ "Inflector", "cfg-expr", + "derive-syn-parse", "frame-support-procedural-tools", "itertools", "proc-macro2", diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index 06b8056aff982..ee1ca4dff8873 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] +derive-syn-parse = "0.1.5" Inflector = "0.11.4" cfg-expr = "0.10.3" itertools = "0.10.3" diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index b379fd450bb84..7e2ad0f308794 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,8 +1,11 @@ +use derive_syn_parse::Parse; use proc_macro::TokenStream; use quote::{quote, ToTokens}; use syn::{ - parse_macro_input, spanned::Spanned, Error, Expr, FnArg, ItemFn, ItemMod, Pat, Result, Stmt, - Type, + parse_macro_input, + spanned::Spanned, + token::{Comma, Gt, Lt}, + Error, Expr, FnArg, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, Type, }; mod keywords { @@ -16,9 +19,25 @@ fn emit_error + Clone, S: Into>(item: &T, message: return syn::Error::new(span, message).to_compile_error().into() } +#[derive(Debug, Clone, PartialEq)] +struct ParamDef { + name: String, + typ: Type, + start: u32, + end: u32, +} + +#[derive(Parse)] +struct RangeArgs { + _lt_token: Lt, + start: LitInt, + _comma: Comma, + end: LitInt, + _gt_token: Gt, +} + struct BenchmarkDef { - // name, typ, A, B - params: Vec<(String, String, u32, u32)>, + params: Vec, setup_stmts: Vec, extrinsic_call_stmt: Stmt, verify_stmts: Vec, @@ -27,22 +46,41 @@ struct BenchmarkDef { impl BenchmarkDef { pub fn from(item_fn: &ItemFn) -> Result { let mut i = 0; // index of child - let params: Vec<(String, String, u32, u32)> = Vec::new(); + let mut params: Vec = Vec::new(); for arg in &item_fn.sig.inputs { // parse params such as "x: Linear<0, 1>" let mut name: Option = None; let mut typ: Option<&Type> = None; + let mut start: Option = None; + let mut end: Option = None; if let FnArg::Typed(arg) = arg { if let Pat::Ident(ident) = &*arg.pat { name = Some(ident.ident.to_token_stream().to_string()); } - typ = Some(&*arg.ty); + let tmp = &*arg.ty; + typ = Some(tmp); + if let Type::Path(tpath) = tmp { + if let Some(segment) = tpath.path.segments.last() { + let args = segment.arguments.to_token_stream().into(); + if let Ok(args) = syn::parse::(args) { + if let Ok(start_parsed) = args.start.base10_parse::() { + start = Some(start_parsed); + } + if let Ok(end_parsed) = args.end.base10_parse::() { + end = Some(end_parsed); + } + } + } + } } - if let (Some(name), Some(typ)) = (name, typ) { - // test - println!("name: {:?}", name); - println!("type: {:?}", typ); + if let (Some(name), Some(typ), Some(start), Some(end)) = (name, typ, start, end) { + // if true, this iteration of param extraction was successful + params.push(ParamDef { name, typ: typ.clone(), start, end }); } else { + return Err(Error::new( + arg.span(), + "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", + )) } } for child in &item_fn.block.stmts { From 33ac0f7a9782ed62ac71f6029a2911e767413cb0 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 20 Dec 2022 03:27:25 -0500 Subject: [PATCH 019/146] successfully parse and make use of linear component ranges :boom: --- frame/grandpa/src/benchmarking_alt.rs | 5 ++- frame/support/procedural/src/benchmark.rs | 43 +++++++++++++++++++---- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/frame/grandpa/src/benchmarking_alt.rs b/frame/grandpa/src/benchmarking_alt.rs index f475ec730dc69..0ce28178898e9 100644 --- a/frame/grandpa/src/benchmarking_alt.rs +++ b/frame/grandpa/src/benchmarking_alt.rs @@ -1,8 +1,7 @@ #![cfg(any(feature = "runtime-benchmarks", test))] -use super::{Pallet as Grandpa, *}; -use frame_support::{benchmark, Linear}; -use frame_system::RawOrigin; +use super::*; +use frame_support::benchmark; use sp_core::H256; #[benchmark] diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 7e2ad0f308794..a26720efd8b18 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,5 +1,6 @@ use derive_syn_parse::Parse; use proc_macro::TokenStream; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ parse_macro_input, @@ -141,6 +142,34 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { .into() } +// prepares a `Vec` to be interpolated by `quote!` +struct UnrolledParams { + param_ranges: Vec, + param_names: Vec, +} + +impl UnrolledParams { + fn from(params: &Vec) -> UnrolledParams { + let param_ranges: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + let start = p.start; + let end = p.end; + quote!(#name, #start, #end).into() + }) + .collect(); + let param_names: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + quote!(#name).into() + }) + .collect(); + UnrolledParams { param_ranges, param_names } + } +} + pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let item_fn = parse_macro_input!(tokens as ItemFn); let benchmark_def = match BenchmarkDef::from(&item_fn) { @@ -153,9 +182,10 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let setup_stmts = benchmark_def.setup_stmts; let extrinsic_call_stmt = benchmark_def.extrinsic_call_stmt; let verify_stmts = benchmark_def.verify_stmts; - let params = vec![quote!(x, 0, 1)]; - let param_names = vec![quote!(x)]; - quote! { + let unrolled = UnrolledParams::from(&benchmark_def.params); + let param_names = unrolled.param_names; + let param_ranges = unrolled.param_ranges; + let res = quote! { #support::assert_impl_all!(#support::Linear<0, 1>: #support::ParamRange); #[allow(non_camel_case_types)] @@ -167,7 +197,7 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { #krate::vec! [ #( - (#krate::BenchmarkParameter::#params) + (#krate::BenchmarkParameter::#param_ranges) ),* ] } @@ -208,6 +238,7 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { })) } } - } - .into() + }; + println!("{}", res.to_string()); + res.into() } From b75410f3cf6fc52b1314a6c5ff07562f867ca7a1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 20 Dec 2022 13:23:06 -0500 Subject: [PATCH 020/146] rename support variable => home because eventually will be moved --- frame/support/procedural/src/benchmark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index a26720efd8b18..a6e13f2129e6a 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -178,7 +178,7 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { }; let name = item_fn.sig.ident; let krate = quote!(::frame_benchmarking); - let support = quote!(::frame_support); + let home = quote!(::frame_support); let setup_stmts = benchmark_def.setup_stmts; let extrinsic_call_stmt = benchmark_def.extrinsic_call_stmt; let verify_stmts = benchmark_def.verify_stmts; @@ -186,7 +186,7 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let param_names = unrolled.param_names; let param_ranges = unrolled.param_ranges; let res = quote! { - #support::assert_impl_all!(#support::Linear<0, 1>: #support::ParamRange); + #home::assert_impl_all!(#home::Linear<0, 1>: #home::ParamRange); #[allow(non_camel_case_types)] struct #name; From 7686c7f263a6837915cfdd0815cdd860b715f3b7 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 20 Dec 2022 13:51:23 -0500 Subject: [PATCH 021/146] compile-time check that param range types implement ParamRange --- frame/grandpa/src/benchmarking_alt.rs | 2 +- frame/support/procedural/src/benchmark.rs | 29 +++++++++++++++++++---- frame/support/src/lib.rs | 1 - 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/frame/grandpa/src/benchmarking_alt.rs b/frame/grandpa/src/benchmarking_alt.rs index 0ce28178898e9..3257643380bbe 100644 --- a/frame/grandpa/src/benchmarking_alt.rs +++ b/frame/grandpa/src/benchmarking_alt.rs @@ -1,7 +1,7 @@ #![cfg(any(feature = "runtime-benchmarks", test))] use super::*; -use frame_support::benchmark; +use frame_support::{benchmark, Linear}; use sp_core::H256; #[benchmark] diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index a6e13f2129e6a..b8a1a675e282a 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -146,6 +146,7 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { struct UnrolledParams { param_ranges: Vec, param_names: Vec, + param_types: Vec, } impl UnrolledParams { @@ -156,37 +157,57 @@ impl UnrolledParams { let name = Ident::new(&p.name, Span::call_site()); let start = p.start; let end = p.end; - quote!(#name, #start, #end).into() + quote!(#name, #start, #end) }) .collect(); let param_names: Vec = params .iter() .map(|p| { let name = Ident::new(&p.name, Span::call_site()); - quote!(#name).into() + quote!(#name) }) .collect(); - UnrolledParams { param_ranges, param_names } + let param_types: Vec = params + .iter() + .map(|p| { + let typ = &p.typ; + quote!(#typ) + }) + .collect(); + UnrolledParams { param_ranges, param_names, param_types } } } pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { + // parse attached item as a function def let item_fn = parse_macro_input!(tokens as ItemFn); + + // build a BenchmarkDef from item_fn let benchmark_def = match BenchmarkDef::from(&item_fn) { Ok(def) => def, Err(err) => return err.to_compile_error().into(), }; + + // set up variables needed during quoting let name = item_fn.sig.ident; let krate = quote!(::frame_benchmarking); let home = quote!(::frame_support); let setup_stmts = benchmark_def.setup_stmts; let extrinsic_call_stmt = benchmark_def.extrinsic_call_stmt; let verify_stmts = benchmark_def.verify_stmts; + + // unroll params (prepare for quoting) let unrolled = UnrolledParams::from(&benchmark_def.params); let param_names = unrolled.param_names; let param_ranges = unrolled.param_ranges; + let param_types = unrolled.param_types; + + // generate final quoted tokens let res = quote! { - #home::assert_impl_all!(#home::Linear<0, 1>: #home::ParamRange); + // compile-time assertions that each referenced param type implements ParamRange + #( + #home::assert_impl_all!(#param_types: #home::ParamRange); + )* #[allow(non_camel_case_types)] struct #name; diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index a5eb37f9d4809..b41a0facfd1d6 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -83,7 +83,6 @@ pub mod migrations; pub mod traits; pub mod weights; -#[macro_use] extern crate static_assertions; #[doc(hidden)] From 061b3fe0a8c2044ab9725d2405ab92dce050400b Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 21 Dec 2022 11:04:18 -0500 Subject: [PATCH 022/146] switch to using balances as example, failing on instance pallet --- frame/balances/src/benchmarking_alt.rs | 38 +++++++++++++++++++++++++ frame/balances/src/lib.rs | 1 + frame/grandpa/src/benchmarking_alt.rs | 39 -------------------------- frame/grandpa/src/lib.rs | 1 - 4 files changed, 39 insertions(+), 40 deletions(-) create mode 100644 frame/balances/src/benchmarking_alt.rs delete mode 100644 frame/grandpa/src/benchmarking_alt.rs diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs new file mode 100644 index 0000000000000..450afa89edf11 --- /dev/null +++ b/frame/balances/src/benchmarking_alt.rs @@ -0,0 +1,38 @@ +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +use frame_system::RawOrigin; +use sp_runtime::traits::Bounded; + +use crate::Pallet as Balances; + +const SEED: u32 = 0; +// existential deposit multiplier +const ED_MULTIPLIER: u32 = 10; + +use frame_benchmarking::{account, whitelisted_caller}; +use frame_support::{benchmark, benchmarks, Linear}; + +#[benchmark] +fn transfer(x: Linear<0, 1>) { + let existential_deposit = T::ExistentialDeposit::get(); + let caller = whitelisted_caller(); + + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, + // and reap this user. + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); +} diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 99d77a3e73361..5629b34e720a6 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -156,6 +156,7 @@ #[macro_use] mod tests; mod benchmarking; +mod benchmarking_alt; pub mod migration; mod tests_composite; mod tests_local; diff --git a/frame/grandpa/src/benchmarking_alt.rs b/frame/grandpa/src/benchmarking_alt.rs deleted file mode 100644 index 3257643380bbe..0000000000000 --- a/frame/grandpa/src/benchmarking_alt.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![cfg(any(feature = "runtime-benchmarks", test))] - -use super::*; -use frame_support::{benchmark, Linear}; -use sp_core::H256; - -#[benchmark] -fn bench_name(x: Linear<0, 1>) { - // NOTE: generated with the test below `test_generate_equivocation_report_blob`. - // the output should be deterministic since the keys we use are static. - // with the current benchmark setup it is not possible to generate this - // programatically from the benchmark setup. - const EQUIVOCATION_PROOF_BLOB: [u8; 257] = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 136, 220, 52, 23, 213, 5, 142, 196, 180, - 80, 62, 12, 18, 234, 26, 10, 137, 190, 32, 15, 233, 137, 34, 66, 61, 67, 52, 1, 79, 166, - 176, 238, 207, 48, 195, 55, 171, 225, 252, 130, 161, 56, 151, 29, 193, 32, 25, 157, 249, - 39, 80, 193, 214, 96, 167, 147, 25, 130, 45, 42, 64, 208, 182, 164, 10, 0, 0, 0, 0, 0, 0, - 0, 234, 236, 231, 45, 70, 171, 135, 246, 136, 153, 38, 167, 91, 134, 150, 242, 215, 83, 56, - 238, 16, 119, 55, 170, 32, 69, 255, 248, 164, 20, 57, 50, 122, 115, 135, 96, 80, 203, 131, - 232, 73, 23, 149, 86, 174, 59, 193, 92, 121, 76, 154, 211, 44, 96, 10, 84, 159, 133, 211, - 56, 103, 0, 59, 2, 96, 20, 69, 2, 32, 179, 16, 184, 108, 76, 215, 64, 195, 78, 143, 73, - 177, 139, 20, 144, 98, 231, 41, 117, 255, 220, 115, 41, 59, 27, 75, 56, 10, 0, 0, 0, 0, 0, - 0, 0, 128, 179, 250, 48, 211, 76, 10, 70, 74, 230, 219, 139, 96, 78, 88, 112, 33, 170, 44, - 184, 59, 200, 155, 143, 128, 40, 222, 179, 210, 190, 84, 16, 182, 21, 34, 94, 28, 193, 163, - 226, 51, 251, 134, 233, 187, 121, 63, 157, 240, 165, 203, 92, 16, 146, 120, 190, 229, 251, - 129, 29, 45, 32, 29, 6, - ]; - - let equivocation_proof1: sp_finality_grandpa::EquivocationProof = - Decode::decode(&mut &EQUIVOCATION_PROOF_BLOB[..]).unwrap(); - - let equivocation_proof2 = equivocation_proof1.clone(); - - #[extrinsic_call] - sp_finality_grandpa::check_equivocation_proof(equivocation_proof1); - - // Post condition verification - assert!(sp_finality_grandpa::check_equivocation_proof(equivocation_proof2)); -} diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 1948fd4d23bd5..aa09b445c6bdd 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -58,7 +58,6 @@ pub mod migrations; #[cfg(any(feature = "runtime-benchmarks", test))] mod benchmarking; -mod benchmarking_alt; #[cfg(all(feature = "std", test))] mod mock; #[cfg(all(feature = "std", test))] From 883c7381200dc602e6d9d0695534e4dc58c93f7c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 21 Dec 2022 15:43:13 -0500 Subject: [PATCH 023/146] successfully set up __origin and __call with balances :boom: --- frame/balances/src/benchmarking_alt.rs | 8 +-- frame/support/procedural/src/benchmark.rs | 72 ++++++++++++++++++----- frame/support/procedural/src/lib.rs | 7 ++- frame/support/src/lib.rs | 2 +- 4 files changed, 69 insertions(+), 20 deletions(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 450afa89edf11..c9d2a77b1be90 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -3,7 +3,7 @@ use super::*; use frame_system::RawOrigin; -use sp_runtime::traits::Bounded; +//use sp_runtime::traits::Bounded; use crate::Pallet as Balances; @@ -12,10 +12,10 @@ const SEED: u32 = 0; const ED_MULTIPLIER: u32 = 10; use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::{benchmark, benchmarks, Linear}; +use frame_support::{instance_benchmark, Linear}; -#[benchmark] -fn transfer(x: Linear<0, 1>) { +#[instance_benchmark] +fn benchmark_transfer(u: Linear<0, 1_000>) { let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index b8a1a675e282a..eacb80d6b9322 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -4,9 +4,10 @@ use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ parse_macro_input, + punctuated::Punctuated, spanned::Spanned, token::{Comma, Gt, Lt}, - Error, Expr, FnArg, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, Type, + Error, Expr, ExprCall, FnArg, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, Type, }; mod keywords { @@ -40,7 +41,8 @@ struct RangeArgs { struct BenchmarkDef { params: Vec, setup_stmts: Vec, - extrinsic_call_stmt: Stmt, + extrinsic_call: ExprCall, + origin: Expr, verify_stmts: Vec, } @@ -87,22 +89,33 @@ impl BenchmarkDef { for child in &item_fn.block.stmts { // find #[extrinsic_call] annotation and build up the setup, call, and verify // blocks based on the location of this annotation - if let Stmt::Semi(Expr::Call(fn_call), token) = child { + if let Stmt::Semi(Expr::Call(fn_call), _token) = child { let mut k = 0; // index of attr for attr in &fn_call.attrs { if let Some(segment) = attr.path.segments.last() { if let Ok(_) = syn::parse::( segment.ident.to_token_stream().into(), ) { - let mut fn_call_copy = fn_call.clone(); - fn_call_copy.attrs.remove(k); // consume #[extrinsic call] + let mut extrinsic_call = fn_call.clone(); + extrinsic_call.attrs.remove(k); // consume #[extrinsic call] + let origin = match extrinsic_call.args.first() { + Some(arg) => arg.clone(), + None => return Err(Error::new( + extrinsic_call.args.span(), + "Extrinsic call must specify its origin as the first argument.", + )), + }; + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = extrinsic_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + extrinsic_call.args = final_args; return Ok(BenchmarkDef { params, setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - extrinsic_call_stmt: Stmt::Semi( - Expr::Call(fn_call_copy), - token.clone(), - ), + extrinsic_call, + origin, verify_stmts: Vec::from( &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], ), @@ -178,7 +191,7 @@ impl UnrolledParams { } } -pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { +pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> TokenStream { // parse attached item as a function def let item_fn = parse_macro_input!(tokens as ItemFn); @@ -192,8 +205,11 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let name = item_fn.sig.ident; let krate = quote!(::frame_benchmarking); let home = quote!(::frame_support); + let codec = quote!(#krate::frame_support::codec); + let traits = quote!(#krate::frame_support::traits); let setup_stmts = benchmark_def.setup_stmts; - let extrinsic_call_stmt = benchmark_def.extrinsic_call_stmt; + let mut extrinsic_call = benchmark_def.extrinsic_call; + let origin = benchmark_def.origin; let verify_stmts = benchmark_def.verify_stmts; // unroll params (prepare for quoting) @@ -202,6 +218,26 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let param_ranges = unrolled.param_ranges; let param_types = unrolled.param_types; + let generics = match is_instance { + false => quote!(T), + true => quote!(T, I), + }; + + let full_generics = match is_instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + // modify extrinsic call to be prefixed with new_call_variant + if let Expr::Path(expr_path) = &mut *extrinsic_call.func { + if let Some(segment) = expr_path.path.segments.last_mut() { + segment.ident = Ident::new( + format!("new_call_variant_{}", segment.ident.to_string()).as_str(), + Span::call_site(), + ); + } // else handle error? + } // else handle error? + // generate final quoted tokens let res = quote! { // compile-time assertions that each referenced param type implements ParamRange @@ -213,7 +249,7 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { struct #name; #[allow(unused_variables)] - impl ::frame_benchmarking::BenchmarkingSetup + impl<#full_generics> #krate::BenchmarkingSetup<#generics> for #name { fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { #krate::vec! [ @@ -247,9 +283,17 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { #( #setup_stmts )* - + let __call = Call::<#generics>::#extrinsic_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); Ok(#krate::Box::new(move || -> Result<(), #krate::BenchmarkError> { - #extrinsic_call_stmt + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin.into(); + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + )?; if verify { #( #verify_stmts diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 851cb078c6dfc..51c8bad319367 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -487,7 +487,12 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStream { - benchmark::benchmark(attrs, tokens) + benchmark::benchmark(attrs, tokens, false) +} + +#[proc_macro_attribute] +pub fn instance_benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStream { + benchmark::benchmark(attrs, tokens, true) } /// Execute the annotated function in a new storage transaction. diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index b41a0facfd1d6..5faa6e3b062b0 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2753,7 +2753,7 @@ pub mod pallet_macros { // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); -pub use frame_support_procedural::{benchmark, benchmarks}; +pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmark}; pub struct Linear; From 193f9467f57070bd23b8d7499ea54e3cdbe8c7db Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 22 Dec 2022 00:45:31 -0500 Subject: [PATCH 024/146] clean up --- frame/support/procedural/src/benchmark.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index eacb80d6b9322..86275514359ef 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -272,13 +272,6 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> .1; )* - // TODO: figure out parameter parsing: - // $( - // let $pre_id : $pre_ty = $pre_ex; - // )* - // $( $param_instancer ; )* - // $( $post )* - // benchmark setup code (stuff before #[extrinsic_call]) #( #setup_stmts From a9784ebfd5b470df6b635b8738b04e62ce73640a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 01:00:30 -0500 Subject: [PATCH 025/146] use a module --- frame/balances/src/benchmarking_alt.rs | 57 ++++++++++++----------- frame/support/procedural/src/benchmark.rs | 25 ++++++++-- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index c9d2a77b1be90..535ff6f1d0f99 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -1,38 +1,43 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; +use frame_support::benchmarks; -use frame_system::RawOrigin; -//use sp_runtime::traits::Bounded; +#[benchmarks] +mod benchmarks { + use super::super::*; -use crate::Pallet as Balances; + use frame_system::RawOrigin; + //use sp_runtime::traits::Bounded; -const SEED: u32 = 0; -// existential deposit multiplier -const ED_MULTIPLIER: u32 = 10; + use crate::Pallet as Balances; -use frame_benchmarking::{account, whitelisted_caller}; -use frame_support::{instance_benchmark, Linear}; + const SEED: u32 = 0; + // existential deposit multiplier + const ED_MULTIPLIER: u32 = 10; -#[instance_benchmark] -fn benchmark_transfer(u: Linear<0, 1_000>) { - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); + use frame_benchmarking::{account, whitelisted_caller}; + use frame_support::{instance_benchmark, Linear}; - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + #[instance_benchmark] + fn benchmark_transfer(u: Linear<0, 1_000>) { + let existential_deposit = T::ExistentialDeposit::get(); + let caller = whitelisted_caller(); - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, - // and reap this user. - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = - existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - #[extrinsic_call] - transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, + // and reap this user. + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - assert_eq!(Balances::::free_balance(&caller), Zero::zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + } } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 86275514359ef..e8ca391e4ea97 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -12,6 +12,7 @@ use syn::{ mod keywords { syn::custom_keyword!(extrinsic_call); + syn::custom_keyword!(cfg); } fn emit_error + Clone, S: Into>(item: &T, message: S) -> TokenStream { @@ -136,7 +137,21 @@ impl BenchmarkDef { pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { let item_mod = parse_macro_input!(tokens as ItemMod); - let contents = match item_mod.content { + let mut extra_attrs: Vec = Vec::new(); + let mut has_cfg_attr = false; + for attr in &item_mod.attrs { + if let Some(segment) = attr.path.segments.first() { + if let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) { + has_cfg_attr = true; + break + } + } + } + if !has_cfg_attr { + // add a cfg attribute to the module since it doesn't have one + extra_attrs.push(quote!(#[cfg(any(feature = "runtime-benchmarks", test))])); + } + let mod_contents = match item_mod.content { Some(content) => content.1, None => return emit_error( @@ -145,10 +160,14 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { ), }; let mod_ident = item_mod.ident; + let mod_attrs = item_mod.attrs; quote! { - #[cfg(any(feature = "runtime-benchmarks", test))] + #(#mod_attrs) + * + #(#extra_attrs) + * mod #mod_ident { - #(#contents) + #(#mod_contents) * } } From d3fe8ac2b540e743e707726644e85508b88843af Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 10:06:35 -0500 Subject: [PATCH 026/146] don't need a variable for transfer --- frame/balances/src/benchmarking_alt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 535ff6f1d0f99..63e3b33b2f512 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -19,7 +19,7 @@ mod benchmarks { use frame_support::{instance_benchmark, Linear}; #[instance_benchmark] - fn benchmark_transfer(u: Linear<0, 1_000>) { + fn benchmark_transfer() { let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); From deb4157fe33b14a256ac40d21cd2ba4ff97c9078 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 10:08:49 -0500 Subject: [PATCH 027/146] rename benchmark_transfer -> transfer because no longer conflicts --- frame/balances/src/benchmarking_alt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 63e3b33b2f512..e146a7832172c 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -19,7 +19,7 @@ mod benchmarks { use frame_support::{instance_benchmark, Linear}; #[instance_benchmark] - fn benchmark_transfer() { + fn transfer() { let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); From 982429dc6568d0bcf7f65d3be64a71ae064a182a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 10:12:46 -0500 Subject: [PATCH 028/146] clean up --- frame/balances/src/benchmarking_alt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index e146a7832172c..ebec203965315 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -16,7 +16,7 @@ mod benchmarks { const ED_MULTIPLIER: u32 = 10; use frame_benchmarking::{account, whitelisted_caller}; - use frame_support::{instance_benchmark, Linear}; + use frame_support::instance_benchmark; #[instance_benchmark] fn transfer() { From 749c7b048ee6377ec0f1251e9187c152d2dfc24f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 10:22:02 -0500 Subject: [PATCH 029/146] working with transfer_increasing_users as well :boom: --- frame/balances/src/benchmarking_alt.rs | 34 +++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index ebec203965315..d6fb81f9a3250 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -16,7 +16,7 @@ mod benchmarks { const ED_MULTIPLIER: u32 = 10; use frame_benchmarking::{account, whitelisted_caller}; - use frame_support::instance_benchmark; + use frame_support::{instance_benchmark, Linear}; #[instance_benchmark] fn transfer() { @@ -40,4 +40,36 @@ mod benchmarks { assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } + + #[instance_benchmark] + fn transfer_increasing_users(u: Linear<0, 1_000>) { + // 1_000 is not very much, but this upper bound can be controlled by the CLI. + let existential_deposit = T::ExistentialDeposit::get(); + let caller = whitelisted_caller(); + + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, + // and reap this user. + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + // Create a bunch of users in storage. + for i in 0..u { + // The `account` function uses `blake2_256` to generate unique accounts, so these + // should be quite random and evenly distributed in the trie. + let new_user: T::AccountId = account("new_user", i, SEED); + let _ = as Currency<_>>::make_free_balance_be(&new_user, balance); + } + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + } } From 0ef810cea9091c74857668ee8b2b72270a4cd5f1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 10:24:38 -0500 Subject: [PATCH 030/146] re-add BareBlock --- frame/support/procedural/src/benchmark.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index e8ca391e4ea97..5fbf0e6f74140 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -3,11 +3,12 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ + parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::{Comma, Gt, Lt}, - Error, Expr, ExprCall, FnArg, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, Type, + Block, Error, Expr, ExprCall, FnArg, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, Type, }; mod keywords { @@ -22,7 +23,19 @@ fn emit_error + Clone, S: Into>(item: &T, message: return syn::Error::new(span, message).to_compile_error().into() } -#[derive(Debug, Clone, PartialEq)] +struct BareBlock { + stmts: Vec, +} + +impl Parse for BareBlock { + fn parse(input: ParseStream) -> syn::Result { + match Block::parse_within(input) { + Ok(stmts) => Ok(BareBlock { stmts }), + Err(e) => Err(e), + } + } +} + struct ParamDef { name: String, typ: Type, From 4fe2cd89eeb615e75a83e1734d19955e60c50b8e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 10:38:28 -0500 Subject: [PATCH 031/146] add comments for undocumented structs+functions+traits --- frame/support/procedural/src/benchmark.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 5fbf0e6f74140..4cc8957d0fe78 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -16,6 +16,7 @@ mod keywords { syn::custom_keyword!(cfg); } +/// A safe wrapper for easily emitting errors in [`quote!`] calls fn emit_error + Clone, S: Into>(item: &T, message: S) -> TokenStream { let item = Into::::into(item.clone()); let message = Into::::into(message); @@ -23,6 +24,10 @@ fn emit_error + Clone, S: Into>(item: &T, message: return syn::Error::new(span, message).to_compile_error().into() } +/// Represents a "bare" block, that is, a the contents of a [`Block`] minus the curly braces. +/// Useful for parsing the contents of a decl macro that takes a block, since the curly braces +/// are not actually included in the input [`TokenStream`] in that scenario. The contents are +/// parsed as a [`Vec`] called `stmts`. Can be used with [`parse_macro_input!`], etc. struct BareBlock { stmts: Vec, } @@ -36,6 +41,7 @@ impl Parse for BareBlock { } } +/// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. struct ParamDef { name: String, typ: Type, @@ -43,6 +49,7 @@ struct ParamDef { end: u32, } +/// Allows easy parsing of the `<10, 20>` component of `x: Linear<10, 20>`. #[derive(Parse)] struct RangeArgs { _lt_token: Lt, @@ -52,6 +59,7 @@ struct RangeArgs { _gt_token: Gt, } +/// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. struct BenchmarkDef { params: Vec, setup_stmts: Vec, @@ -61,6 +69,7 @@ struct BenchmarkDef { } impl BenchmarkDef { + /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. pub fn from(item_fn: &ItemFn) -> Result { let mut i = 0; // index of child let mut params: Vec = Vec::new(); @@ -187,7 +196,8 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { .into() } -// prepares a `Vec` to be interpolated by `quote!` +/// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable +/// arrays formatted in such a way that they can be interpolated directly. struct UnrolledParams { param_ranges: Vec, param_names: Vec, @@ -329,6 +339,6 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> } } }; - println!("{}", res.to_string()); + // println!("{}", res.to_string()); res.into() } From 75297bb936579c38d1d40fe76ddbb4f801d148ce Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 11:21:31 -0500 Subject: [PATCH 032/146] refactor in preparation for removing module requirements --- frame/balances/src/benchmarking_alt.rs | 4 +-- frame/support/procedural/src/benchmark.rs | 2 +- frame/support/src/lib.rs | 41 ++++++++++++++--------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index d6fb81f9a3250..49c09258ee299 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -1,6 +1,6 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_support::benchmarks; +use frame_support::benchmarking::benchmarks; #[benchmarks] mod benchmarks { @@ -16,7 +16,7 @@ mod benchmarks { const ED_MULTIPLIER: u32 = 10; use frame_benchmarking::{account, whitelisted_caller}; - use frame_support::{instance_benchmark, Linear}; + use frame_support::benchmarking::*; #[instance_benchmark] fn transfer() { diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 4cc8957d0fe78..d8e5cc5310c20 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -246,7 +246,7 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> // set up variables needed during quoting let name = item_fn.sig.ident; let krate = quote!(::frame_benchmarking); - let home = quote!(::frame_support); + let home = quote!(::frame_support::benchmarking); 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/src/lib.rs b/frame/support/src/lib.rs index 5faa6e3b062b0..e4436bfcafac0 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -85,9 +85,6 @@ pub mod weights; extern crate static_assertions; -#[doc(hidden)] -pub use static_assertions::assert_impl_all; - #[doc(hidden)] pub mod unsigned { #[doc(hidden)] @@ -2753,21 +2750,33 @@ pub mod pallet_macros { // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); -pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmark}; - -pub struct Linear; - -pub trait ParamRange { - fn start(&self) -> u32; - fn end(&self) -> u32; -} +pub mod benchmarking { + pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmark}; -impl ParamRange for Linear { - fn start(&self) -> u32 { - return A + #[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. + 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. + pub trait ParamRange { + fn start(&self) -> u32; + fn end(&self) -> u32; } - fn end(&self) -> u32 { - return B + impl ParamRange for Linear { + fn start(&self) -> u32 { + return A + } + + fn end(&self) -> u32 { + return B + } } } From dc12eb2465b525dc77aaabaeb8b707f34a48a135 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 23 Dec 2022 17:47:15 -0500 Subject: [PATCH 033/146] switch to a block instead of a module --- frame/balances/src/benchmarking_alt.rs | 5 ++- frame/support/procedural/src/benchmark.rs | 37 +++-------------------- frame/support/procedural/src/lib.rs | 6 ++-- 3 files changed, 9 insertions(+), 39 deletions(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 49c09258ee299..696253d9c3b39 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -2,9 +2,8 @@ use frame_support::benchmarking::benchmarks; -#[benchmarks] -mod benchmarks { - use super::super::*; +benchmarks! { + use super::*; use frame_system::RawOrigin; //use sp_runtime::traits::Bounded; diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index d8e5cc5310c20..07e58ba05f64e 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -157,41 +157,12 @@ impl BenchmarkDef { } } -pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream) -> TokenStream { - let item_mod = parse_macro_input!(tokens as ItemMod); - let mut extra_attrs: Vec = Vec::new(); - let mut has_cfg_attr = false; - for attr in &item_mod.attrs { - if let Some(segment) = attr.path.segments.first() { - if let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) { - has_cfg_attr = true; - break - } - } - } - if !has_cfg_attr { - // add a cfg attribute to the module since it doesn't have one - extra_attrs.push(quote!(#[cfg(any(feature = "runtime-benchmarks", test))])); - } - let mod_contents = match item_mod.content { - Some(content) => content.1, - None => - return emit_error( - &item_mod.to_token_stream(), - "#[frame_support::benchmarks] can only be applied to a non-empty module.", - ), - }; - let mod_ident = item_mod.ident; - let mod_attrs = item_mod.attrs; +pub fn benchmarks(tokens: TokenStream) -> TokenStream { + let block = parse_macro_input!(tokens as BareBlock); + let contents = block.stmts; quote! { - #(#mod_attrs) - * - #(#extra_attrs) + #(#contents) * - mod #mod_ident { - #(#mod_contents) - * - } } .into() } diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 51c8bad319367..2924dfcf3df27 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -480,9 +480,9 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) } -#[proc_macro_attribute] -pub fn benchmarks(attrs: TokenStream, tokens: TokenStream) -> TokenStream { - benchmark::benchmarks(attrs, tokens) +#[proc_macro] +pub fn benchmarks(tokens: TokenStream) -> TokenStream { + benchmark::benchmarks(tokens) } #[proc_macro_attribute] From 905f901e545baea019f0ad1f06609b2d2c7b9d0e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 27 Dec 2022 04:40:58 -0500 Subject: [PATCH 034/146] use the outer macro pattern to to enable #[benchmarks] aggregation --- frame/support/procedural/src/benchmark.rs | 106 ++++++++++++++++------ 1 file changed, 78 insertions(+), 28 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 07e58ba05f64e..a0e0b1e011373 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -8,20 +8,14 @@ use syn::{ punctuated::Punctuated, spanned::Spanned, token::{Comma, Gt, Lt}, - Block, Error, Expr, ExprCall, FnArg, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, Type, + Block, Error, Expr, ExprCall, FnArg, Item, ItemFn, LitInt, Pat, Result, Stmt, Type, }; mod keywords { syn::custom_keyword!(extrinsic_call); syn::custom_keyword!(cfg); -} - -/// A safe wrapper for easily emitting errors in [`quote!`] calls -fn emit_error + Clone, S: Into>(item: &T, message: S) -> TokenStream { - let item = Into::::into(item.clone()); - let message = Into::::into(message); - let span = proc_macro2::TokenStream::from(item).span(); - return syn::Error::new(span, message).to_compile_error().into() + syn::custom_keyword!(benchmark); + syn::custom_keyword!(instance_benchmark); } /// Represents a "bare" block, that is, a the contents of a [`Block`] minus the curly braces. @@ -42,6 +36,7 @@ impl Parse for BareBlock { } /// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. +#[derive(Clone)] struct ParamDef { name: String, typ: Type, @@ -60,6 +55,7 @@ struct RangeArgs { } /// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. +#[derive(Clone)] struct BenchmarkDef { params: Vec, setup_stmts: Vec, @@ -158,13 +154,65 @@ impl BenchmarkDef { } pub fn benchmarks(tokens: TokenStream) -> TokenStream { - let block = parse_macro_input!(tokens as BareBlock); - let contents = block.stmts; - quote! { - #(#contents) - * + let mut block = parse_macro_input!(tokens as BareBlock); + let mut expanded_stmts: Vec = Vec::new(); + let mut benchmark_defs: Vec = Vec::new(); + for stmt in &mut block.stmts { + let mut found_item: Option<(ItemFn, bool)> = None; + if let Stmt::Item(stmt) = stmt { + if let Item::Fn(func) = stmt { + let mut i = 0; + for attr in &func.attrs.clone() { + let mut consumed = false; + if let Some(seg) = attr.path.segments.last() { + if let Ok(_) = + syn::parse::(seg.ident.to_token_stream().into()) + { + consumed = true; + func.attrs.remove(i); + found_item = Some((func.clone(), false)); + } else if let Ok(_) = syn::parse::( + seg.ident.to_token_stream().into(), + ) { + consumed = true; + func.attrs.remove(i); + found_item = Some((func.clone(), true)); + } + } + if !consumed { + i += 1; + } + } + } + } + if let Some((item_fn, is_instance)) = found_item { + // this item is a #[benchmark] or #[instance_benchmark] + + // build a BenchmarkDef from item_fn + let benchmark_def = match BenchmarkDef::from(&item_fn) { + Ok(def) => def, + Err(err) => return err.to_compile_error().into(), + }; + + // expand benchmark_def + let expanded = expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, is_instance); + + expanded_stmts.push(expanded); + benchmark_defs.push(benchmark_def); + } else { + // this is not a benchmark item, copy it in verbatim + expanded_stmts.push(stmt.to_token_stream()); + } } - .into() + + // TODO: we can now do outer macro pattern stuff with benchmark_defs here + + let res = quote! { + #(#expanded_stmts) + * + }; + println!("{}", res.to_string()); + res.into() } /// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable @@ -204,18 +252,8 @@ impl UnrolledParams { } } -pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> TokenStream { - // parse attached item as a function def - let item_fn = parse_macro_input!(tokens as ItemFn); - - // build a BenchmarkDef from item_fn - let benchmark_def = match BenchmarkDef::from(&item_fn) { - Ok(def) => def, - Err(err) => return err.to_compile_error().into(), - }; - +fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool) -> TokenStream2 { // set up variables needed during quoting - let name = item_fn.sig.ident; let krate = quote!(::frame_benchmarking); let home = quote!(::frame_support::benchmarking); let codec = quote!(#krate::frame_support::codec); @@ -310,6 +348,18 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> } } }; - // println!("{}", res.to_string()); - res.into() + res +} + +pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> TokenStream { + // parse attached item as a function def + let item_fn = parse_macro_input!(tokens as ItemFn); + + // build a BenchmarkDef from item_fn + let benchmark_def = match BenchmarkDef::from(&item_fn) { + Ok(def) => def, + Err(err) => return err.to_compile_error().into(), + }; + + expand_benchmark(benchmark_def, &item_fn.sig.ident, is_instance).into() } From 9ae7041bc851b8c96ee139c6f6afd46379f6e541 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 27 Dec 2022 04:57:21 -0500 Subject: [PATCH 035/146] successfully generate SelectedBenchmark :boom: --- frame/support/procedural/src/benchmark.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index a0e0b1e011373..0f898ceaff947 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -157,6 +157,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { let mut block = parse_macro_input!(tokens as BareBlock); let mut expanded_stmts: Vec = Vec::new(); let mut benchmark_defs: Vec = Vec::new(); + let mut benchmark_names: Vec = Vec::new(); for stmt in &mut block.stmts { let mut found_item: Option<(ItemFn, bool)> = None; if let Stmt::Item(stmt) = stmt { @@ -196,6 +197,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { // expand benchmark_def let expanded = expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, is_instance); + benchmark_names.push(item_fn.sig.ident.clone()); expanded_stmts.push(expanded); benchmark_defs.push(benchmark_def); @@ -205,11 +207,17 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { } } - // TODO: we can now do outer macro pattern stuff with benchmark_defs here + // TODO: components() after SelectedBenchmark let res = quote! { #(#expanded_stmts) * + + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + #(#benchmark_names), + * + } }; println!("{}", res.to_string()); res.into() From f989f894c1ae8475fc32ab8abf1695464cfc88d0 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 28 Dec 2022 02:36:38 -0500 Subject: [PATCH 036/146] implement components for SelectedBenchmark --- frame/support/procedural/src/benchmark.rs | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 0f898ceaff947..48b2ee2f4e185 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -158,6 +158,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { let mut expanded_stmts: Vec = Vec::new(); let mut benchmark_defs: Vec = Vec::new(); let mut benchmark_names: Vec = Vec::new(); + let mut any_instance = false; for stmt in &mut block.stmts { let mut found_item: Option<(ItemFn, bool)> = None; if let Stmt::Item(stmt) = stmt { @@ -178,6 +179,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { consumed = true; func.attrs.remove(i); found_item = Some((func.clone(), true)); + any_instance = true; } } if !consumed { @@ -209,6 +211,16 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { // TODO: components() after SelectedBenchmark + let generics = match any_instance { + false => quote!(T), + true => quote!(T, I), + }; + let full_generics = match any_instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + let krate = quote!(::frame_benchmarking); + let res = quote! { #(#expanded_stmts) * @@ -218,6 +230,19 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { #(#benchmark_names), * } + + impl<#full_generics> #krate::BenchmarkingSetup<#generics> for SelectedBenchmark { + fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup<#generics>>::components(&#benchmark_names) + } + ) + * + } + } + } }; println!("{}", res.to_string()); res.into() From 2189e0d1b6383926d6ae1e6af6658db45a7750e7 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 28 Dec 2022 02:55:41 -0500 Subject: [PATCH 037/146] implement instance for SelectedBenchmark --- frame/support/procedural/src/benchmark.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 48b2ee2f4e185..98c0074dc26ee 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -242,6 +242,29 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { * } } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool, + ) -> Result< + #krate::Box< + dyn FnOnce() -> Result<(), #krate::BenchmarkError>, + >, + #krate::BenchmarkError, + > { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup< + T, + I, + >>::instance(&#benchmark_names, components, verify) + } + ) + * + } + } } }; println!("{}", res.to_string()); From c1b36677563d89930784da6f45683a05dda369bb Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 29 Dec 2022 01:59:39 -0500 Subject: [PATCH 038/146] properly track #[extra] --- frame/balances/src/lib.rs | 2 +- frame/support/procedural/src/benchmark.rs | 53 +++++++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 5629b34e720a6..97159947de0cd 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -155,7 +155,7 @@ #[macro_use] mod tests; -mod benchmarking; +// mod benchmarking; mod benchmarking_alt; pub mod migration; mod tests_composite; diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 98c0074dc26ee..95b68d99438af 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -12,10 +12,13 @@ use syn::{ }; mod keywords { - syn::custom_keyword!(extrinsic_call); - syn::custom_keyword!(cfg); - syn::custom_keyword!(benchmark); - syn::custom_keyword!(instance_benchmark); + use syn::custom_keyword; + + custom_keyword!(extrinsic_call); + custom_keyword!(cfg); + custom_keyword!(benchmark); + custom_keyword!(instance_benchmark); + custom_keyword!(extra); } /// Represents a "bare" block, that is, a the contents of a [`Block`] minus the curly braces. @@ -62,6 +65,7 @@ struct BenchmarkDef { extrinsic_call: ExprCall, origin: Expr, verify_stmts: Vec, + extra: bool, } impl BenchmarkDef { @@ -69,6 +73,16 @@ impl BenchmarkDef { pub fn from(item_fn: &ItemFn) -> Result { let mut i = 0; // index of child let mut params: Vec = Vec::new(); + let mut extra = false; + for attr in &item_fn.attrs { + if let Some(segment) = attr.path.segments.last() { + if let Ok(_) = + syn::parse::(segment.ident.to_token_stream().into()) + { + extra = true; + } + } + } for arg in &item_fn.sig.inputs { // parse params such as "x: Linear<0, 1>" let mut name: Option = None; @@ -138,6 +152,7 @@ impl BenchmarkDef { verify_stmts: Vec::from( &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], ), + extra, }) } } @@ -158,6 +173,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { let mut expanded_stmts: Vec = Vec::new(); let mut benchmark_defs: Vec = Vec::new(); let mut benchmark_names: Vec = Vec::new(); + let mut extra_benchmark_names: Vec = Vec::new(); let mut any_instance = false; for stmt in &mut block.stmts { let mut found_item: Option<(ItemFn, bool)> = None; @@ -199,7 +215,12 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { // expand benchmark_def let expanded = expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, is_instance); + + // record benchmark name benchmark_names.push(item_fn.sig.ident.clone()); + if benchmark_def.extra { + extra_benchmark_names.push(item_fn.sig.ident.clone()); + } expanded_stmts.push(expanded); benchmark_defs.push(benchmark_def); @@ -220,6 +241,9 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { true => quote!(T: Config, I: 'static), }; let krate = quote!(::frame_benchmarking); + let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); + let extra_benchmark_names_str: Vec = + extra_benchmark_names.iter().map(|n| n.to_string()).collect(); let res = quote! { #(#expanded_stmts) @@ -266,6 +290,27 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { } } } + #[cfg(any(feature = "runtime-benchmarks", test))] + impl, I: 'static> ::frame_benchmarking::Benchmarking for Pallet + where + T: frame_system::Config, + { + fn benchmarks( + extra: bool, + ) -> ::frame_benchmarking::Vec<::frame_benchmarking::BenchmarkMetadata> { + let mut all_names = #krate::vec![ + #(#benchmark_names_str), + * + ]; + if !extra { + let extra = [ + #(#extra_benchmark_names_str), + * + ]; + all_names.retain(|x| !extra.contains(x)); + } + } + } }; println!("{}", res.to_string()); res.into() From cf0a8974b1772b6ebdbe0e6a083a51c0beae39f1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 29 Dec 2022 03:46:32 -0500 Subject: [PATCH 039/146] working impl for fn benchmarks() --- frame/support/procedural/src/benchmark.rs | 29 +++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 95b68d99438af..8383de5004850 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -232,6 +232,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { // TODO: components() after SelectedBenchmark + // generics let generics = match any_instance { false => quote!(T), true => quote!(T, I), @@ -240,11 +241,23 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { false => quote!(T: Config), true => quote!(T: Config, I: 'static), }; + let krate = quote!(::frame_benchmarking); + + // benchmark name variables let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); let extra_benchmark_names_str: Vec = extra_benchmark_names.iter().map(|n| n.to_string()).collect(); + let mut selected_benchmark_mappings: Vec = Vec::new(); + for i in 0..benchmark_names.len() { + let name_ident = &benchmark_names[i]; + let name_str = &benchmark_names_str[i]; + selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)) + } + println!("benchmark_names: {:?}", benchmark_names); + println!("selected_benchmark_mappings: {:?}", selected_benchmark_mappings); + // emit final quoted tokens let res = quote! { #(#expanded_stmts) * @@ -291,13 +304,13 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { } } #[cfg(any(feature = "runtime-benchmarks", test))] - impl, I: 'static> ::frame_benchmarking::Benchmarking for Pallet + impl<#full_generics> #krate::Benchmarking for Pallet<#generics> where T: frame_system::Config, { fn benchmarks( extra: bool, - ) -> ::frame_benchmarking::Vec<::frame_benchmarking::BenchmarkMetadata> { + ) -> #krate::Vec<#krate::BenchmarkMetadata> { let mut all_names = #krate::vec![ #(#benchmark_names_str), * @@ -309,6 +322,18 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { ]; all_names.retain(|x| !extra.contains(x)); } + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + #(#selected_benchmark_mappings), + *, + _ => panic!("all benchmarks should be selectable") + }; + let components = >::components(&selected_benchmark); + #krate::BenchmarkMetadata { + name: benchmark.as_bytes().to_vec(), + components, + } + }).collect::<#krate::Vec<_>>() } } }; From 199ae057c0e05277edf71ef457843b2b00b01315 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 31 Dec 2022 01:30:15 -0500 Subject: [PATCH 040/146] run_benchmarks WIP --- frame/support/procedural/src/benchmark.rs | 34 +++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 8383de5004850..69cef1a980612 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -230,8 +230,6 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { } } - // TODO: components() after SelectedBenchmark - // generics let generics = match any_instance { false => quote!(T), @@ -243,6 +241,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { }; let krate = quote!(::frame_benchmarking); + let support = quote!(#krate::frame_support); // benchmark name variables let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); @@ -335,6 +334,37 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { } }).collect::<#krate::Vec<_>>() } + + fn run_benchmark( + extrinsic: &[u8], + c: &[(#krate::BenchmarkParameter, u32)], + whitelist: &[#krate::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<#krate::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { + let extrinsic = #krate::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; + let selected_benchmark = match extrinsic { + #(#selected_benchmark_mappings), + *, + _ => return Err("Could not find extrinsic.".into()), + }; + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = as #support::storage::StorageMap< + _, + _, + >>::hashed_key_for( + #krate::whitelisted_caller::(), + ); + whitelist.push(whitelisted_caller_key.into()); + let transactional_layer_key = #krate::TrackedStorageKey::new( + #support::storage::transactional::TRANSACTION_LEVEL_KEY.into(), + ); + whitelist.push(transactional_layer_key); + #krate::benchmarking::set_whitelist(whitelist); + + } } }; println!("{}", res.to_string()); From a7df617e9d04ea34b87ca7956aade9a924728df5 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 1 Jan 2023 01:55:49 -0500 Subject: [PATCH 041/146] finish run_benchmark! impl :boom: --- frame/support/procedural/src/benchmark.rs | 121 +++++++++++++++++++--- 1 file changed, 107 insertions(+), 14 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 69cef1a980612..40eac2f359a72 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -19,6 +19,7 @@ mod keywords { custom_keyword!(benchmark); custom_keyword!(instance_benchmark); custom_keyword!(extra); + custom_keyword!(skip_meta); } /// Represents a "bare" block, that is, a the contents of a [`Block`] minus the curly braces. @@ -66,6 +67,7 @@ struct BenchmarkDef { origin: Expr, verify_stmts: Vec, extra: bool, + skip_meta: bool, } impl BenchmarkDef { @@ -74,12 +76,16 @@ impl BenchmarkDef { let mut i = 0; // index of child let mut params: Vec = Vec::new(); let mut extra = false; + let mut skip_meta = false; for attr in &item_fn.attrs { if let Some(segment) = attr.path.segments.last() { - if let Ok(_) = - syn::parse::(segment.ident.to_token_stream().into()) + if let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) { extra = true; + } else if let Ok(_) = + syn::parse::(segment.ident.to_token_stream().into()) + { + skip_meta = true; } } } @@ -153,6 +159,7 @@ impl BenchmarkDef { &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], ), extra, + skip_meta, }) } } @@ -174,6 +181,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { let mut benchmark_defs: Vec = Vec::new(); let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); + let mut skip_meta_benchmark_names: Vec = Vec::new(); let mut any_instance = false; for stmt in &mut block.stmts { let mut found_item: Option<(ItemFn, bool)> = None; @@ -217,9 +225,13 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { let expanded = expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, is_instance); // record benchmark name - benchmark_names.push(item_fn.sig.ident.clone()); + let name = item_fn.sig.ident; + benchmark_names.push(name.clone()); if benchmark_def.extra { - extra_benchmark_names.push(item_fn.sig.ident.clone()); + extra_benchmark_names.push(name.clone()); + } + if benchmark_def.skip_meta { + skip_meta_benchmark_names.push(name.clone()) } expanded_stmts.push(expanded); @@ -247,14 +259,14 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); let extra_benchmark_names_str: Vec = extra_benchmark_names.iter().map(|n| n.to_string()).collect(); + let skip_meta_benchmark_names_str: Vec = + skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect(); let mut selected_benchmark_mappings: Vec = Vec::new(); for i in 0..benchmark_names.len() { let name_ident = &benchmark_names[i]; let name_str = &benchmark_names_str[i]; selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)) } - println!("benchmark_names: {:?}", benchmark_names); - println!("selected_benchmark_mappings: {:?}", selected_benchmark_mappings); // emit final quoted tokens let res = quote! { @@ -284,9 +296,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { components: &[(#krate::BenchmarkParameter, u32)], verify: bool, ) -> Result< - #krate::Box< - dyn FnOnce() -> Result<(), #krate::BenchmarkError>, - >, + #krate::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError, > { match self { @@ -351,11 +361,8 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { let mut whitelist = whitelist.to_vec(); let whitelisted_caller_key = as #support::storage::StorageMap< - _, - _, - >>::hashed_key_for( - #krate::whitelisted_caller::(), + > as #support::storage::StorageMap<_, _,>>::hashed_key_for( + #krate::whitelisted_caller::() ); whitelist.push(whitelisted_caller_key.into()); let transactional_layer_key = #krate::TrackedStorageKey::new( @@ -363,7 +370,93 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { ); whitelist.push(transactional_layer_key); #krate::benchmarking::set_whitelist(whitelist); + let mut results: #krate::Vec<#krate::BenchmarkResult> = #krate::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + #krate::defer!(#krate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as #krate::BenchmarkingSetup<#generics> + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::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. + #krate::benchmarking::commit_db(); + + // Reset the read/write counter so we don't count operations in the setup process. + #krate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + #krate::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?})", + extrinsic, + c + ); + + let start_pov = #krate::benchmarking::proof_size(); + let start_extrinsic = #krate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = #krate::benchmarking::current_time(); + let end_pov = #krate::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 + #krate::benchmarking::commit_db(); + #krate::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = #krate::benchmarking::read_write_count(); + #krate::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + + // Time the storage root recalculation. + let start_storage_root = #krate::benchmarking::current_time(); + #krate::storage_root(#krate::StateVersion::V1); + let finish_storage_root = #krate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + #krate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + #krate::benchmarking::get_read_and_written_keys() + }; + + results.push(#krate::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); } } }; From 58aa62ef3246da33b17047654200af07d72f98da Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:35:59 -0500 Subject: [PATCH 042/146] import balances transfer_best_case benchmark --- frame/balances/src/benchmarking_alt.rs | 24 +++++++++++++++++++++++ frame/support/procedural/src/benchmark.rs | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 696253d9c3b39..696f4a3aa8f8b 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -40,6 +40,30 @@ benchmarks! { assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } + // Benchmark `transfer` with the best possible condition: + // * Both accounts exist and will continue to exist. + #[instance_benchmark] + #[extra] + fn transfer_best_case() { + let caller = whitelisted_caller(); + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + + // Give the sender account max funds for transfer (their account will never reasonably be killed). + let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + + // Give the recipient account existential deposit (thus their account already exists). + let existential_deposit = T::ExistentialDeposit::get(); + let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); + let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + + assert!(!Balances::::free_balance(&caller).is_zero()); + assert!(!Balances::::free_balance(&recipient).is_zero()); + } + #[instance_benchmark] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 40eac2f359a72..02102688c686d 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -460,7 +460,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { } } }; - println!("{}", res.to_string()); + // println!("{}", res.to_string()); res.into() } From 44bf75294d5de95e45f203d481910bb67bcb0f52 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:38:57 -0500 Subject: [PATCH 043/146] import transfer_keep_alive balances pallet benchmark --- frame/balances/src/benchmarking_alt.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 696f4a3aa8f8b..cb489f14739c7 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -64,6 +64,26 @@ benchmarks! { assert!(!Balances::::free_balance(&recipient).is_zero()); } + // Benchmark `transfer_keep_alive` with the worst possible condition: + // * The recipient account is created. + #[instance_benchmark] + fn transfer_keep_alive() { + let caller = whitelisted_caller(); + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + + // Give the sender account max funds, thus a transfer will not kill account. + let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + let existential_deposit = T::ExistentialDeposit::get(); + let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + + #[extrinsic_call] + transfer_keep_alive(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + + assert!(!Balances::::free_balance(&caller).is_zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + } + #[instance_benchmark] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. From fadbc5b5a35cf5668ec93dff90ae398393c5a289 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:40:45 -0500 Subject: [PATCH 044/146] import set_balance_creating balances pallet benchmark --- frame/balances/src/benchmarking_alt.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index cb489f14739c7..fdaa66e524d05 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -84,6 +84,24 @@ benchmarks! { assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } + // Benchmark `set_balance` coming from ROOT account. This always creates an account. + #[instance_benchmark] + fn set_balance_creating() { + let user: T::AccountId = account("user", 0, SEED); + let user_lookup = T::Lookup::unlookup(user.clone()); + + // Give the user some initial balance. + let existential_deposit = T::ExistentialDeposit::get(); + let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); + + #[extrinsic_call] + set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount); + + assert_eq!(Balances::::free_balance(&user), balance_amount); + assert_eq!(Balances::::reserved_balance(&user), balance_amount); + } + #[instance_benchmark] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. From 8aad678661743ad0c1dabc0245f0a9d9b62c8e73 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:43:30 -0500 Subject: [PATCH 045/146] import set_balance_killing balances pallet benchmark --- frame/balances/src/benchmarking_alt.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index fdaa66e524d05..b76f42462cef8 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -102,6 +102,23 @@ benchmarks! { assert_eq!(Balances::::reserved_balance(&user), balance_amount); } + // Benchmark `set_balance` coming from ROOT account. This always kills an account. + #[instance_benchmark] + fn set_balance_killing() { + let user: T::AccountId = account("user", 0, SEED); + let user_lookup = T::Lookup::unlookup(user.clone()); + + // Give the user some initial balance. + let existential_deposit = T::ExistentialDeposit::get(); + let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); + + #[extrinsic_call] + set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()); + + assert!(Balances::::free_balance(&user).is_zero()); + } + #[instance_benchmark] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. From 316210d0943def2a5b87e4d235c50e500f7abd7c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:45:29 -0500 Subject: [PATCH 046/146] import force_transfer balances pallet benchmark --- frame/balances/src/benchmarking_alt.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index b76f42462cef8..1bce8fc2ac98c 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -119,6 +119,31 @@ benchmarks! { assert!(Balances::::free_balance(&user).is_zero()); } + // Benchmark `force_transfer` extrinsic with the worst possible conditions: + // * Transfer will kill the sender account. + // * Transfer will create the recipient account. + #[instance_benchmark] + fn force_transfer() { + let existential_deposit = T::ExistentialDeposit::get(); + let source: T::AccountId = account("source", 0, SEED); + let source_lookup = T::Lookup::unlookup(source.clone()); + + // Give some multiple of the existential deposit + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&source, balance); + + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user. + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + #[extrinsic_call] + force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); + + assert_eq!(Balances::::free_balance(&source), Zero::zero()); + assert_eq!(Balances::::free_balance(&recipient), transfer_amount); + } + #[instance_benchmark] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. From 8218b3f507ae4b07fbd8a8cc18c26c4d968a0107 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:46:36 -0500 Subject: [PATCH 047/146] add #[extra] annotation and docs to transfer_increasing_users --- frame/balances/src/benchmarking_alt.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 1bce8fc2ac98c..5f1c556202622 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -144,7 +144,12 @@ benchmarks! { assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } + + // This benchmark performs the same operation as `transfer` in the worst case scenario, + // but additionally introduces many new users into the storage, increasing the the merkle + // trie and PoV size. #[instance_benchmark] + #[extra] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. let existential_deposit = T::ExistentialDeposit::get(); From eae94641d730e80b215e2225e0c329ad8bba235a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:49:04 -0500 Subject: [PATCH 048/146] import transfer_all balances pallet benchmark --- frame/balances/src/benchmarking_alt.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 5f1c556202622..07f85b8ab8e87 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -180,4 +180,25 @@ benchmarks! { assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } + + // Benchmark `transfer_all` with the worst possible condition: + // * The recipient account is created + // * The sender is killed + #[instance_benchmark] + fn transfer_all() { + let caller = whitelisted_caller(); + let recipient: T::AccountId = account("recipient", 0, SEED); + let recipient_lookup = T::Lookup::unlookup(recipient.clone()); + + // Give some multiple of the existential deposit + let existential_deposit = T::ExistentialDeposit::get(); + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&caller, balance); + + #[extrinsic_call] + transfer_all(RawOrigin::Signed(caller.clone()), recipient_lookup, false); + + assert!(Balances::::free_balance(&caller).is_zero()); + assert_eq!(Balances::::free_balance(&recipient), balance); + } } From 057dc07a423d4d1b438b40c3dc798e75cc8165a0 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:50:37 -0500 Subject: [PATCH 049/146] import force_unreserve balances pallet benchmark --- frame/balances/src/benchmarking_alt.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 07f85b8ab8e87..1ef67f371921b 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -201,4 +201,26 @@ benchmarks! { assert!(Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), balance); } + + #[instance_benchmark] + fn force_unreserve() { + let user: T::AccountId = account("user", 0, SEED); + let user_lookup = T::Lookup::unlookup(user.clone()); + + // Give some multiple of the existential deposit + let existential_deposit = T::ExistentialDeposit::get(); + let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let _ = as Currency<_>>::make_free_balance_be(&user, balance); + + // Reserve the balance + as ReservableCurrency<_>>::reserve(&user, balance)?; + assert_eq!(Balances::::reserved_balance(&user), balance); + assert!(Balances::::free_balance(&user).is_zero()); + + #[extrinsic_call] + force_unreserve(RawOrigin::Root, user_lookup, balance); + + assert!(Balances::::reserved_balance(&user).is_zero()); + assert_eq!(Balances::::free_balance(&user), balance); + } } From 4f70c523d622c37246939d00f0aea96a403c064a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:53:30 -0500 Subject: [PATCH 050/146] prepare to implement impl_benchmark_test_suite! --- frame/balances/src/benchmarking_alt.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index 1ef67f371921b..e38c132dd5669 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -223,4 +223,12 @@ benchmarks! { assert!(Balances::::reserved_balance(&user).is_zero()); assert_eq!(Balances::::free_balance(&user), balance); } + + // use frame_benchmarking::impl_benchmark_test_suite; + + // impl_benchmark_test_suite!( + // Balances, + // crate::tests_composite::ExtBuilder::default().build(), + // crate::tests_composite::Test, + // ); } From 02bf5833fc5f03c15588d1a8369470566b64e6b8 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 2 Jan 2023 00:54:38 -0500 Subject: [PATCH 051/146] ensure tests cover #[extra] before and after #[benchmark] tag --- frame/balances/src/benchmarking_alt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs index e38c132dd5669..1a61833aee1d8 100644 --- a/frame/balances/src/benchmarking_alt.rs +++ b/frame/balances/src/benchmarking_alt.rs @@ -148,8 +148,8 @@ benchmarks! { // This benchmark performs the same operation as `transfer` in the worst case scenario, // but additionally introduces many new users into the storage, increasing the the merkle // trie and PoV size. - #[instance_benchmark] #[extra] + #[instance_benchmark] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. let existential_deposit = T::ExistentialDeposit::get(); From f4c3035f4bd01bfed227076d15071c11a39a05f8 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 3 Jan 2023 10:03:58 -0500 Subject: [PATCH 052/146] refactor --- frame/balances/src/benchmarking.rs | 122 ++++++++----- frame/balances/src/benchmarking_alt.rs | 234 ------------------------- frame/balances/src/lib.rs | 3 +- 3 files changed, 78 insertions(+), 281 deletions(-) delete mode 100644 frame/balances/src/benchmarking_alt.rs diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 206adba0f044b..0d87992c76d44 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,23 +19,25 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; +use frame_support::benchmarking::benchmarks; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; -use frame_system::RawOrigin; -use sp_runtime::traits::Bounded; +benchmarks! { + use super::*; -use crate::Pallet as Balances; + use frame_system::RawOrigin; + //use sp_runtime::traits::Bounded; -const SEED: u32 = 0; -// existential deposit multiplier -const ED_MULTIPLIER: u32 = 10; + use crate::Pallet as Balances; -benchmarks_instance_pallet! { - // Benchmark `transfer` extrinsic with the worst possible conditions: - // * Transfer will kill the sender account. - // * Transfer will create the recipient account. - transfer { + const SEED: u32 = 0; + // existential deposit multiplier + const ED_MULTIPLIER: u32 = 10; + + use frame_benchmarking::{account, whitelisted_caller}; + use frame_support::benchmarking::*; + + #[instance_benchmark] + fn transfer() { let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); @@ -47,17 +49,21 @@ benchmarks_instance_pallet! { // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } // Benchmark `transfer` with the best possible condition: // * Both accounts exist and will continue to exist. + #[instance_benchmark] #[extra] - transfer_best_case { + fn transfer_best_case() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); @@ -69,15 +75,18 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert!(!Balances::::free_balance(&caller).is_zero()); assert!(!Balances::::free_balance(&recipient).is_zero()); } // Benchmark `transfer_keep_alive` with the worst possible condition: // * The recipient account is created. - transfer_keep_alive { + #[instance_benchmark] + fn transfer_keep_alive() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); @@ -86,14 +95,17 @@ benchmarks_instance_pallet! { let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); let existential_deposit = T::ExistentialDeposit::get(); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + transfer_keep_alive(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert!(!Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } // Benchmark `set_balance` coming from ROOT account. This always creates an account. - set_balance_creating { + #[instance_benchmark] + fn set_balance_creating() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -101,14 +113,17 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - }: set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount) - verify { + + #[extrinsic_call] + set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount); + assert_eq!(Balances::::free_balance(&user), balance_amount); assert_eq!(Balances::::reserved_balance(&user), balance_amount); } // Benchmark `set_balance` coming from ROOT account. This always kills an account. - set_balance_killing { + #[instance_benchmark] + fn set_balance_killing() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -116,15 +131,18 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - }: set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()) - verify { + + #[extrinsic_call] + set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()); + assert!(Balances::::free_balance(&user).is_zero()); } // Benchmark `force_transfer` extrinsic with the worst possible conditions: // * Transfer will kill the sender account. // * Transfer will create the recipient account. - force_transfer { + #[instance_benchmark] + fn force_transfer() { let existential_deposit = T::ExistentialDeposit::get(); let source: T::AccountId = account("source", 0, SEED); let source_lookup = T::Lookup::unlookup(source.clone()); @@ -137,19 +155,22 @@ benchmarks_instance_pallet! { let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - }: force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&source), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } + // This benchmark performs the same operation as `transfer` in the worst case scenario, // but additionally introduces many new users into the storage, increasing the the merkle // trie and PoV size. #[extra] - transfer_increasing_users { + #[instance_benchmark] + fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. - let u in 0 .. 1_000; let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); @@ -161,17 +182,20 @@ benchmarks_instance_pallet! { // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); // Create a bunch of users in storage. - for i in 0 .. u { + for i in 0..u { // The `account` function uses `blake2_256` to generate unique accounts, so these // should be quite random and evenly distributed in the trie. let new_user: T::AccountId = account("new_user", i, SEED); let _ = as Currency<_>>::make_free_balance_be(&new_user, balance); } - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } @@ -179,7 +203,8 @@ benchmarks_instance_pallet! { // Benchmark `transfer_all` with the worst possible condition: // * The recipient account is created // * The sender is killed - transfer_all { + #[instance_benchmark] + fn transfer_all() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); @@ -188,13 +213,16 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, false) - verify { + + #[extrinsic_call] + transfer_all(RawOrigin::Signed(caller.clone()), recipient_lookup, false); + assert!(Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), balance); } - force_unreserve { + #[instance_benchmark] + fn force_unreserve() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -208,15 +236,19 @@ benchmarks_instance_pallet! { assert_eq!(Balances::::reserved_balance(&user), balance); assert!(Balances::::free_balance(&user).is_zero()); - }: _(RawOrigin::Root, user_lookup, balance) - verify { + #[extrinsic_call] + force_unreserve(RawOrigin::Root, user_lookup, balance); + assert!(Balances::::reserved_balance(&user).is_zero()); assert_eq!(Balances::::free_balance(&user), balance); } + // TODO: re-implement + use frame_benchmarking::impl_benchmark_test_suite; + impl_benchmark_test_suite!( Balances, crate::tests_composite::ExtBuilder::default().build(), crate::tests_composite::Test, - ) + ); } diff --git a/frame/balances/src/benchmarking_alt.rs b/frame/balances/src/benchmarking_alt.rs deleted file mode 100644 index 1a61833aee1d8..0000000000000 --- a/frame/balances/src/benchmarking_alt.rs +++ /dev/null @@ -1,234 +0,0 @@ -#![cfg(feature = "runtime-benchmarks")] - -use frame_support::benchmarking::benchmarks; - -benchmarks! { - use super::*; - - use frame_system::RawOrigin; - //use sp_runtime::traits::Bounded; - - use crate::Pallet as Balances; - - const SEED: u32 = 0; - // existential deposit multiplier - const ED_MULTIPLIER: u32 = 10; - - use frame_benchmarking::{account, whitelisted_caller}; - use frame_support::benchmarking::*; - - #[instance_benchmark] - fn transfer() { - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, - // and reap this user. - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = - existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - - #[extrinsic_call] - transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); - - assert_eq!(Balances::::free_balance(&caller), Zero::zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); - } - - // Benchmark `transfer` with the best possible condition: - // * Both accounts exist and will continue to exist. - #[instance_benchmark] - #[extra] - fn transfer_best_case() { - let caller = whitelisted_caller(); - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - - // Give the sender account max funds for transfer (their account will never reasonably be killed). - let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); - - // Give the recipient account existential deposit (thus their account already exists). - let existential_deposit = T::ExistentialDeposit::get(); - let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); - let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - - #[extrinsic_call] - transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); - - assert!(!Balances::::free_balance(&caller).is_zero()); - assert!(!Balances::::free_balance(&recipient).is_zero()); - } - - // Benchmark `transfer_keep_alive` with the worst possible condition: - // * The recipient account is created. - #[instance_benchmark] - fn transfer_keep_alive() { - let caller = whitelisted_caller(); - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - - // Give the sender account max funds, thus a transfer will not kill account. - let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); - let existential_deposit = T::ExistentialDeposit::get(); - let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - - #[extrinsic_call] - transfer_keep_alive(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); - - assert!(!Balances::::free_balance(&caller).is_zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); - } - - // Benchmark `set_balance` coming from ROOT account. This always creates an account. - #[instance_benchmark] - fn set_balance_creating() { - let user: T::AccountId = account("user", 0, SEED); - let user_lookup = T::Lookup::unlookup(user.clone()); - - // Give the user some initial balance. - let existential_deposit = T::ExistentialDeposit::get(); - let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - - #[extrinsic_call] - set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount); - - assert_eq!(Balances::::free_balance(&user), balance_amount); - assert_eq!(Balances::::reserved_balance(&user), balance_amount); - } - - // Benchmark `set_balance` coming from ROOT account. This always kills an account. - #[instance_benchmark] - fn set_balance_killing() { - let user: T::AccountId = account("user", 0, SEED); - let user_lookup = T::Lookup::unlookup(user.clone()); - - // Give the user some initial balance. - let existential_deposit = T::ExistentialDeposit::get(); - let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - - #[extrinsic_call] - set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()); - - assert!(Balances::::free_balance(&user).is_zero()); - } - - // Benchmark `force_transfer` extrinsic with the worst possible conditions: - // * Transfer will kill the sender account. - // * Transfer will create the recipient account. - #[instance_benchmark] - fn force_transfer() { - let existential_deposit = T::ExistentialDeposit::get(); - let source: T::AccountId = account("source", 0, SEED); - let source_lookup = T::Lookup::unlookup(source.clone()); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&source, balance); - - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user. - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - - #[extrinsic_call] - force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); - - assert_eq!(Balances::::free_balance(&source), Zero::zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); - } - - - // This benchmark performs the same operation as `transfer` in the worst case scenario, - // but additionally introduces many new users into the storage, increasing the the merkle - // trie and PoV size. - #[extra] - #[instance_benchmark] - fn transfer_increasing_users(u: Linear<0, 1_000>) { - // 1_000 is not very much, but this upper bound can be controlled by the CLI. - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, - // and reap this user. - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = - existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - - // Create a bunch of users in storage. - for i in 0..u { - // The `account` function uses `blake2_256` to generate unique accounts, so these - // should be quite random and evenly distributed in the trie. - let new_user: T::AccountId = account("new_user", i, SEED); - let _ = as Currency<_>>::make_free_balance_be(&new_user, balance); - } - - #[extrinsic_call] - transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); - - assert_eq!(Balances::::free_balance(&caller), Zero::zero()); - assert_eq!(Balances::::free_balance(&recipient), transfer_amount); - } - - // Benchmark `transfer_all` with the worst possible condition: - // * The recipient account is created - // * The sender is killed - #[instance_benchmark] - fn transfer_all() { - let caller = whitelisted_caller(); - let recipient: T::AccountId = account("recipient", 0, SEED); - let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - - // Give some multiple of the existential deposit - let existential_deposit = T::ExistentialDeposit::get(); - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - - #[extrinsic_call] - transfer_all(RawOrigin::Signed(caller.clone()), recipient_lookup, false); - - assert!(Balances::::free_balance(&caller).is_zero()); - assert_eq!(Balances::::free_balance(&recipient), balance); - } - - #[instance_benchmark] - fn force_unreserve() { - let user: T::AccountId = account("user", 0, SEED); - let user_lookup = T::Lookup::unlookup(user.clone()); - - // Give some multiple of the existential deposit - let existential_deposit = T::ExistentialDeposit::get(); - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - let _ = as Currency<_>>::make_free_balance_be(&user, balance); - - // Reserve the balance - as ReservableCurrency<_>>::reserve(&user, balance)?; - assert_eq!(Balances::::reserved_balance(&user), balance); - assert!(Balances::::free_balance(&user).is_zero()); - - #[extrinsic_call] - force_unreserve(RawOrigin::Root, user_lookup, balance); - - assert!(Balances::::reserved_balance(&user).is_zero()); - assert_eq!(Balances::::free_balance(&user), balance); - } - - // use frame_benchmarking::impl_benchmark_test_suite; - - // impl_benchmark_test_suite!( - // Balances, - // crate::tests_composite::ExtBuilder::default().build(), - // crate::tests_composite::Test, - // ); -} diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 97159947de0cd..000ce852bce1d 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -155,8 +155,7 @@ #[macro_use] mod tests; -// mod benchmarking; -mod benchmarking_alt; +pub mod benchmarking; pub mod migration; mod tests_composite; mod tests_local; From 03c2a7f75531c6574aaafd1a7dcc5d2123e44b31 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 3 Jan 2023 10:05:41 -0500 Subject: [PATCH 053/146] clean up --- frame/balances/src/benchmarking.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 0d87992c76d44..0365b2cbd2525 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -244,11 +244,11 @@ benchmarks! { } // TODO: re-implement - use frame_benchmarking::impl_benchmark_test_suite; + // use frame_benchmarking::impl_benchmark_test_suite; - impl_benchmark_test_suite!( - Balances, - crate::tests_composite::ExtBuilder::default().build(), - crate::tests_composite::Test, - ); + // impl_benchmark_test_suite!( + // Balances, + // crate::tests_composite::ExtBuilder::default().build(), + // crate::tests_composite::Test, + // ); } From 3023e48fab398514834a995d6de40da41a684583 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 3 Jan 2023 10:17:16 -0500 Subject: [PATCH 054/146] fix --- frame/balances/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 000ce852bce1d..99d77a3e73361 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -155,7 +155,7 @@ #[macro_use] mod tests; -pub mod benchmarking; +mod benchmarking; pub mod migration; mod tests_composite; mod tests_local; From 90d839f697cfa8448523b0717af98eced3a207d2 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 3 Jan 2023 12:26:35 -0500 Subject: [PATCH 055/146] move to outer --- frame/balances/src/benchmarking.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 0365b2cbd2525..8693804a5f7a0 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -21,21 +21,21 @@ use frame_support::benchmarking::benchmarks; -benchmarks! { - use super::*; +use super::*; - use frame_system::RawOrigin; - //use sp_runtime::traits::Bounded; +use frame_system::RawOrigin; +//use sp_runtime::traits::Bounded; - use crate::Pallet as Balances; +use crate::Pallet as Balances; - const SEED: u32 = 0; - // existential deposit multiplier - const ED_MULTIPLIER: u32 = 10; +const SEED: u32 = 0; +// existential deposit multiplier +const ED_MULTIPLIER: u32 = 10; - use frame_benchmarking::{account, whitelisted_caller}; - use frame_support::benchmarking::*; +use frame_benchmarking::{account, whitelisted_caller}; +use frame_support::benchmarking::*; +benchmarks! { #[instance_benchmark] fn transfer() { let existential_deposit = T::ExistentialDeposit::get(); From 700124ec6a49dc7e7571b0529b20b5d805ee4a6b Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 4 Jan 2023 10:58:05 -0500 Subject: [PATCH 056/146] switch to benchmarks/instance_benchmarks --- frame/balances/src/benchmarking.rs | 22 ++++++++--------- frame/support/procedural/src/benchmark.rs | 29 +++++++---------------- frame/support/procedural/src/lib.rs | 7 +++++- frame/support/src/lib.rs | 2 +- 4 files changed, 25 insertions(+), 35 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 8693804a5f7a0..ff5249d25ce98 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -19,8 +19,6 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_support::benchmarking::benchmarks; - use super::*; use frame_system::RawOrigin; @@ -35,8 +33,8 @@ const ED_MULTIPLIER: u32 = 10; use frame_benchmarking::{account, whitelisted_caller}; use frame_support::benchmarking::*; -benchmarks! { - #[instance_benchmark] +instance_benchmarks! { + #[benchmark] fn transfer() { let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); @@ -61,7 +59,7 @@ benchmarks! { // Benchmark `transfer` with the best possible condition: // * Both accounts exist and will continue to exist. - #[instance_benchmark] + #[benchmark] #[extra] fn transfer_best_case() { let caller = whitelisted_caller(); @@ -85,7 +83,7 @@ benchmarks! { // Benchmark `transfer_keep_alive` with the worst possible condition: // * The recipient account is created. - #[instance_benchmark] + #[benchmark] fn transfer_keep_alive() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); @@ -104,7 +102,7 @@ benchmarks! { } // Benchmark `set_balance` coming from ROOT account. This always creates an account. - #[instance_benchmark] + #[benchmark] fn set_balance_creating() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -122,7 +120,7 @@ benchmarks! { } // Benchmark `set_balance` coming from ROOT account. This always kills an account. - #[instance_benchmark] + #[benchmark] fn set_balance_killing() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -141,7 +139,7 @@ benchmarks! { // Benchmark `force_transfer` extrinsic with the worst possible conditions: // * Transfer will kill the sender account. // * Transfer will create the recipient account. - #[instance_benchmark] + #[benchmark] fn force_transfer() { let existential_deposit = T::ExistentialDeposit::get(); let source: T::AccountId = account("source", 0, SEED); @@ -168,7 +166,7 @@ benchmarks! { // but additionally introduces many new users into the storage, increasing the the merkle // trie and PoV size. #[extra] - #[instance_benchmark] + #[benchmark] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. let existential_deposit = T::ExistentialDeposit::get(); @@ -203,7 +201,7 @@ benchmarks! { // Benchmark `transfer_all` with the worst possible condition: // * The recipient account is created // * The sender is killed - #[instance_benchmark] + #[benchmark] fn transfer_all() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); @@ -221,7 +219,7 @@ benchmarks! { assert_eq!(Balances::::free_balance(&recipient), balance); } - #[instance_benchmark] + #[benchmark] fn force_unreserve() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 02102688c686d..a9d96077c76b0 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -17,7 +17,6 @@ mod keywords { custom_keyword!(extrinsic_call); custom_keyword!(cfg); custom_keyword!(benchmark); - custom_keyword!(instance_benchmark); custom_keyword!(extra); custom_keyword!(skip_meta); } @@ -175,44 +174,32 @@ impl BenchmarkDef { } } -pub fn benchmarks(tokens: TokenStream) -> TokenStream { +pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { let mut block = parse_macro_input!(tokens as BareBlock); let mut expanded_stmts: Vec = Vec::new(); let mut benchmark_defs: Vec = Vec::new(); let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); - let mut any_instance = false; for stmt in &mut block.stmts { - let mut found_item: Option<(ItemFn, bool)> = None; + let mut found_item: Option = None; if let Stmt::Item(stmt) = stmt { if let Item::Fn(func) = stmt { let mut i = 0; for attr in &func.attrs.clone() { - let mut consumed = false; if let Some(seg) = attr.path.segments.last() { if let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) { - consumed = true; func.attrs.remove(i); - found_item = Some((func.clone(), false)); - } else if let Ok(_) = syn::parse::( - seg.ident.to_token_stream().into(), - ) { - consumed = true; - func.attrs.remove(i); - found_item = Some((func.clone(), true)); - any_instance = true; + found_item = Some(func.clone()); + i += 1; } } - if !consumed { - i += 1; - } } } } - if let Some((item_fn, is_instance)) = found_item { + if let Some(item_fn) = found_item { // this item is a #[benchmark] or #[instance_benchmark] // build a BenchmarkDef from item_fn @@ -222,7 +209,7 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { }; // expand benchmark_def - let expanded = expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, is_instance); + let expanded = expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, instance); // record benchmark name let name = item_fn.sig.ident; @@ -243,11 +230,11 @@ pub fn benchmarks(tokens: TokenStream) -> TokenStream { } // generics - let generics = match any_instance { + let generics = match instance { false => quote!(T), true => quote!(T, I), }; - let full_generics = match any_instance { + let full_generics = match instance { false => quote!(T: Config), true => quote!(T: Config, I: 'static), }; diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 2924dfcf3df27..3229057a9bb44 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -482,7 +482,12 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { #[proc_macro] pub fn benchmarks(tokens: TokenStream) -> TokenStream { - benchmark::benchmarks(tokens) + benchmark::benchmarks(tokens, false) +} + +#[proc_macro] +pub fn instance_benchmarks(tokens: TokenStream) -> TokenStream { + benchmark::benchmarks(tokens, true) } #[proc_macro_attribute] diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index e4436bfcafac0..ddabe62eb0ccf 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2751,7 +2751,7 @@ pub mod pallet_macros { sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); pub mod benchmarking { - pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmark}; + pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmarks}; #[doc(hidden)] pub use static_assertions::assert_impl_all; From 23717681e6b1c66c41ed5c274fe3b2883b825823 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 13:29:38 -0500 Subject: [PATCH 057/146] test impl almost done, strange compiler error --- frame/balances/src/benchmarking.rs | 15 ++- frame/support/procedural/src/benchmark.rs | 116 +++++++++++++++++++++- 2 files changed, 120 insertions(+), 11 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index ff5249d25ce98..7d9bac40fd8e8 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -30,7 +30,7 @@ const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; -use frame_benchmarking::{account, whitelisted_caller}; +use frame_benchmarking::{account, impl_benchmark_test_suite, whitelisted_caller}; use frame_support::benchmarking::*; instance_benchmarks! { @@ -241,12 +241,9 @@ instance_benchmarks! { assert_eq!(Balances::::free_balance(&user), balance); } - // TODO: re-implement - // use frame_benchmarking::impl_benchmark_test_suite; - - // impl_benchmark_test_suite!( - // Balances, - // crate::tests_composite::ExtBuilder::default().build(), - // crate::tests_composite::Test, - // ); + impl_benchmark_test_suite!( + Balances, + crate::tests_composite::ExtBuilder::default().build(), + crate::tests_composite::Test, + ); } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index a9d96077c76b0..4cf5325b55da9 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -249,10 +249,17 @@ pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { let skip_meta_benchmark_names_str: Vec = skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect(); let mut selected_benchmark_mappings: Vec = Vec::new(); + let mut benchmarks_by_name_mappings: Vec = Vec::new(); + let test_idents: Vec = benchmark_names_str + .iter() + .map(|n| Ident::new(format!("test_{}", n).as_str(), Span::call_site())) + .collect(); for i in 0..benchmark_names.len() { let name_ident = &benchmark_names[i]; let name_str = &benchmark_names_str[i]; - selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)) + let test_ident = &test_idents[i]; + selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); + benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident)) } // emit final quoted tokens @@ -446,8 +453,31 @@ pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { return Ok(results); } } + + #[cfg(test)] + impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config { + /// 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<(), #krate::BenchmarkError> { + let name = #krate::str::from_utf8(name) + .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + #(#benchmarks_by_name_mappings), + *, + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } }; - // println!("{}", res.to_string()); + println!("{}", res.to_string()); res.into() } @@ -498,6 +528,7 @@ fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool let mut extrinsic_call = benchmark_def.extrinsic_call; let origin = benchmark_def.origin; let verify_stmts = benchmark_def.verify_stmts; + let test_ident = Ident::new(format!("test_{}", name.to_string()).as_str(), Span::call_site()); // unroll params (prepare for quoting) let unrolled = UnrolledParams::from(&benchmark_def.params); @@ -583,6 +614,87 @@ fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool })) } } + + #[cfg(test)] + impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config { + #[allow(unused)] + fn #test_ident() -> Result<(), #krate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::#name; + let components = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::components(&selected_benchmark); + let execute_benchmark = | + c: #krate::Vec<(#krate::BenchmarkParameter, u32)> + | -> Result<(), #krate::BenchmarkError> { + // Always reset the state after the benchmark. + #krate::defer!(#krate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::instance(&selected_benchmark, &c, true)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::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(|_| { + #krate::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 = #krate::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: #krate::Vec<(#krate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + return Ok(()); + } + } }; res } From d9c77e6eacaaa6523787d5fe7da2d85cc439fac1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 14:40:42 -0500 Subject: [PATCH 058/146] benchmark test suites working :boom: --- frame/support/procedural/src/benchmark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 4cf5325b55da9..50f5df0b1a494 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -259,7 +259,7 @@ pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { let name_str = &benchmark_names_str[i]; let test_ident = &test_idents[i]; selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); - benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident)) + benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) } // emit final quoted tokens @@ -477,7 +477,7 @@ pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { } } }; - println!("{}", res.to_string()); + // println!("{}", res.to_string()); res.into() } From d8fb36a8927bb73572afeb8e49d247a2e263b8f4 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 15:34:13 -0500 Subject: [PATCH 059/146] clean up --- frame/balances/src/benchmarking.rs | 9 +++------ frame/support/procedural/src/benchmark.rs | 2 -- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 7d9bac40fd8e8..c3917a0078a1b 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -20,19 +20,16 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; +use crate::Pallet as Balances; +use frame_benchmarking::{account, impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::benchmarking::*; use frame_system::RawOrigin; -//use sp_runtime::traits::Bounded; - -use crate::Pallet as Balances; const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; -use frame_benchmarking::{account, impl_benchmark_test_suite, whitelisted_caller}; -use frame_support::benchmarking::*; - instance_benchmarks! { #[benchmark] fn transfer() { diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 50f5df0b1a494..87993c6d03845 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -15,7 +15,6 @@ mod keywords { use syn::custom_keyword; custom_keyword!(extrinsic_call); - custom_keyword!(cfg); custom_keyword!(benchmark); custom_keyword!(extra); custom_keyword!(skip_meta); @@ -477,7 +476,6 @@ pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { } } }; - // println!("{}", res.to_string()); res.into() } From 63358d8dbfe1b24a3bf5e142ed358f57c56c669a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 15:47:08 -0500 Subject: [PATCH 060/146] add stub and basic parsing for where_clause --- frame/support/procedural/src/benchmark.rs | 6 ++++++ frame/support/procedural/src/lib.rs | 5 +++++ frame/support/src/lib.rs | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 87993c6d03845..d7dcb1eeced99 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -9,6 +9,7 @@ use syn::{ spanned::Spanned, token::{Comma, Gt, Lt}, Block, Error, Expr, ExprCall, FnArg, Item, ItemFn, LitInt, Pat, Result, Stmt, Type, + WhereClause, }; mod keywords { @@ -709,3 +710,8 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> expand_benchmark(benchmark_def, &item_fn.sig.ident, is_instance).into() } + +pub fn where_clause(tokens: TokenStream) -> TokenStream { + let _clause = parse_macro_input!(tokens as WhereClause); + quote!().into() +} diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 3229057a9bb44..fd7ebba27e2a4 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -500,6 +500,11 @@ pub fn instance_benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStrea benchmark::benchmark(attrs, tokens, true) } +#[proc_macro] +pub fn where_clause(tokens: TokenStream) -> TokenStream { + benchmark::where_clause(tokens) +} + /// Execute the annotated function in a new storage transaction. /// /// The return type of the annotated function must be `Result`. All changes to storage performed diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index ddabe62eb0ccf..149df95ca884e 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2751,7 +2751,7 @@ pub mod pallet_macros { sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); pub mod benchmarking { - pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmarks}; + pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmarks, where_clause}; #[doc(hidden)] pub use static_assertions::assert_impl_all; From 520d7eb854b58f9c139bed1cf0268b6eb3a62369 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 18:20:46 -0500 Subject: [PATCH 061/146] working except where clause and extrinsic calls containing method chains --- frame/message-queue/src/benchmarking.rs | 98 ++++++++++++++--------- frame/support/procedural/src/benchmark.rs | 87 ++++++++++++++------ 2 files changed, 120 insertions(+), 65 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 9cd6b75e4d0ae..03609d60f000f 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -22,13 +22,13 @@ use super::{mock_helpers::*, Pallet as MessageQueue, *}; -use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_support::traits::Get; +use frame_benchmarking::whitelisted_caller; +use frame_support::{benchmarking::*, traits::Get}; use frame_system::RawOrigin; use sp_std::prelude::*; benchmarks! { - where_clause { + where_clause! { where // NOTE: We need to generate multiple origins, therefore Origin is `From`. The // `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be @@ -38,52 +38,59 @@ benchmarks! { } // Worst case path of `ready_ring_knit`. - ready_ring_knit { + #[benchmark] + fn ready_ring_knit() { let mid: MessageOriginOf:: = 1.into(); build_ring::(&[0.into(), mid.clone(), 2.into()]); unknit::(&mid); assert_ring::(&[0.into(), 2.into()]); - let mut neighbours = None; - }: { - neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); - } verify { + + #[extrinsic_call] + let neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); + // The neighbours needs to be modified manually. BookStateFor::::mutate(&mid, |b| { b.ready_neighbours = neighbours }); assert_ring::(&[0.into(), 2.into(), mid]); } // Worst case path of `ready_ring_unknit`. - ready_ring_unknit { + #[benchmark] + fn ready_ring_unknit() { build_ring::(&[0.into(), 1.into(), 2.into()]); assert_ring::(&[0.into(), 1.into(), 2.into()]); let o: MessageOriginOf:: = 0.into(); let neighbours = BookStateFor::::get(&o).ready_neighbours.unwrap(); - }: { + + #[extrinsic_call] MessageQueue::::ready_ring_unknit(&o, neighbours); - } verify { + assert_ring::(&[1.into(), 2.into()]); } // `service_queues` without any queue processing. - service_queue_base { - }: { - MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX) + #[benchmark] + fn service_queue_base() { + #[extrinsic_call] + MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX); } // `service_page` without any message processing but with page completion. - service_page_base_completion { + #[benchmark] + fn service_page_base_completion() { let origin: MessageOriginOf = 0.into(); let page = PageOf::::default(); Pages::::insert(&origin, 0, &page); let mut book_state = single_page_book::(); let mut meter = WeightMeter::max_limit(); let limit = Weight::MAX; - }: { - MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit) + + #[extrinsic_call] + MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); } // `service_page` without any message processing and without page completion. - service_page_base_no_completion { + #[benchmark] + fn service_page_base_no_completion() { let origin: MessageOriginOf = 0.into(); let mut page = PageOf::::default(); // Mock the storage such that `is_complete` returns `false` but `peek_first` returns `None`. @@ -93,21 +100,25 @@ benchmarks! { let mut book_state = single_page_book::(); let mut meter = WeightMeter::max_limit(); let limit = Weight::MAX; - }: { - MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit) + + #[extrinsic_call] + MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); } // Processing a single message from a page. - service_page_item { + #[benchmark] + fn service_page_item() { let msg = vec![1u8; MaxMessageLenOf::::get() as usize]; let mut page = page::(&msg.clone()); let mut book = book_for::(&page); assert!(page.peek_first().is_some(), "There is one message"); let mut weight = WeightMeter::max_limit(); - }: { + + #[extrinsic_call] let status = MessageQueue::::service_page_item(&0u32.into(), 0, &mut book, &mut page, &mut weight, Weight::MAX); + assert_eq!(status, ItemExecutionStatus::Executed(true)); - } verify { + // Check that it was processed. assert_last_event::(Event::Processed { hash: T::Hashing::hash(&msg), origin: 0.into(), @@ -119,17 +130,20 @@ benchmarks! { } // Worst case for calling `bump_service_head`. - bump_service_head { + #[benchmark] + fn bump_service_head() { setup_bump_service_head::(0.into(), 10.into()); let mut weight = WeightMeter::max_limit(); - }: { + + #[extrinsic_call] MessageQueue::::bump_service_head(&mut weight); - } verify { + assert_eq!(ServiceHead::::get().unwrap(), 10u32.into()); assert_eq!(weight.consumed, T::WeightInfo::bump_service_head()); } - reap_page { + #[benchmark] + fn reap_page() { // Mock the storage to get a *cullable* but not *reapable* page. let origin: MessageOriginOf = 0.into(); let mut book = single_page_book::(); @@ -148,16 +162,19 @@ benchmarks! { BookStateFor::::insert(&origin, &book); assert!(Pages::::contains_key(&origin, 0)); - }: _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0) - verify { + #[extrinsic_call] + reap_page(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); + assert_last_event::(Event::PageReaped{ origin: 0.into(), index: 0 }.into()); assert!(!Pages::::contains_key(&origin, 0)); } // Worst case for `execute_overweight` where the page is removed as completed. // - // The worst case occurs when executing the last message in a page of which all are skipped since it is using `peek_index` which has linear complexities. - execute_overweight_page_removed { + // The worst case occurs when executing the last message in a page of which all are skipped + // since it is using `peek_index` which has linear complexities. + #[benchmark] + fn execute_overweight_page_removed() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); // Skip all messages. @@ -168,10 +185,10 @@ benchmarks! { let book = book_for::(&page); Pages::::insert(&origin, 0, &page); BookStateFor::::insert(&origin, &book); - }: { - MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap() - } - verify { + + #[extrinsic_call] + MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap(); + assert_last_event::(Event::Processed { hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), weight_used: Weight::from_parts(1, 1), success: true @@ -180,7 +197,8 @@ benchmarks! { } // Worst case for `execute_overweight` where the page is updated. - execute_overweight_page_updated { + #[benchmark] + fn execute_overweight_page_updated() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); // Skip all messages. @@ -190,10 +208,10 @@ benchmarks! { let book = book_for::(&page); Pages::::insert(&origin, 0, &page); BookStateFor::::insert(&origin, &book); - }: { - MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap() - } - verify { + + #[extrinsic_call] + MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap(); + assert_last_event::(Event::Processed { hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), weight_used: Weight::from_parts(1, 1), success: true diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index d7dcb1eeced99..cf1f90404e544 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -63,6 +63,7 @@ struct BenchmarkDef { params: Vec, setup_stmts: Vec, extrinsic_call: ExprCall, + extrinsic_call_lhs_var_name: Option, origin: Expr, verify_stmts: Vec, extra: bool, @@ -127,7 +128,9 @@ impl BenchmarkDef { for child in &item_fn.block.stmts { // find #[extrinsic_call] annotation and build up the setup, call, and verify // blocks based on the location of this annotation - if let Stmt::Semi(Expr::Call(fn_call), _token) = child { + let mut lhs_var_name: Option = None; + let mut expr_call: Option = None; + if let Stmt::Semi(Expr::Call(fn_call), _semi) = child { let mut k = 0; // index of attr for attr in &fn_call.attrs { if let Some(segment) = attr.path.segments.last() { @@ -136,35 +139,62 @@ impl BenchmarkDef { ) { let mut extrinsic_call = fn_call.clone(); extrinsic_call.attrs.remove(k); // consume #[extrinsic call] - let origin = match extrinsic_call.args.first() { - Some(arg) => arg.clone(), - None => return Err(Error::new( - extrinsic_call.args.span(), - "Extrinsic call must specify its origin as the first argument.", - )), - }; - let mut final_args = Punctuated::::new(); - let args: Vec<&Expr> = extrinsic_call.args.iter().collect(); - for arg in &args[1..] { - final_args.push((*(*arg)).clone()); + expr_call = Some(extrinsic_call); + } + } + k += 1; + } + } else if let Stmt::Local(local) = child { + let mut k = 0; // index of attr + for attr in &local.attrs { + if let Some(segment) = attr.path.segments.last() { + if let Ok(_) = syn::parse::( + segment.ident.to_token_stream().into(), + ) { + if let Some((_, boxed)) = &local.init { + if let Expr::Call(call) = &**boxed { + // found let-style extrinsic call + let mut local = local.clone(); + local.attrs.remove(k); // consume #[extrinsic_call] + expr_call = Some(call.clone()); + if let Pat::Ident(ident) = local.pat { + lhs_var_name = Some(ident.ident.clone()); + } + } } - extrinsic_call.args = final_args; - return Ok(BenchmarkDef { - params, - setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - extrinsic_call, - origin, - verify_stmts: Vec::from( - &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], - ), - extra, - skip_meta, - }) } } k += 1; } } + if let Some(mut expr_call) = expr_call { + let origin = match expr_call.args.first() { + Some(arg) => arg.clone(), + None => + return Err(Error::new( + expr_call.args.span(), + "Extrinsic call must specify its origin as the first argument.", + )), + }; + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + return Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + extrinsic_call: expr_call, + extrinsic_call_lhs_var_name: lhs_var_name, + origin, + verify_stmts: Vec::from( + &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], + ), + extra, + skip_meta, + }) + } i += 1; } return Err(Error::new( @@ -555,6 +585,13 @@ fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool } // else handle error? } // else handle error? + let final_call = match benchmark_def.extrinsic_call_lhs_var_name { + Some(var) => quote!(let #var = Call::<#generics>::#extrinsic_call;), + None => quote!(let __call = Call::<#generics>::#extrinsic_call;), + }; + + println!("\nfinal_call: {}\n", final_call.to_string()); + // generate final quoted tokens let res = quote! { // compile-time assertions that each referenced param type implements ParamRange @@ -593,7 +630,7 @@ fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool #( #setup_stmts )* - let __call = Call::<#generics>::#extrinsic_call; + #final_call // extrinsic call let __benchmarked_call_encoded = #codec::Encode::encode(&__call); Ok(#krate::Box::new(move || -> Result<(), #krate::BenchmarkError> { let __call_decoded = as #codec::Decode> From 1fb58986051cdb333d1e4885ce4ab61be6f7a37f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 18:57:24 -0500 Subject: [PATCH 062/146] assume option (2) for now wrt https://github.com/paritytech/substrate/pull/12924#issuecomment-1372938718 --- frame/message-queue/src/benchmarking.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 03609d60f000f..2fb70d713520f 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -46,7 +46,9 @@ benchmarks! { assert_ring::(&[0.into(), 2.into()]); #[extrinsic_call] - let neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); + let neighbours = MessageQueue::::ready_ring_knit(&mid); + + neighbors.ok(); // The neighbours needs to be modified manually. BookStateFor::::mutate(&mid, |b| { b.ready_neighbours = neighbours }); @@ -187,8 +189,9 @@ benchmarks! { BookStateFor::::insert(&origin, &book); #[extrinsic_call] - MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap(); + let call = MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX); + call.unwrap(); assert_last_event::(Event::Processed { hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), weight_used: Weight::from_parts(1, 1), success: true @@ -210,8 +213,9 @@ benchmarks! { BookStateFor::::insert(&origin, &book); #[extrinsic_call] - MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap(); + let call = MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX); + call.unwrap(); assert_last_event::(Event::Processed { hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), weight_used: Weight::from_parts(1, 1), success: true From 9d61cd36aba565c8fdd08d777948ccef7dd8387e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 19:56:43 -0500 Subject: [PATCH 063/146] clean up --- frame/support/procedural/src/benchmark.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index cf1f90404e544..e2ef4ee999e76 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -590,8 +590,6 @@ fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool None => quote!(let __call = Call::<#generics>::#extrinsic_call;), }; - println!("\nfinal_call: {}\n", final_call.to_string()); - // generate final quoted tokens let res = quote! { // compile-time assertions that each referenced param type implements ParamRange From 34b770b12a5a5ef71cdb8db4eaf3f1337206ab6d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 20:58:37 -0500 Subject: [PATCH 064/146] switch to attribute-style --- frame/balances/src/benchmarking.rs | 24 +- frame/message-queue/src/benchmarking.rs | 84 +++- frame/support/procedural/src/benchmark.rs | 466 +++++++++++----------- frame/support/procedural/src/lib.rs | 12 +- 4 files changed, 309 insertions(+), 277 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index c3917a0078a1b..8fd1cee99b9b3 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -30,7 +30,10 @@ const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; -instance_benchmarks! { +#[instance_benchmarks] +mod benchmarks { + use super::*; + #[benchmark] fn transfer() { let existential_deposit = T::ExistentialDeposit::get(); @@ -63,12 +66,15 @@ instance_benchmarks! { let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - // Give the sender account max funds for transfer (their account will never reasonably be killed). - let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + // Give the sender account max funds for transfer (their account will never reasonably be + // killed). + let _ = + as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); // Give the recipient account existential deposit (thus their account already exists). let existential_deposit = T::ExistentialDeposit::get(); - let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); + let _ = + as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); #[extrinsic_call] @@ -87,7 +93,8 @@ instance_benchmarks! { let recipient_lookup = T::Lookup::unlookup(recipient.clone()); // Give the sender account max funds, thus a transfer will not kill account. - let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + let _ = + as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); let existential_deposit = T::ExistentialDeposit::get(); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); @@ -146,10 +153,12 @@ instance_benchmarks! { let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&source, balance); - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user. + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, + // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); #[extrinsic_call] force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); @@ -158,7 +167,6 @@ instance_benchmarks! { assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } - // This benchmark performs the same operation as `transfer` in the worst case scenario, // but additionally introduces many new users into the storage, increasing the the merkle // trie and PoV size. diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 2fb70d713520f..78b95fac938d4 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -27,7 +27,9 @@ use frame_support::{benchmarking::*, traits::Get}; use frame_system::RawOrigin; use sp_std::prelude::*; -benchmarks! { +#[benchmarks] +mod benchmarks { + use super::*; where_clause! { where // NOTE: We need to generate multiple origins, therefore Origin is `From`. The @@ -40,7 +42,7 @@ benchmarks! { // Worst case path of `ready_ring_knit`. #[benchmark] fn ready_ring_knit() { - let mid: MessageOriginOf:: = 1.into(); + let mid: MessageOriginOf = 1.into(); build_ring::(&[0.into(), mid.clone(), 2.into()]); unknit::(&mid); assert_ring::(&[0.into(), 2.into()]); @@ -51,7 +53,7 @@ benchmarks! { neighbors.ok(); // The neighbours needs to be modified manually. - BookStateFor::::mutate(&mid, |b| { b.ready_neighbours = neighbours }); + BookStateFor::::mutate(&mid, |b| b.ready_neighbours = neighbours); assert_ring::(&[0.into(), 2.into(), mid]); } @@ -60,7 +62,7 @@ benchmarks! { fn ready_ring_unknit() { build_ring::(&[0.into(), 1.into(), 2.into()]); assert_ring::(&[0.into(), 1.into(), 2.into()]); - let o: MessageOriginOf:: = 0.into(); + let o: MessageOriginOf = 0.into(); let neighbours = BookStateFor::::get(&o).ready_neighbours.unwrap(); #[extrinsic_call] @@ -117,15 +119,27 @@ benchmarks! { let mut weight = WeightMeter::max_limit(); #[extrinsic_call] - let status = MessageQueue::::service_page_item(&0u32.into(), 0, &mut book, &mut page, &mut weight, Weight::MAX); + let status = MessageQueue::::service_page_item( + &0u32.into(), + 0, + &mut book, + &mut page, + &mut weight, + Weight::MAX, + ); assert_eq!(status, ItemExecutionStatus::Executed(true)); // Check that it was processed. - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&msg), origin: 0.into(), - weight_used: 1.into_weight(), success: true - }.into()); + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&msg), + origin: 0.into(), + weight_used: 1.into_weight(), + success: true, + } + .into(), + ); let (_, processed, _) = page.peek_index(0).unwrap(); assert!(processed); assert_eq!(book.message_count, 0); @@ -151,7 +165,7 @@ benchmarks! { let mut book = single_page_book::(); let (page, msgs) = full_page::(); - for p in 0 .. T::MaxStale::get() * T::MaxStale::get() { + for p in 0..T::MaxStale::get() * T::MaxStale::get() { if p == 0 { Pages::::insert(&origin, p, &page); } @@ -167,7 +181,7 @@ benchmarks! { #[extrinsic_call] reap_page(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); - assert_last_event::(Event::PageReaped{ origin: 0.into(), index: 0 }.into()); + assert_last_event::(Event::PageReaped { origin: 0.into(), index: 0 }.into()); assert!(!Pages::::contains_key(&origin, 0)); } @@ -189,13 +203,24 @@ benchmarks! { BookStateFor::::insert(&origin, &book); #[extrinsic_call] - let call = MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX); + let call = MessageQueue::::execute_overweight( + RawOrigin::Signed(whitelisted_caller()).into(), + 0u32.into(), + 0u32, + ((msgs - 1) as u32).into(), + Weight::MAX, + ); call.unwrap(); - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), - weight_used: Weight::from_parts(1, 1), success: true - }.into()); + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), + origin: 0.into(), + weight_used: Weight::from_parts(1, 1), + success: true, + } + .into(), + ); assert!(!Pages::::contains_key(&origin, 0), "Page must be removed"); } @@ -213,15 +238,30 @@ benchmarks! { BookStateFor::::insert(&origin, &book); #[extrinsic_call] - let call = MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX); + let call = MessageQueue::::execute_overweight( + RawOrigin::Signed(whitelisted_caller()).into(), + 0u32.into(), + 0u32, + ((msgs - 1) as u32).into(), + Weight::MAX, + ); call.unwrap(); - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), - weight_used: Weight::from_parts(1, 1), success: true - }.into()); + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), + origin: 0.into(), + weight_used: Weight::from_parts(1, 1), + success: true, + } + .into(), + ); assert!(Pages::::contains_key(&origin, 0), "Page must be updated"); } - impl_benchmark_test_suite!(MessageQueue, crate::mock::new_test_ext::(), crate::integration_test::Test); + impl_benchmark_test_suite!( + MessageQueue, + crate::mock::new_test_ext::(), + crate::integration_test::Test + ); } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index e2ef4ee999e76..949b4a780513d 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -3,12 +3,11 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ - parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::{Comma, Gt, Lt}, - Block, Error, Expr, ExprCall, FnArg, Item, ItemFn, LitInt, Pat, Result, Stmt, Type, + Error, Expr, ExprCall, FnArg, Item, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, Type, WhereClause, }; @@ -21,23 +20,6 @@ mod keywords { custom_keyword!(skip_meta); } -/// Represents a "bare" block, that is, a the contents of a [`Block`] minus the curly braces. -/// Useful for parsing the contents of a decl macro that takes a block, since the curly braces -/// are not actually included in the input [`TokenStream`] in that scenario. The contents are -/// parsed as a [`Vec`] called `stmts`. Can be used with [`parse_macro_input!`], etc. -struct BareBlock { - stmts: Vec, -} - -impl Parse for BareBlock { - fn parse(input: ParseStream) -> syn::Result { - match Block::parse_within(input) { - Ok(stmts) => Ok(BareBlock { stmts }), - Err(e) => Err(e), - } - } -} - /// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. #[derive(Clone)] struct ParamDef { @@ -204,16 +186,18 @@ impl BenchmarkDef { } } -pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { - let mut block = parse_macro_input!(tokens as BareBlock); +pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream, instance: bool) -> TokenStream { + let module = parse_macro_input!(tokens as ItemMod); + let mod_vis = module.vis; + let mod_name = module.ident; let mut expanded_stmts: Vec = Vec::new(); let mut benchmark_defs: Vec = Vec::new(); let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); - for stmt in &mut block.stmts { - let mut found_item: Option = None; - if let Stmt::Item(stmt) = stmt { + if let Some(mut content) = module.content { + for stmt in &mut content.1 { + let mut found_item: Option = None; if let Item::Fn(func) = stmt { let mut i = 0; for attr in &func.attrs.clone() { @@ -228,34 +212,31 @@ pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { } } } - } - if let Some(item_fn) = found_item { - // this item is a #[benchmark] or #[instance_benchmark] - - // build a BenchmarkDef from item_fn - let benchmark_def = match BenchmarkDef::from(&item_fn) { - Ok(def) => def, - Err(err) => return err.to_compile_error().into(), - }; - - // expand benchmark_def - let expanded = expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, instance); - - // record benchmark name - let name = item_fn.sig.ident; - benchmark_names.push(name.clone()); - if benchmark_def.extra { - extra_benchmark_names.push(name.clone()); - } - if benchmark_def.skip_meta { - skip_meta_benchmark_names.push(name.clone()) - } + if let Some(item_fn) = found_item { + // this item is a #[benchmark] or #[instance_benchmark] + let benchmark_def = match BenchmarkDef::from(&item_fn) { + Ok(def) => def, + Err(err) => return err.to_compile_error().into(), + }; + let expanded = + expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, instance); + + // record benchmark name + let name = item_fn.sig.ident; + benchmark_names.push(name.clone()); + if benchmark_def.extra { + extra_benchmark_names.push(name.clone()); + } + if benchmark_def.skip_meta { + skip_meta_benchmark_names.push(name.clone()) + } - expanded_stmts.push(expanded); - benchmark_defs.push(benchmark_def); - } else { - // this is not a benchmark item, copy it in verbatim - expanded_stmts.push(stmt.to_token_stream()); + expanded_stmts.push(expanded); + benchmark_defs.push(benchmark_def); + } else { + // this is not a benchmark item, copy it in verbatim + expanded_stmts.push(stmt.to_token_stream()); + } } } @@ -294,218 +275,221 @@ pub fn benchmarks(tokens: TokenStream, instance: bool) -> TokenStream { // emit final quoted tokens let res = quote! { - #(#expanded_stmts) - * - - #[allow(non_camel_case_types)] - enum SelectedBenchmark { - #(#benchmark_names), + #mod_vis mod #mod_name { + #(#expanded_stmts) * - } - impl<#full_generics> #krate::BenchmarkingSetup<#generics> for SelectedBenchmark { - fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { - match self { - #( - Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup<#generics>>::components(&#benchmark_names) - } - ) - * - } + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + #(#benchmark_names), + * } - fn instance( - &self, - components: &[(#krate::BenchmarkParameter, u32)], - verify: bool, - ) -> Result< - #krate::Box Result<(), #krate::BenchmarkError>>, - #krate::BenchmarkError, - > { - match self { - #( - Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup< - T, - I, - >>::instance(&#benchmark_names, components, verify) - } - ) - * + impl<#full_generics> #krate::BenchmarkingSetup<#generics> for SelectedBenchmark { + fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup<#generics>>::components(&#benchmark_names) + } + ) + * + } + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool, + ) -> Result< + #krate::Box Result<(), #krate::BenchmarkError>>, + #krate::BenchmarkError, + > { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup< + T, + I, + >>::instance(&#benchmark_names, components, verify) + } + ) + * + } } } - } - #[cfg(any(feature = "runtime-benchmarks", test))] - impl<#full_generics> #krate::Benchmarking for Pallet<#generics> - where - T: frame_system::Config, - { - fn benchmarks( - extra: bool, - ) -> #krate::Vec<#krate::BenchmarkMetadata> { - let mut all_names = #krate::vec![ - #(#benchmark_names_str), - * - ]; - if !extra { - let extra = [ - #(#extra_benchmark_names_str), + #[cfg(any(feature = "runtime-benchmarks", test))] + impl<#full_generics> #krate::Benchmarking for Pallet<#generics> + where + T: frame_system::Config, + { + fn benchmarks( + extra: bool, + ) -> #krate::Vec<#krate::BenchmarkMetadata> { + let mut all_names = #krate::vec![ + #(#benchmark_names_str), * ]; - all_names.retain(|x| !extra.contains(x)); + if !extra { + let extra = [ + #(#extra_benchmark_names_str), + * + ]; + all_names.retain(|x| !extra.contains(x)); + } + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + #(#selected_benchmark_mappings), + *, + _ => panic!("all benchmarks should be selectable") + }; + let components = >::components(&selected_benchmark); + #krate::BenchmarkMetadata { + name: benchmark.as_bytes().to_vec(), + components, + } + }).collect::<#krate::Vec<_>>() } - all_names.into_iter().map(|benchmark| { - let selected_benchmark = match benchmark { + + fn run_benchmark( + extrinsic: &[u8], + c: &[(#krate::BenchmarkParameter, u32)], + whitelist: &[#krate::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<#krate::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { + let extrinsic = #krate::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; + let selected_benchmark = match extrinsic { #(#selected_benchmark_mappings), *, - _ => panic!("all benchmarks should be selectable") + _ => return Err("Could not find extrinsic.".into()), }; - let components = >::components(&selected_benchmark); - #krate::BenchmarkMetadata { - name: benchmark.as_bytes().to_vec(), - components, - } - }).collect::<#krate::Vec<_>>() - } - - fn run_benchmark( - extrinsic: &[u8], - c: &[(#krate::BenchmarkParameter, u32)], - whitelist: &[#krate::TrackedStorageKey], - verify: bool, - internal_repeats: u32, - ) -> Result<#krate::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { - let extrinsic = #krate::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; - let selected_benchmark = match extrinsic { - #(#selected_benchmark_mappings), - *, - _ => return Err("Could not find extrinsic.".into()), - }; - let mut whitelist = whitelist.to_vec(); - let whitelisted_caller_key = as #support::storage::StorageMap<_, _,>>::hashed_key_for( - #krate::whitelisted_caller::() - ); - whitelist.push(whitelisted_caller_key.into()); - let transactional_layer_key = #krate::TrackedStorageKey::new( - #support::storage::transactional::TRANSACTION_LEVEL_KEY.into(), - ); - whitelist.push(transactional_layer_key); - #krate::benchmarking::set_whitelist(whitelist); - let mut results: #krate::Vec<#krate::BenchmarkResult> = #krate::Vec::new(); - - // Always do at least one internal repeat... - for _ in 0 .. internal_repeats.max(1) { - // Always reset the state after the benchmark. - #krate::defer!(#krate::benchmarking::wipe_db()); - - // Set up the externalities environment for the setup we want to - // benchmark. - let closure_to_benchmark = < - SelectedBenchmark as #krate::BenchmarkingSetup<#generics> - >::instance(&selected_benchmark, c, verify)?; - - // Set the block number to at least 1 so events are deposited. - if #krate::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. - #krate::benchmarking::commit_db(); - - // Reset the read/write counter so we don't count operations in the setup process. - #krate::benchmarking::reset_read_write_count(); - - // Time the extrinsic logic. - #krate::log::trace!( - target: "benchmark", - "Start Benchmark: {} ({:?})", - extrinsic, - c + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = as #support::storage::StorageMap<_, _,>>::hashed_key_for( + #krate::whitelisted_caller::() ); - - let start_pov = #krate::benchmarking::proof_size(); - let start_extrinsic = #krate::benchmarking::current_time(); - - closure_to_benchmark()?; - - let finish_extrinsic = #krate::benchmarking::current_time(); - let end_pov = #krate::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 - #krate::benchmarking::commit_db(); - #krate::log::trace!( - target: "benchmark", - "End Benchmark: {} ns", elapsed_extrinsic + whitelist.push(whitelisted_caller_key.into()); + let transactional_layer_key = #krate::TrackedStorageKey::new( + #support::storage::transactional::TRANSACTION_LEVEL_KEY.into(), ); - let read_write_count = #krate::benchmarking::read_write_count(); - #krate::log::trace!( - target: "benchmark", - "Read/Write Count {:?}", read_write_count - ); - - // Time the storage root recalculation. - let start_storage_root = #krate::benchmarking::current_time(); - #krate::storage_root(#krate::StateVersion::V1); - let finish_storage_root = #krate::benchmarking::current_time(); - let elapsed_storage_root = finish_storage_root - start_storage_root; + whitelist.push(transactional_layer_key); + #krate::benchmarking::set_whitelist(whitelist); + let mut results: #krate::Vec<#krate::BenchmarkResult> = #krate::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + #krate::defer!(#krate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as #krate::BenchmarkingSetup<#generics> + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::Zero::is_zero(&frame_system::Pallet::::block_number()) { + frame_system::Pallet::::set_block_number(1u32.into()); + } - let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; - let read_and_written_keys = if skip_meta.contains(&extrinsic) { - #krate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] - } else { - #krate::benchmarking::get_read_and_written_keys() - }; + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + #krate::benchmarking::commit_db(); + + // Reset the read/write counter so we don't count operations in the setup process. + #krate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + #krate::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?})", + extrinsic, + c + ); + + let start_pov = #krate::benchmarking::proof_size(); + let start_extrinsic = #krate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = #krate::benchmarking::current_time(); + let end_pov = #krate::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 + #krate::benchmarking::commit_db(); + #krate::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = #krate::benchmarking::read_write_count(); + #krate::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + + // Time the storage root recalculation. + let start_storage_root = #krate::benchmarking::current_time(); + #krate::storage_root(#krate::StateVersion::V1); + let finish_storage_root = #krate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + #krate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + #krate::benchmarking::get_read_and_written_keys() + }; + + results.push(#krate::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, + }); + } - results.push(#krate::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); } - - return Ok(results); } - } - #[cfg(test)] - impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config { - /// 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<(), #krate::BenchmarkError> { - let name = #krate::str::from_utf8(name) - .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; - match name { - #(#benchmarks_by_name_mappings), - *, - _ => Err("Could not find test for requested benchmark.".into()), + #[cfg(test)] + impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config { + /// 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<(), #krate::BenchmarkError> { + let name = #krate::str::from_utf8(name) + .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + #(#benchmarks_by_name_mappings), + *, + _ => Err("Could not find test for requested benchmark.".into()), + } } } } + #mod_vis use #mod_name::*; }; res.into() } diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index fd7ebba27e2a4..a05e4963137ac 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -480,14 +480,14 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) } -#[proc_macro] -pub fn benchmarks(tokens: TokenStream) -> TokenStream { - benchmark::benchmarks(tokens, false) +#[proc_macro_attribute] +pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { + benchmark::benchmarks(attr, tokens, false) } -#[proc_macro] -pub fn instance_benchmarks(tokens: TokenStream) -> TokenStream { - benchmark::benchmarks(tokens, true) +#[proc_macro_attribute] +pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { + benchmark::benchmarks(attr, tokens, true) } #[proc_macro_attribute] From a90a04f808e6ea54415ac6875749248c2320a976 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 5 Jan 2023 23:31:23 -0500 Subject: [PATCH 065/146] properly handle where clauses --- frame/message-queue/src/benchmarking.rs | 33 ++++++++-------- frame/support/procedural/src/benchmark.rs | 48 ++++++++++++++--------- frame/support/procedural/src/lib.rs | 5 --- frame/support/src/lib.rs | 2 +- 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 78b95fac938d4..52fd196cfbb70 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -22,22 +22,21 @@ use super::{mock_helpers::*, Pallet as MessageQueue, *}; -use frame_benchmarking::whitelisted_caller; +use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; use frame_support::{benchmarking::*, traits::Get}; use frame_system::RawOrigin; use sp_std::prelude::*; -#[benchmarks] +#[benchmarks( + where + <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, + ::Size: From, + // NOTE: We need to generate multiple origins, therefore Origin is `From`. The + // `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be + // removed if really necessary. +)] mod benchmarks { use super::*; - where_clause! { - where - // NOTE: We need to generate multiple origins, therefore Origin is `From`. The - // `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be - // removed if really necessary. - <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, - ::Size: From, - } // Worst case path of `ready_ring_knit`. #[benchmark] @@ -50,9 +49,9 @@ mod benchmarks { #[extrinsic_call] let neighbours = MessageQueue::::ready_ring_knit(&mid); - neighbors.ok(); + neighbours.ok(); - // The neighbours needs to be modified manually. + // The neighbors needs to be modified manually. BookStateFor::::mutate(&mid, |b| b.ready_neighbours = neighbours); assert_ring::(&[0.into(), 2.into(), mid]); } @@ -259,9 +258,9 @@ mod benchmarks { assert!(Pages::::contains_key(&origin, 0), "Page must be updated"); } - impl_benchmark_test_suite!( - MessageQueue, - crate::mock::new_test_ext::(), - crate::integration_test::Test - ); + // impl_benchmark_test_suite!( + // MessageQueue, + // crate::mock::new_test_ext::(), + // crate::integration_test::Test + // ); } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 949b4a780513d..f31c52500b5ce 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -3,6 +3,7 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ + parse::Nothing, parse_macro_input, punctuated::Punctuated, spanned::Spanned, @@ -186,8 +187,12 @@ impl BenchmarkDef { } } -pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream, instance: bool) -> TokenStream { +pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> TokenStream { let module = parse_macro_input!(tokens as ItemMod); + let where_clause = match syn::parse::(attrs.clone()) { + Ok(_) => quote!(), + Err(_) => parse_macro_input!(attrs as WhereClause).predicates.to_token_stream(), + }; let mod_vis = module.vis; let mod_name = module.ident; let mut expanded_stmts: Vec = Vec::new(); @@ -218,8 +223,14 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream, instance: bool) -> T Ok(def) => def, Err(err) => return err.to_compile_error().into(), }; - let expanded = - expand_benchmark(benchmark_def.clone(), &item_fn.sig.ident, instance); + + // expand benchmark + let expanded = expand_benchmark( + benchmark_def.clone(), + &item_fn.sig.ident, + instance, + where_clause.clone(), + ); // record benchmark name let name = item_fn.sig.ident; @@ -285,7 +296,7 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream, instance: bool) -> T * } - impl<#full_generics> #krate::BenchmarkingSetup<#generics> for SelectedBenchmark { + impl<#full_generics> #krate::BenchmarkingSetup<#generics> for SelectedBenchmark where #where_clause { fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { match self { #( @@ -309,8 +320,7 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream, instance: bool) -> T #( Self::#benchmark_names => { <#benchmark_names as #krate::BenchmarkingSetup< - T, - I, + #generics >>::instance(&#benchmark_names, components, verify) } ) @@ -320,8 +330,7 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream, instance: bool) -> T } #[cfg(any(feature = "runtime-benchmarks", test))] impl<#full_generics> #krate::Benchmarking for Pallet<#generics> - where - T: frame_system::Config, + where T: frame_system::Config, #where_clause { fn benchmarks( extra: bool, @@ -467,7 +476,7 @@ pub fn benchmarks(_attrs: TokenStream, tokens: TokenStream, instance: bool) -> T } #[cfg(test)] - impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config { + impl<#full_generics> Pallet<#generics> 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 @@ -531,7 +540,12 @@ impl UnrolledParams { } } -fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool) -> TokenStream2 { +fn expand_benchmark( + benchmark_def: BenchmarkDef, + name: &Ident, + is_instance: bool, + where_clause: TokenStream2, +) -> TokenStream2 { // set up variables needed during quoting let krate = quote!(::frame_benchmarking); let home = quote!(::frame_support::benchmarking); @@ -570,7 +584,10 @@ fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool } // else handle error? let final_call = match benchmark_def.extrinsic_call_lhs_var_name { - Some(var) => quote!(let #var = Call::<#generics>::#extrinsic_call;), + Some(var) => quote! { + let #var = Call::<#generics>::#extrinsic_call; + let __call = #var; + }, None => quote!(let __call = Call::<#generics>::#extrinsic_call;), }; @@ -634,7 +651,7 @@ fn expand_benchmark(benchmark_def: BenchmarkDef, name: &Ident, is_instance: bool } #[cfg(test)] - impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config { + impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config, #where_clause { #[allow(unused)] fn #test_ident() -> Result<(), #krate::BenchmarkError> { let selected_benchmark = SelectedBenchmark::#name; @@ -727,10 +744,5 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> Err(err) => return err.to_compile_error().into(), }; - expand_benchmark(benchmark_def, &item_fn.sig.ident, is_instance).into() -} - -pub fn where_clause(tokens: TokenStream) -> TokenStream { - let _clause = parse_macro_input!(tokens as WhereClause); - quote!().into() + expand_benchmark(benchmark_def, &item_fn.sig.ident, is_instance, quote!()).into() } diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index a05e4963137ac..be66d8ea7471e 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -500,11 +500,6 @@ pub fn instance_benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStrea benchmark::benchmark(attrs, tokens, true) } -#[proc_macro] -pub fn where_clause(tokens: TokenStream) -> TokenStream { - benchmark::where_clause(tokens) -} - /// Execute the annotated function in a new storage transaction. /// /// The return type of the annotated function must be `Result`. All changes to storage performed diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 149df95ca884e..ddabe62eb0ccf 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2751,7 +2751,7 @@ pub mod pallet_macros { sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); pub mod benchmarking { - pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmarks, where_clause}; + pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmarks}; #[doc(hidden)] pub use static_assertions::assert_impl_all; From f8525e2247cd0c3be61536e21d0eeefd83ecb0c9 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 6 Jan 2023 02:55:01 -0500 Subject: [PATCH 066/146] fix subtle missing where clause, now just MessageQueue issues --- frame/message-queue/src/benchmarking.rs | 10 +++++----- frame/support/procedural/src/benchmark.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 52fd196cfbb70..c4795b4e75ffb 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -258,9 +258,9 @@ mod benchmarks { assert!(Pages::::contains_key(&origin, 0), "Page must be updated"); } - // impl_benchmark_test_suite!( - // MessageQueue, - // crate::mock::new_test_ext::(), - // crate::integration_test::Test - // ); + impl_benchmark_test_suite!( + MessageQueue, + crate::mock::new_test_ext::(), + crate::integration_test::Test + ); } diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index f31c52500b5ce..7e193b84b9fc0 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -603,7 +603,7 @@ fn expand_benchmark( #[allow(unused_variables)] impl<#full_generics> #krate::BenchmarkingSetup<#generics> - for #name { + for #name where #where_clause { fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { #krate::vec! [ #( From 59ae6272aae1b7168df2c7713120dc08423be20f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 6 Jan 2023 13:50:09 -0500 Subject: [PATCH 067/146] fix block formatting in message-queue pallet --- frame/message-queue/src/benchmarking.rs | 95 +++++++++++++------------ 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index c4795b4e75ffb..1261e7eeeddee 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -45,19 +45,19 @@ mod benchmarks { build_ring::(&[0.into(), mid.clone(), 2.into()]); unknit::(&mid); assert_ring::(&[0.into(), 2.into()]); + let mut neighbours = None; #[extrinsic_call] - let neighbours = MessageQueue::::ready_ring_knit(&mid); - - neighbours.ok(); + { + neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); + } - // The neighbors needs to be modified manually. + // The neighbours needs to be modified manually. BookStateFor::::mutate(&mid, |b| b.ready_neighbours = neighbours); assert_ring::(&[0.into(), 2.into(), mid]); } // Worst case path of `ready_ring_unknit`. - #[benchmark] fn ready_ring_unknit() { build_ring::(&[0.into(), 1.into(), 2.into()]); assert_ring::(&[0.into(), 1.into(), 2.into()]); @@ -65,20 +65,22 @@ mod benchmarks { let neighbours = BookStateFor::::get(&o).ready_neighbours.unwrap(); #[extrinsic_call] - MessageQueue::::ready_ring_unknit(&o, neighbours); + { + MessageQueue::::ready_ring_unknit(&o, neighbours); + } assert_ring::(&[1.into(), 2.into()]); } // `service_queues` without any queue processing. - #[benchmark] fn service_queue_base() { #[extrinsic_call] - MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX); + { + MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX); + } } // `service_page` without any message processing but with page completion. - #[benchmark] fn service_page_base_completion() { let origin: MessageOriginOf = 0.into(); let page = PageOf::::default(); @@ -88,11 +90,12 @@ mod benchmarks { let limit = Weight::MAX; #[extrinsic_call] - MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); + { + MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); + } } // `service_page` without any message processing and without page completion. - #[benchmark] fn service_page_base_no_completion() { let origin: MessageOriginOf = 0.into(); let mut page = PageOf::::default(); @@ -105,11 +108,12 @@ mod benchmarks { let limit = Weight::MAX; #[extrinsic_call] - MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); + { + MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); + } } // Processing a single message from a page. - #[benchmark] fn service_page_item() { let msg = vec![1u8; MaxMessageLenOf::::get() as usize]; let mut page = page::(&msg.clone()); @@ -118,16 +122,17 @@ mod benchmarks { let mut weight = WeightMeter::max_limit(); #[extrinsic_call] - let status = MessageQueue::::service_page_item( - &0u32.into(), - 0, - &mut book, - &mut page, - &mut weight, - Weight::MAX, - ); - - assert_eq!(status, ItemExecutionStatus::Executed(true)); + { + let status = MessageQueue::::service_page_item( + &0u32.into(), + 0, + &mut book, + &mut page, + &mut weight, + Weight::MAX, + ); + assert_eq!(status, ItemExecutionStatus::Executed(true)); + } // Check that it was processed. assert_last_event::( @@ -145,19 +150,19 @@ mod benchmarks { } // Worst case for calling `bump_service_head`. - #[benchmark] fn bump_service_head() { setup_bump_service_head::(0.into(), 10.into()); let mut weight = WeightMeter::max_limit(); #[extrinsic_call] - MessageQueue::::bump_service_head(&mut weight); + { + MessageQueue::::bump_service_head(&mut weight); + } assert_eq!(ServiceHead::::get().unwrap(), 10u32.into()); assert_eq!(weight.consumed, T::WeightInfo::bump_service_head()); } - #[benchmark] fn reap_page() { // Mock the storage to get a *cullable* but not *reapable* page. let origin: MessageOriginOf = 0.into(); @@ -188,7 +193,6 @@ mod benchmarks { // // The worst case occurs when executing the last message in a page of which all are skipped // since it is using `peek_index` which has linear complexities. - #[benchmark] fn execute_overweight_page_removed() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); @@ -202,15 +206,17 @@ mod benchmarks { BookStateFor::::insert(&origin, &book); #[extrinsic_call] - let call = MessageQueue::::execute_overweight( - RawOrigin::Signed(whitelisted_caller()).into(), - 0u32.into(), - 0u32, - ((msgs - 1) as u32).into(), - Weight::MAX, - ); + { + MessageQueue::::execute_overweight( + RawOrigin::Signed(whitelisted_caller()).into(), + 0u32.into(), + 0u32, + ((msgs - 1) as u32).into(), + Weight::MAX, + ) + .unwrap(); + } - call.unwrap(); assert_last_event::( Event::Processed { hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), @@ -224,7 +230,6 @@ mod benchmarks { } // Worst case for `execute_overweight` where the page is updated. - #[benchmark] fn execute_overweight_page_updated() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); @@ -237,15 +242,17 @@ mod benchmarks { BookStateFor::::insert(&origin, &book); #[extrinsic_call] - let call = MessageQueue::::execute_overweight( - RawOrigin::Signed(whitelisted_caller()).into(), - 0u32.into(), - 0u32, - ((msgs - 1) as u32).into(), - Weight::MAX, - ); + { + MessageQueue::::execute_overweight( + RawOrigin::Signed(whitelisted_caller()).into(), + 0u32.into(), + 0u32, + ((msgs - 1) as u32).into(), + Weight::MAX, + ) + .unwrap(); + } - call.unwrap(); assert_last_event::( Event::Processed { hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), From 3b7627126b6951e7c05f492169056a756dc7be3b Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 6 Jan 2023 14:31:17 -0500 Subject: [PATCH 068/146] switch to block vs non-block parsing of extrinsic call --- frame/support/procedural/src/benchmark.rs | 96 ++++++++++++----------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 7e193b84b9fc0..4a8b35ec5cce8 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -8,8 +8,8 @@ use syn::{ punctuated::Punctuated, spanned::Spanned, token::{Comma, Gt, Lt}, - Error, Expr, ExprCall, FnArg, Item, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, Type, - WhereClause, + Error, Expr, ExprBlock, ExprCall, FnArg, Item, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, + Type, WhereClause, }; mod keywords { @@ -40,14 +40,19 @@ struct RangeArgs { _gt_token: Gt, } +#[derive(Clone)] +enum ExtrinsicCallDef { + Encoded { origin: Expr, expr_call: ExprCall }, // no block, just an extrinsic call + Block(ExprBlock), /* something in a block, we don't care what + * it is */ +} + /// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. #[derive(Clone)] struct BenchmarkDef { params: Vec, setup_stmts: Vec, - extrinsic_call: ExprCall, - extrinsic_call_lhs_var_name: Option, - origin: Expr, + extrinsic_call: ExtrinsicCallDef, verify_stmts: Vec, extra: bool, skip_meta: bool, @@ -109,68 +114,67 @@ impl BenchmarkDef { } } for child in &item_fn.block.stmts { - // find #[extrinsic_call] annotation and build up the setup, call, and verify - // blocks based on the location of this annotation - let mut lhs_var_name: Option = None; - let mut expr_call: Option = None; - if let Stmt::Semi(Expr::Call(fn_call), _semi) = child { + let mut extrinsic_call: Option = None; + + // #[extrinsic_call] handling for the non-block (single item) case + if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { let mut k = 0; // index of attr - for attr in &fn_call.attrs { + for attr in &expr_call.attrs { if let Some(segment) = attr.path.segments.last() { if let Ok(_) = syn::parse::( segment.ident.to_token_stream().into(), ) { - let mut extrinsic_call = fn_call.clone(); - extrinsic_call.attrs.remove(k); // consume #[extrinsic call] - expr_call = Some(extrinsic_call); + let mut expr_call = expr_call.clone(); + + // consume #[extrinsic_call] tokens + expr_call.attrs.remove(k); + + // extract origin from expr_call + let origin = match expr_call.args.first() { + Some(arg) => arg.clone(), + None => + return Err(Error::new( + expr_call.args.span(), + "Single-item extrinsic calls must specify their origin as the first argument.", + )), + }; + + // remove first arg from expr_call + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + + extrinsic_call = Some(ExtrinsicCallDef::Encoded { origin, expr_call }); } } k += 1; } - } else if let Stmt::Local(local) = child { + } else if let Stmt::Expr(Expr::Block(block)) = child { let mut k = 0; // index of attr - for attr in &local.attrs { + for attr in &block.attrs { if let Some(segment) = attr.path.segments.last() { if let Ok(_) = syn::parse::( segment.ident.to_token_stream().into(), ) { - if let Some((_, boxed)) = &local.init { - if let Expr::Call(call) = &**boxed { - // found let-style extrinsic call - let mut local = local.clone(); - local.attrs.remove(k); // consume #[extrinsic_call] - expr_call = Some(call.clone()); - if let Pat::Ident(ident) = local.pat { - lhs_var_name = Some(ident.ident.clone()); - } - } - } + let mut block = block.clone(); + + // consume #[extrinsic_call] tokens + block.attrs.remove(k); + + extrinsic_call = Some(ExtrinsicCallDef::Block(block)); } } k += 1; } } - if let Some(mut expr_call) = expr_call { - let origin = match expr_call.args.first() { - Some(arg) => arg.clone(), - None => - return Err(Error::new( - expr_call.args.span(), - "Extrinsic call must specify its origin as the first argument.", - )), - }; - let mut final_args = Punctuated::::new(); - let args: Vec<&Expr> = expr_call.args.iter().collect(); - for arg in &args[1..] { - final_args.push((*(*arg)).clone()); - } - expr_call.args = final_args; + if let Some(extrinsic_call) = extrinsic_call { return Ok(BenchmarkDef { params, setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - extrinsic_call: expr_call, - extrinsic_call_lhs_var_name: lhs_var_name, - origin, + extrinsic_call, verify_stmts: Vec::from( &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], ), @@ -182,7 +186,7 @@ impl BenchmarkDef { } return Err(Error::new( item_fn.block.brace_token.span, - "Missing #[extrinsic_call] annotation in benchmark function body.", + "No valid #[extrinsic_call] annotation found in benchmark function body.", )) } } From 43e461f87fa0221aca698edef44149f717465d25 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 6 Jan 2023 17:46:33 -0500 Subject: [PATCH 069/146] working now but some benchmark tests failing --- frame/message-queue/src/benchmarking.rs | 9 +++ frame/support/procedural/src/benchmark.rs | 77 ++++++++++++----------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 1261e7eeeddee..20748305f382e 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -58,6 +58,7 @@ mod benchmarks { } // Worst case path of `ready_ring_unknit`. + #[benchmark] fn ready_ring_unknit() { build_ring::(&[0.into(), 1.into(), 2.into()]); assert_ring::(&[0.into(), 1.into(), 2.into()]); @@ -73,6 +74,7 @@ mod benchmarks { } // `service_queues` without any queue processing. + #[benchmark] fn service_queue_base() { #[extrinsic_call] { @@ -81,6 +83,7 @@ mod benchmarks { } // `service_page` without any message processing but with page completion. + #[benchmark] fn service_page_base_completion() { let origin: MessageOriginOf = 0.into(); let page = PageOf::::default(); @@ -96,6 +99,7 @@ mod benchmarks { } // `service_page` without any message processing and without page completion. + #[benchmark] fn service_page_base_no_completion() { let origin: MessageOriginOf = 0.into(); let mut page = PageOf::::default(); @@ -114,6 +118,7 @@ mod benchmarks { } // Processing a single message from a page. + #[benchmark] fn service_page_item() { let msg = vec![1u8; MaxMessageLenOf::::get() as usize]; let mut page = page::(&msg.clone()); @@ -150,6 +155,7 @@ mod benchmarks { } // Worst case for calling `bump_service_head`. + #[benchmark] fn bump_service_head() { setup_bump_service_head::(0.into(), 10.into()); let mut weight = WeightMeter::max_limit(); @@ -163,6 +169,7 @@ mod benchmarks { assert_eq!(weight.consumed, T::WeightInfo::bump_service_head()); } + #[benchmark] fn reap_page() { // Mock the storage to get a *cullable* but not *reapable* page. let origin: MessageOriginOf = 0.into(); @@ -193,6 +200,7 @@ mod benchmarks { // // The worst case occurs when executing the last message in a page of which all are skipped // since it is using `peek_index` which has linear complexities. + #[benchmark] fn execute_overweight_page_removed() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); @@ -230,6 +238,7 @@ mod benchmarks { } // Worst case for `execute_overweight` where the page is updated. + #[benchmark] fn execute_overweight_page_updated() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 4a8b35ec5cce8..17013b0ce05d8 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -43,8 +43,7 @@ struct RangeArgs { #[derive(Clone)] enum ExtrinsicCallDef { Encoded { origin: Expr, expr_call: ExprCall }, // no block, just an extrinsic call - Block(ExprBlock), /* something in a block, we don't care what - * it is */ + Block(ExprBlock), // call is somewhere in the block } /// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. @@ -138,15 +137,6 @@ impl BenchmarkDef { "Single-item extrinsic calls must specify their origin as the first argument.", )), }; - - // remove first arg from expr_call - let mut final_args = Punctuated::::new(); - let args: Vec<&Expr> = expr_call.args.iter().collect(); - for arg in &args[1..] { - final_args.push((*(*arg)).clone()); - } - expr_call.args = final_args; - extrinsic_call = Some(ExtrinsicCallDef::Encoded { origin, expr_call }); } } @@ -556,8 +546,6 @@ fn expand_benchmark( let codec = quote!(#krate::frame_support::codec); let traits = quote!(#krate::frame_support::traits); let setup_stmts = benchmark_def.setup_stmts; - let mut extrinsic_call = benchmark_def.extrinsic_call; - let origin = benchmark_def.origin; let verify_stmts = benchmark_def.verify_stmts; let test_ident = Ident::new(format!("test_{}", name.to_string()).as_str(), Span::call_site()); @@ -577,22 +565,45 @@ fn expand_benchmark( true => quote!(T: Config, I: 'static), }; - // modify extrinsic call to be prefixed with new_call_variant - if let Expr::Path(expr_path) = &mut *extrinsic_call.func { - if let Some(segment) = expr_path.path.segments.last_mut() { - segment.ident = Ident::new( - format!("new_call_variant_{}", segment.ident.to_string()).as_str(), - Span::call_site(), - ); - } // else handle error? - } // else handle error? - - let final_call = match benchmark_def.extrinsic_call_lhs_var_name { - Some(var) => quote! { - let #var = Call::<#generics>::#extrinsic_call; - let __call = #var; + let (final_call, post_call) = match benchmark_def.extrinsic_call { + ExtrinsicCallDef::Encoded { origin, expr_call } => { + let mut expr_call = expr_call.clone(); + + // remove first arg from expr_call + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + + // modify extrinsic call to be prefixed with new_call_variant + if let Expr::Path(expr_path) = &mut *expr_call.func { + if let Some(segment) = expr_path.path.segments.last_mut() { + segment.ident = Ident::new( + format!("new_call_variant_{}", segment.ident.to_string()).as_str(), + Span::call_site(), + ); + } + } + ( + quote! { + let __call = Call::<#generics>::#expr_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); + }, + quote! { + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin.into(); + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + )?; + }, + ) }, - None => quote!(let __call = Call::<#generics>::#extrinsic_call;), + ExtrinsicCallDef::Block(block) => (quote!(#block), quote!()), }; // generate final quoted tokens @@ -634,16 +645,8 @@ fn expand_benchmark( #setup_stmts )* #final_call // extrinsic call - let __benchmarked_call_encoded = #codec::Encode::encode(&__call); Ok(#krate::Box::new(move || -> Result<(), #krate::BenchmarkError> { - let __call_decoded = as #codec::Decode> - ::decode(&mut &__benchmarked_call_encoded[..]) - .expect("call is encoded above, encoding must be correct"); - let __origin = #origin.into(); - as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( - __call_decoded, - __origin, - )?; + #post_call if verify { #( #verify_stmts From 50fd18a7b2b70e837384e76e1585c21d48db305d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 6 Jan 2023 21:07:02 -0500 Subject: [PATCH 070/146] message-queue tests working (run order issue fixed) :tada: --- frame/support/procedural/src/benchmark.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 17013b0ce05d8..79ccd1a1b2ce3 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -565,7 +565,7 @@ fn expand_benchmark( true => quote!(T: Config, I: 'static), }; - let (final_call, post_call) = match benchmark_def.extrinsic_call { + let (pre_call, post_call) = match benchmark_def.extrinsic_call { ExtrinsicCallDef::Encoded { origin, expr_call } => { let mut expr_call = expr_call.clone(); @@ -603,7 +603,7 @@ fn expand_benchmark( }, ) }, - ExtrinsicCallDef::Block(block) => (quote!(#block), quote!()), + ExtrinsicCallDef::Block(block) => (quote!(), quote!(#block)), }; // generate final quoted tokens @@ -644,7 +644,7 @@ fn expand_benchmark( #( #setup_stmts )* - #final_call // extrinsic call + #pre_call Ok(#krate::Box::new(move || -> Result<(), #krate::BenchmarkError> { #post_call if verify { From e27951220d0811aec0ab2ac69b620c530433e7ad Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 6 Jan 2023 22:33:59 -0500 Subject: [PATCH 071/146] add comments and internal docs for fame_support_procedural::benchmark --- frame/support/procedural/src/benchmark.rs | 41 ++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 79ccd1a1b2ce3..ef769053da54e 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,3 +1,22 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +//! Home of the parsing and expansion code for the new pallet benchmarking syntax + use derive_syn_parse::Parse; use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; @@ -40,6 +59,7 @@ struct RangeArgs { _gt_token: Gt, } +/// Represents the parsed extrinsic call for a benchmark #[derive(Clone)] enum ExtrinsicCallDef { Encoded { origin: Expr, expr_call: ExprCall }, // no block, just an extrinsic call @@ -64,6 +84,8 @@ impl BenchmarkDef { let mut params: Vec = Vec::new(); let mut extra = false; let mut skip_meta = false; + + // detect extra and skip_meta attributes for attr in &item_fn.attrs { if let Some(segment) = attr.path.segments.last() { if let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) @@ -76,8 +98,9 @@ impl BenchmarkDef { } } } + + // parse params such as "x: Linear<0, 1>" for arg in &item_fn.sig.inputs { - // parse params such as "x: Linear<0, 1>" let mut name: Option = None; let mut typ: Option<&Type> = None; let mut start: Option = None; @@ -112,11 +135,12 @@ impl BenchmarkDef { )) } } + + // #[extrinsic_call] handling for child in &item_fn.block.stmts { let mut extrinsic_call: Option = None; - - // #[extrinsic_call] handling for the non-block (single item) case if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { + // non-block (encoded) case let mut k = 0; // index of attr for attr in &expr_call.attrs { if let Some(segment) = attr.path.segments.last() { @@ -143,6 +167,7 @@ impl BenchmarkDef { k += 1; } } else if let Stmt::Expr(Expr::Block(block)) = child { + // block case let mut k = 0; // index of attr for attr in &block.attrs { if let Some(segment) = attr.path.segments.last() { @@ -160,6 +185,8 @@ impl BenchmarkDef { k += 1; } } + + // return parsed BenchmarkDef if let Some(extrinsic_call) = extrinsic_call { return Ok(BenchmarkDef { params, @@ -176,11 +203,12 @@ impl BenchmarkDef { } return Err(Error::new( item_fn.block.brace_token.span, - "No valid #[extrinsic_call] annotation found in benchmark function body.", + "No valid #[extrinsic_call] annotation could be found in benchmark function body.", )) } } +/// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> TokenStream { let module = parse_macro_input!(tokens as ItemMod); let where_clause = match syn::parse::(attrs.clone()) { @@ -506,6 +534,7 @@ struct UnrolledParams { } impl UnrolledParams { + /// Constructs an [`UnrolledParams`] from a [`Vec`] fn from(params: &Vec) -> UnrolledParams { let param_ranges: Vec = params .iter() @@ -534,6 +563,7 @@ impl UnrolledParams { } } +/// Performs expansion of an already-parsed [`BenchmarkDef`]. fn expand_benchmark( benchmark_def: BenchmarkDef, name: &Ident, @@ -586,6 +616,7 @@ fn expand_benchmark( ); } } + // (pre_call, post_call): ( quote! { let __call = Call::<#generics>::#expr_call; @@ -741,6 +772,7 @@ fn expand_benchmark( res } +/// Performs macro parsing and expansion for an individual `#[benchmark]` invocation pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> TokenStream { // parse attached item as a function def let item_fn = parse_macro_input!(tokens as ItemFn); @@ -751,5 +783,6 @@ pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> Err(err) => return err.to_compile_error().into(), }; + // perform expansion expand_benchmark(benchmark_def, &item_fn.sig.ident, is_instance, quote!()).into() } From 7f2c3ed8085c3ba6fcdda5e48d037396d6d442c3 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 6 Jan 2023 22:36:49 -0500 Subject: [PATCH 072/146] fix license years --- frame/message-queue/src/benchmarking.rs | 2 +- frame/support/procedural/src/benchmark.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 20748305f382e..a706b2f22f868 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index ef769053da54e..9015853f09dbb 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); From 73511ed9118e9629a44f9731b5213f20cbaf8a4f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 7 Jan 2023 19:10:10 -0500 Subject: [PATCH 073/146] docs for lib.rs --- frame/support/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index ddabe62eb0ccf..06cdb20070d74 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2750,9 +2750,11 @@ pub mod pallet_macros { // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); +/// Contains macros, structs, and traits associated with v2 of the pallet benchmarking syntax. pub mod benchmarking { pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmarks}; + // Used in #[benchmark] to ensure that benchmark function arguments implement [`ParamRange`]. #[doc(hidden)] pub use static_assertions::assert_impl_all; @@ -2766,7 +2768,10 @@ pub mod benchmarking { /// [`Linear`] but this could later be extended to support additional non-linear parameter /// ranges. 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; } From 3b1387c66d82687c80771c9d0bc56ff7f94bda98 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 7 Jan 2023 20:02:37 -0500 Subject: [PATCH 074/146] add docs to new support procedural macros --- frame/support/procedural/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index be66d8ea7471e..c56f13a372a40 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -480,21 +480,37 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) } +/// 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. #[proc_macro_attribute] pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { benchmark::benchmarks(attr, tokens, false) } +// 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. #[proc_macro_attribute] pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { benchmark::benchmarks(attr, tokens, true) } +/// An attribute macro used to declare a benchmark within a benchmarking module. Must be +/// attached to a function definition containing an `#[extrinsic_call]` attribute. +/// +/// See `frame_support::benchmarking` for more info. #[proc_macro_attribute] pub fn benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStream { benchmark::benchmark(attrs, tokens, false) } +/// An attribute macro used to declare an instance benchmark within a benchmarking module. Must +/// be attached to a function definition containing an `#[extrinsic_call]` attribute. +/// +/// See `frame_support::benchmarking` for more info. #[proc_macro_attribute] pub fn instance_benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStream { benchmark::benchmark(attrs, tokens, true) From f3183938d7baa5046e51c9f0f27634bdd1c99164 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 7 Jan 2023 21:43:22 -0500 Subject: [PATCH 075/146] don't allow #[benchmark] outside of benchmarking module --- frame/support/procedural/src/benchmark.rs | 15 --------------- frame/support/procedural/src/lib.rs | 16 +++++----------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 9015853f09dbb..6c6ef5c6ff872 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -771,18 +771,3 @@ fn expand_benchmark( }; res } - -/// Performs macro parsing and expansion for an individual `#[benchmark]` invocation -pub fn benchmark(_attrs: TokenStream, tokens: TokenStream, is_instance: bool) -> TokenStream { - // parse attached item as a function def - let item_fn = parse_macro_input!(tokens as ItemFn); - - // build a BenchmarkDef from item_fn - let benchmark_def = match BenchmarkDef::from(&item_fn) { - Ok(def) => def, - Err(err) => return err.to_compile_error().into(), - }; - - // perform expansion - expand_benchmark(benchmark_def, &item_fn.sig.ident, is_instance, quote!()).into() -} diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index c56f13a372a40..01caa8ab0a300 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -503,17 +503,11 @@ pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStrea /// /// See `frame_support::benchmarking` for more info. #[proc_macro_attribute] -pub fn benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStream { - benchmark::benchmark(attrs, tokens, false) -} - -/// An attribute macro used to declare an instance benchmark within a benchmarking module. Must -/// be attached to a function definition containing an `#[extrinsic_call]` attribute. -/// -/// See `frame_support::benchmarking` for more info. -#[proc_macro_attribute] -pub fn instance_benchmark(attrs: TokenStream, tokens: TokenStream) -> TokenStream { - benchmark::benchmark(attrs, tokens, true) +pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[benchmark]` must be in a module labeled with #[benchmarks] or #[instance_benchmarks]." + )) + .into() } /// Execute the annotated function in a new storage transaction. From b885d62766920735aa95ffef0ab328a1d1f06266 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 8 Jan 2023 14:29:52 -0500 Subject: [PATCH 076/146] add docs --- frame/support/src/lib.rs | 165 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 4 deletions(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 06cdb20070d74..a6295bdfe8662 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2747,26 +2747,180 @@ pub mod pallet_macros { }; } -// Generate a macro that will enable/disable code based on `std` feature being active. -sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); - /// 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; @@ -2785,3 +2939,6 @@ pub mod benchmarking { } } } + +// Generate a macro that will enable/disable code based on `std` feature being active. +sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); From f7c745408f0df3d18521011122c5163f6e239391 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 9 Jan 2023 03:24:57 -0500 Subject: [PATCH 077/146] use benchmark(extra, skip_meta) style args --- frame/balances/src/benchmarking.rs | 6 +- frame/support/procedural/src/benchmark.rs | 91 +++++++++++++++++------ 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 8fd1cee99b9b3..b7ba2acb73add 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -59,8 +59,7 @@ mod benchmarks { // Benchmark `transfer` with the best possible condition: // * Both accounts exist and will continue to exist. - #[benchmark] - #[extra] + #[benchmark(extra)] fn transfer_best_case() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); @@ -170,8 +169,7 @@ mod benchmarks { // This benchmark performs the same operation as `transfer` in the worst case scenario, // but additionally introduces many new users into the storage, increasing the the merkle // trie and PoV size. - #[extra] - #[benchmark] + #[benchmark(extra)] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. let existential_deposit = T::ExistentialDeposit::get(); diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 6c6ef5c6ff872..b7d7df66aef2a 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -22,13 +22,14 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ - parse::Nothing, + parenthesized, + parse::{Nothing, ParseStream}, parse_macro_input, punctuated::Punctuated, spanned::Spanned, - token::{Comma, Gt, Lt}, + token::{Comma, Gt, Lt, Paren}, Error, Expr, ExprBlock, ExprCall, FnArg, Item, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, - Type, WhereClause, + Token, Type, WhereClause, }; mod keywords { @@ -59,6 +60,65 @@ struct RangeArgs { _gt_token: Gt, } +#[derive(Clone, Debug)] +struct BenchmarkAttrs { + skip_meta: bool, + extra: bool, +} + +/// Represents a single benchmark option +enum BenchmarkAttrKeyword { + Extra, + SkipMeta, +} + +impl syn::parse::Parse for BenchmarkAttrKeyword { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::extra) { + let _extra: keywords::extra = input.parse()?; + return Ok(BenchmarkAttrKeyword::Extra) + } else if lookahead.peek(keywords::skip_meta) { + let _skip_meta: keywords::skip_meta = input.parse()?; + return Ok(BenchmarkAttrKeyword::SkipMeta) + } else { + return Err(lookahead.error()) + } + } +} + +impl syn::parse::Parse for BenchmarkAttrs { + fn parse(input: ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if !lookahead.peek(Paren) { + let _nothing: Nothing = input.parse()?; + return Ok(BenchmarkAttrs { skip_meta: false, extra: false }) + } + let content; + let _paren: Paren = parenthesized!(content in input); + let mut extra = false; + let mut skip_meta = false; + let args = Punctuated::::parse_terminated(&content)?; + for arg in args.into_iter() { + match arg { + BenchmarkAttrKeyword::Extra => { + if extra { + return Err(content.error("`extra` can only be specified once")) + } + extra = true; + }, + BenchmarkAttrKeyword::SkipMeta => { + if skip_meta { + return Err(content.error("`skip_meta` can only be specified once")) + } + skip_meta = true; + }, + } + } + Ok(BenchmarkAttrs { extra, skip_meta }) + } +} + /// Represents the parsed extrinsic call for a benchmark #[derive(Clone)] enum ExtrinsicCallDef { @@ -79,25 +139,9 @@ struct BenchmarkDef { impl BenchmarkDef { /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. - pub fn from(item_fn: &ItemFn) -> Result { + pub fn from(item_fn: &ItemFn, extra: bool, skip_meta: bool) -> Result { let mut i = 0; // index of child let mut params: Vec = Vec::new(); - let mut extra = false; - let mut skip_meta = false; - - // detect extra and skip_meta attributes - for attr in &item_fn.attrs { - if let Some(segment) = attr.path.segments.last() { - if let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) - { - extra = true; - } else if let Ok(_) = - syn::parse::(segment.ident.to_token_stream().into()) - { - skip_meta = true; - } - } - } // parse params such as "x: Linear<0, 1>" for arg in &item_fn.sig.inputs { @@ -222,6 +266,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); + let mut args: Option = None; if let Some(mut content) = module.content { for stmt in &mut content.1 { let mut found_item: Option = None; @@ -232,6 +277,8 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To if let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) { + let tokens = attr.tokens.to_token_stream().into(); + args = Some(parse_macro_input!(tokens as BenchmarkAttrs)); func.attrs.remove(i); found_item = Some(func.clone()); i += 1; @@ -239,9 +286,9 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To } } } - if let Some(item_fn) = found_item { + if let (Some(item_fn), Some(args)) = (found_item, &args) { // this item is a #[benchmark] or #[instance_benchmark] - let benchmark_def = match BenchmarkDef::from(&item_fn) { + let benchmark_def = match BenchmarkDef::from(&item_fn, args.extra, args.skip_meta) { Ok(def) => def, Err(err) => return err.to_compile_error().into(), }; From 2877c2c7d352c176b85cffeb2bf7621703e4c63c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 9 Jan 2023 03:31:58 -0500 Subject: [PATCH 078/146] update docs accordingly --- frame/support/src/lib.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index a6295bdfe8662..39470708cbc11 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2851,17 +2851,12 @@ pub mod pallet_macros { /// 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]` +/// ### Optional Attributes /// -/// 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`. +/// 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`. /// /// ## Where Clause /// From 5df08c98d95d6d82be39e1731645db325e4fcfa5 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 9 Jan 2023 03:42:12 -0500 Subject: [PATCH 079/146] appease clippy --- frame/support/procedural/src/benchmark.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index b7d7df66aef2a..ba913ac363596 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -185,8 +185,7 @@ impl BenchmarkDef { let mut extrinsic_call: Option = None; if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { // non-block (encoded) case - let mut k = 0; // index of attr - for attr in &expr_call.attrs { + for (k, attr) in (&expr_call.attrs).iter().enumerate() { if let Some(segment) = attr.path.segments.last() { if let Ok(_) = syn::parse::( segment.ident.to_token_stream().into(), @@ -208,12 +207,10 @@ impl BenchmarkDef { extrinsic_call = Some(ExtrinsicCallDef::Encoded { origin, expr_call }); } } - k += 1; } } else if let Stmt::Expr(Expr::Block(block)) = child { // block case - let mut k = 0; // index of attr - for attr in &block.attrs { + for (k, attr) in (&block.attrs).iter().enumerate() { if let Some(segment) = attr.path.segments.last() { if let Ok(_) = syn::parse::( segment.ident.to_token_stream().into(), @@ -226,7 +223,6 @@ impl BenchmarkDef { extrinsic_call = Some(ExtrinsicCallDef::Block(block)); } } - k += 1; } } From 7802bfd0bdff598f71cc34c906dc82023066f976 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 9 Jan 2023 04:22:46 -0500 Subject: [PATCH 080/146] bump ci From 09bd5ee6a96df3595ecb90d485857bb2da947be2 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 9 Jan 2023 13:07:46 -0500 Subject: [PATCH 081/146] add notes about `extra` and `skip_meta` --- frame/support/src/lib.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 39470708cbc11..16d80e64762b8 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2856,7 +2856,19 @@ pub mod pallet_macros { /// 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`. +/// 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 /// From c601966c322e7bb8fe1ff5e25a79eb7ab49e9743 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 9 Jan 2023 14:16:05 -0500 Subject: [PATCH 082/146] fix doc tests --- frame/support/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 16d80e64762b8..7756beeff095f 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2765,7 +2765,7 @@ pub mod pallet_macros { /// /// ## General Syntax /// -/// ``` +/// ```no_run /// #![cfg(feature = "runtime-benchmarks")] /// /// use super::{mock_helpers::*, Pallet as MyPallet}; @@ -2877,7 +2877,7 @@ pub mod pallet_macros { /// 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. /// -/// ``` +/// ```no_run /// #[benchmarks( /// where /// <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, @@ -2895,7 +2895,7 @@ pub mod pallet_macros { /// including the `frame_benchmarking::impl_benchmark_test_suite` macro. /// /// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module): -/// ``` +/// ```no_run /// #[benchmarks] /// mod benchmarks { /// use super::*; From 7c8af264ca38e51445ff1e4762557fe4e4e8ab0a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 9 Jan 2023 14:32:37 -0500 Subject: [PATCH 083/146] re-run CI From 4b3707e4d6b42e5ce2380dafade737562b309a34 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 9 Jan 2023 14:53:17 -0500 Subject: [PATCH 084/146] use `ignore` instead of `no_run` on doc examples --- frame/support/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 7756beeff095f..b3f8c8d413ce1 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2765,7 +2765,7 @@ pub mod pallet_macros { /// /// ## General Syntax /// -/// ```no_run +/// ```ignore /// #![cfg(feature = "runtime-benchmarks")] /// /// use super::{mock_helpers::*, Pallet as MyPallet}; @@ -2877,7 +2877,7 @@ pub mod pallet_macros { /// 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. /// -/// ```no_run +/// ```ignore /// #[benchmarks( /// where /// <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, @@ -2895,7 +2895,7 @@ pub mod pallet_macros { /// including the `frame_benchmarking::impl_benchmark_test_suite` macro. /// /// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module): -/// ```no_run +/// ```ignore /// #[benchmarks] /// mod benchmarks { /// use super::*; From e6a671f6913f15ca2a61b611cffd4540d1aea750 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 11 Jan 2023 15:06:45 -0500 Subject: [PATCH 085/146] bump CI From 6e808af64dcb8f40228ad2e419fc20117342e3f6 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 11 Jan 2023 16:35:06 -0500 Subject: [PATCH 086/146] replace some if-lets with if-elses --- frame/support/procedural/src/benchmark.rs | 50 ++++++++--------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index ba913ac363596..e172ced188a9c 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -145,39 +145,23 @@ impl BenchmarkDef { // parse params such as "x: Linear<0, 1>" for arg in &item_fn.sig.inputs { - let mut name: Option = None; - let mut typ: Option<&Type> = None; - let mut start: Option = None; - let mut end: Option = None; - if let FnArg::Typed(arg) = arg { - if let Pat::Ident(ident) = &*arg.pat { - name = Some(ident.ident.to_token_stream().to_string()); - } - let tmp = &*arg.ty; - typ = Some(tmp); - if let Type::Path(tpath) = tmp { - if let Some(segment) = tpath.path.segments.last() { - let args = segment.arguments.to_token_stream().into(); - if let Ok(args) = syn::parse::(args) { - if let Ok(start_parsed) = args.start.base10_parse::() { - start = Some(start_parsed); - } - if let Ok(end_parsed) = args.end.base10_parse::() { - end = Some(end_parsed); - } - } - } - } - } - if let (Some(name), Some(typ), Some(start), Some(end)) = (name, typ, start, end) { - // if true, this iteration of param extraction was successful - params.push(ParamDef { name, typ: typ.clone(), start, end }); - } else { - return Err(Error::new( - arg.span(), - "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", - )) - } + let span = arg.span(); + let invalid_param = || { + return Err(Error::new(span, "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", )) + }; + + let FnArg::Typed(arg) = arg else { return invalid_param() }; + let Pat::Ident(ident) = &*arg.pat else { return invalid_param() }; + let name = ident.ident.to_token_stream().to_string(); + let typ = &*arg.ty; + let Type::Path(tpath) = typ else { return invalid_param() }; + let Some(segment) = tpath.path.segments.last() else { return invalid_param() }; + let args = segment.arguments.to_token_stream().into(); + let Ok(args) = syn::parse::(args) else { return invalid_param() }; + let Ok(start) = args.start.base10_parse::() else { return invalid_param() }; + let Ok(end) = args.end.base10_parse::() else { return invalid_param() }; + + params.push(ParamDef { name, typ: typ.clone(), start, end }); } // #[extrinsic_call] handling From c907eb1d0cfcb448f9240e949a3b2bfb171be005 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 11 Jan 2023 17:43:20 -0500 Subject: [PATCH 087/146] more refactoring of if-let statements --- frame/support/procedural/src/benchmark.rs | 74 +++++++++++------------ 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index e172ced188a9c..e48bf8403c254 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -166,48 +166,42 @@ impl BenchmarkDef { // #[extrinsic_call] handling for child in &item_fn.block.stmts { - let mut extrinsic_call: Option = None; - if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { - // non-block (encoded) case - for (k, attr) in (&expr_call.attrs).iter().enumerate() { - if let Some(segment) = attr.path.segments.last() { - if let Ok(_) = syn::parse::( - segment.ident.to_token_stream().into(), - ) { - let mut expr_call = expr_call.clone(); - - // consume #[extrinsic_call] tokens - expr_call.attrs.remove(k); - - // extract origin from expr_call - let origin = match expr_call.args.first() { - Some(arg) => arg.clone(), - None => - return Err(Error::new( - expr_call.args.span(), - "Single-item extrinsic calls must specify their origin as the first argument.", - )), - }; - extrinsic_call = Some(ExtrinsicCallDef::Encoded { origin, expr_call }); - } + let mut extrinsic_call = None; + match child { + Stmt::Semi(Expr::Call(expr_call), _semi) => { + // non-block (encoded) case + for (k, attr) in (&expr_call.attrs).iter().enumerate() { + let Some(segment) = attr.path.segments.last() else { continue; }; + let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { continue; }; + let mut expr_call = expr_call.clone(); + + // consume #[extrinsic_call] tokens + expr_call.attrs.remove(k); + + // extract origin from expr_call + let origin = match expr_call.args.first() { + Some(arg) => arg.clone(), + None => return Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument.")), + }; + extrinsic_call = Some(ExtrinsicCallDef::Encoded { origin, expr_call }); + break } - } - } else if let Stmt::Expr(Expr::Block(block)) = child { - // block case - for (k, attr) in (&block.attrs).iter().enumerate() { - if let Some(segment) = attr.path.segments.last() { - if let Ok(_) = syn::parse::( - segment.ident.to_token_stream().into(), - ) { - let mut block = block.clone(); - - // consume #[extrinsic_call] tokens - block.attrs.remove(k); - - extrinsic_call = Some(ExtrinsicCallDef::Block(block)); - } + }, + Stmt::Expr(Expr::Block(block)) => { + // block case + for (k, attr) in (&block.attrs).iter().enumerate() { + let Some(segment) = attr.path.segments.last() else { continue; }; + let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { continue; }; + let mut block = block.clone(); + + // consume #[extrinsic_call] tokens + block.attrs.remove(k); + + extrinsic_call = Some(ExtrinsicCallDef::Block(block)); + break } - } + }, + _ => (), } // return parsed BenchmarkDef From 08268b516586028e95c5a82a41b284bec3caaa33 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 11 Jan 2023 23:10:32 -0500 Subject: [PATCH 088/146] fix remaining if-lets in BenchmarkDef::from() --- frame/support/procedural/src/benchmark.rs | 26 +++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index e48bf8403c254..10a2c8ac88fe7 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -203,21 +203,19 @@ impl BenchmarkDef { }, _ => (), } + let Some(extrinsic_call) = extrinsic_call else { + i += 1; + continue; + }; - // return parsed BenchmarkDef - if let Some(extrinsic_call) = extrinsic_call { - return Ok(BenchmarkDef { - params, - setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - extrinsic_call, - verify_stmts: Vec::from( - &item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()], - ), - extra, - skip_meta, - }) - } - i += 1; + return Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + extrinsic_call, + verify_stmts: Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), + extra, + skip_meta, + }) } return Err(Error::new( item_fn.block.brace_token.span, From ffa06eab41deccbabd73bd8aa0b2903856959d01 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 00:25:53 -0500 Subject: [PATCH 089/146] fix if-lets in benchmarks() --- frame/support/procedural/src/benchmark.rs | 98 +++++++++++------------ 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 10a2c8ac88fe7..74221fed5a606 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -20,7 +20,7 @@ use derive_syn_parse::Parse; use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; -use quote::{quote, ToTokens}; +use quote::{quote, quote_spanned, ToTokens}; use syn::{ parenthesized, parse::{Nothing, ParseStream}, @@ -227,6 +227,7 @@ impl BenchmarkDef { /// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> TokenStream { let module = parse_macro_input!(tokens as ItemMod); + let mod_span = module.span().clone(); let where_clause = match syn::parse::(attrs.clone()) { Ok(_) => quote!(), Err(_) => parse_macro_input!(attrs as WhereClause).predicates.to_token_stream(), @@ -238,57 +239,54 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); - let mut args: Option = None; - if let Some(mut content) = module.content { - for stmt in &mut content.1 { - let mut found_item: Option = None; - if let Item::Fn(func) = stmt { - let mut i = 0; - for attr in &func.attrs.clone() { - if let Some(seg) = attr.path.segments.last() { - if let Ok(_) = - syn::parse::(seg.ident.to_token_stream().into()) - { - let tokens = attr.tokens.to_token_stream().into(); - args = Some(parse_macro_input!(tokens as BenchmarkAttrs)); - func.attrs.remove(i); - found_item = Some(func.clone()); - i += 1; - } - } - } - } - if let (Some(item_fn), Some(args)) = (found_item, &args) { - // this item is a #[benchmark] or #[instance_benchmark] - let benchmark_def = match BenchmarkDef::from(&item_fn, args.extra, args.skip_meta) { - Ok(def) => def, - Err(err) => return err.to_compile_error().into(), - }; - - // expand benchmark - let expanded = expand_benchmark( - benchmark_def.clone(), - &item_fn.sig.ident, - instance, - where_clause.clone(), - ); - - // record benchmark name - let name = item_fn.sig.ident; - benchmark_names.push(name.clone()); - if benchmark_def.extra { - extra_benchmark_names.push(name.clone()); - } - if benchmark_def.skip_meta { - skip_meta_benchmark_names.push(name.clone()) - } + let Some(mut content) = module.content else { + // this will compile error already because attributes attached to empty modules are unstable + // but including error anyway to make this future-proof + return quote_spanned!(mod_span=> "Module cannot be empty!".to_compile_error()).into() + }; + for stmt in &mut content.1 { + let mut push_stmt = || { + expanded_stmts.push(stmt.to_token_stream()); + }; + let Item::Fn(mut func) = stmt.clone() else { push_stmt(); continue; }; + for (i, attr) in (&func.attrs.clone()).iter().enumerate() { + let Some(seg) = attr.path.segments.last() else { push_stmt(); continue; }; + let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) else { push_stmt(); continue; }; + let tokens = attr.tokens.to_token_stream().into(); + let args = parse_macro_input!(tokens as BenchmarkAttrs); + + // consume #[benchmark] attr + func.attrs.remove(i); + + // parse benchmark def + let benchmark_def = match BenchmarkDef::from(&func, args.extra, args.skip_meta) { + Ok(def) => def, + Err(err) => return err.to_compile_error().into(), + }; - expanded_stmts.push(expanded); - benchmark_defs.push(benchmark_def); - } else { - // this is not a benchmark item, copy it in verbatim - expanded_stmts.push(stmt.to_token_stream()); + // expand benchmark + let expanded = expand_benchmark( + benchmark_def.clone(), + &func.sig.ident, + instance, + where_clause.clone(), + ); + + // record benchmark name + let name = func.sig.ident; + + // process name vecs + benchmark_names.push(name.clone()); + if benchmark_def.extra { + extra_benchmark_names.push(name.clone()); + } + if benchmark_def.skip_meta { + skip_meta_benchmark_names.push(name.clone()) } + + expanded_stmts.push(expanded); + benchmark_defs.push(benchmark_def); + break } } From 8f3b7b594070ea652421b22ee7d700be07aa83a8 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 01:39:33 -0500 Subject: [PATCH 090/146] fix remaining if-lets, use nested find_map for extrinsic call --- frame/support/procedural/src/benchmark.rs | 89 +++++++++++------------ 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 74221fed5a606..a57946e72d0ba 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -140,7 +140,6 @@ struct BenchmarkDef { impl BenchmarkDef { /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. pub fn from(item_fn: &ItemFn, extra: bool, skip_meta: bool) -> Result { - let mut i = 0; // index of child let mut params: Vec = Vec::new(); // parse params such as "x: Linear<0, 1>" @@ -165,14 +164,12 @@ impl BenchmarkDef { } // #[extrinsic_call] handling - for child in &item_fn.block.stmts { - let mut extrinsic_call = None; + let Some(Ok((i, extrinsic_call))) = (&item_fn.block.stmts).iter().enumerate().find_map(|(i, child)| { match child { - Stmt::Semi(Expr::Call(expr_call), _semi) => { - // non-block (encoded) case - for (k, attr) in (&expr_call.attrs).iter().enumerate() { - let Some(segment) = attr.path.segments.last() else { continue; }; - let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { continue; }; + Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case + (&expr_call.attrs).iter().enumerate().find_map(|(k, attr)| { + let Some(segment) = attr.path.segments.last() else { return None; }; + let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { return None; }; let mut expr_call = expr_call.clone(); // consume #[extrinsic_call] tokens @@ -181,46 +178,40 @@ impl BenchmarkDef { // extract origin from expr_call let origin = match expr_call.args.first() { Some(arg) => arg.clone(), - None => return Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument.")), + None => return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))), }; - extrinsic_call = Some(ExtrinsicCallDef::Encoded { origin, expr_call }); - break - } + Some(Ok((i, ExtrinsicCallDef::Encoded { origin, expr_call }))) + }) }, - Stmt::Expr(Expr::Block(block)) => { - // block case - for (k, attr) in (&block.attrs).iter().enumerate() { - let Some(segment) = attr.path.segments.last() else { continue; }; - let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { continue; }; + Stmt::Expr(Expr::Block(block)) => { // #[block] case + (&block.attrs).iter().enumerate().find_map(|(k, attr)| { + let Some(segment) = attr.path.segments.last() else { return None; }; + let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { return None; }; let mut block = block.clone(); // consume #[extrinsic_call] tokens block.attrs.remove(k); - extrinsic_call = Some(ExtrinsicCallDef::Block(block)); - break - } + Some(Ok((i, ExtrinsicCallDef::Block(block)))) + }) }, - _ => (), + _ => None } - let Some(extrinsic_call) = extrinsic_call else { - i += 1; - continue; - }; + }) else { + return Err(Error::new( + item_fn.block.brace_token.span, + "No valid #[extrinsic_call] annotation could be found in benchmark function body.", + )) + }; - return Ok(BenchmarkDef { - params, - setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - extrinsic_call, - verify_stmts: Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), - extra, - skip_meta, - }) - } - return Err(Error::new( - item_fn.block.brace_token.span, - "No valid #[extrinsic_call] annotation could be found in benchmark function body.", - )) + Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + extrinsic_call, + verify_stmts: Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), + extra, + skip_meta, + }) } } @@ -625,16 +616,20 @@ fn expand_benchmark( expr_call.args = final_args; // modify extrinsic call to be prefixed with new_call_variant - if let Expr::Path(expr_path) = &mut *expr_call.func { - if let Some(segment) = expr_path.path.segments.last_mut() { - segment.ident = Ident::new( - format!("new_call_variant_{}", segment.ident.to_string()).as_str(), - Span::call_site(), - ); - } - } - // (pre_call, post_call): + let expr_span = expr_call.span().clone(); + let call_err = || { + quote_spanned!(expr_span=> "Extrinsic call must be a function call".to_compile_error()).into() + }; + let Expr::Path(expr_path) = &mut *expr_call.func else { return call_err(); }; + let Some(segment) = expr_path.path.segments.last_mut() else { return call_err(); }; + segment.ident = Ident::new( + // mutation occurs here + format!("new_call_variant_{}", segment.ident.to_string()).as_str(), + Span::call_site(), + ); + ( + // (pre_call, post_call): quote! { let __call = Call::<#generics>::#expr_call; let __benchmarked_call_encoded = #codec::Encode::encode(&__call); From 7410006dbb72e150a2842b84765a206c89b9ac18 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 01:49:27 -0500 Subject: [PATCH 091/146] switch to use #[extrinsic_call] or #[block] situationally --- frame/message-queue/src/benchmarking.rs | 18 +++++++++--------- frame/support/procedural/src/benchmark.rs | 9 +++++---- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index a706b2f22f868..0aa49937c5f30 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -47,7 +47,7 @@ mod benchmarks { assert_ring::(&[0.into(), 2.into()]); let mut neighbours = None; - #[extrinsic_call] + #[block] { neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); } @@ -65,7 +65,7 @@ mod benchmarks { let o: MessageOriginOf = 0.into(); let neighbours = BookStateFor::::get(&o).ready_neighbours.unwrap(); - #[extrinsic_call] + #[block] { MessageQueue::::ready_ring_unknit(&o, neighbours); } @@ -76,7 +76,7 @@ mod benchmarks { // `service_queues` without any queue processing. #[benchmark] fn service_queue_base() { - #[extrinsic_call] + #[block] { MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX); } @@ -92,7 +92,7 @@ mod benchmarks { let mut meter = WeightMeter::max_limit(); let limit = Weight::MAX; - #[extrinsic_call] + #[block] { MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); } @@ -111,7 +111,7 @@ mod benchmarks { let mut meter = WeightMeter::max_limit(); let limit = Weight::MAX; - #[extrinsic_call] + #[block] { MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); } @@ -126,7 +126,7 @@ mod benchmarks { assert!(page.peek_first().is_some(), "There is one message"); let mut weight = WeightMeter::max_limit(); - #[extrinsic_call] + #[block] { let status = MessageQueue::::service_page_item( &0u32.into(), @@ -160,7 +160,7 @@ mod benchmarks { setup_bump_service_head::(0.into(), 10.into()); let mut weight = WeightMeter::max_limit(); - #[extrinsic_call] + #[block] { MessageQueue::::bump_service_head(&mut weight); } @@ -213,7 +213,7 @@ mod benchmarks { Pages::::insert(&origin, 0, &page); BookStateFor::::insert(&origin, &book); - #[extrinsic_call] + #[block] { MessageQueue::::execute_overweight( RawOrigin::Signed(whitelisted_caller()).into(), @@ -250,7 +250,7 @@ mod benchmarks { Pages::::insert(&origin, 0, &page); BookStateFor::::insert(&origin, &book); - #[extrinsic_call] + #[block] { MessageQueue::::execute_overweight( RawOrigin::Signed(whitelisted_caller()).into(), diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index a57946e72d0ba..c246220cb3657 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -35,9 +35,10 @@ use syn::{ mod keywords { use syn::custom_keyword; - custom_keyword!(extrinsic_call); custom_keyword!(benchmark); + custom_keyword!(block); custom_keyword!(extra); + custom_keyword!(extrinsic_call); custom_keyword!(skip_meta); } @@ -186,10 +187,10 @@ impl BenchmarkDef { Stmt::Expr(Expr::Block(block)) => { // #[block] case (&block.attrs).iter().enumerate().find_map(|(k, attr)| { let Some(segment) = attr.path.segments.last() else { return None; }; - let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { return None; }; + let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { return None; }; let mut block = block.clone(); - // consume #[extrinsic_call] tokens + // consume #[block] tokens block.attrs.remove(k); Some(Ok((i, ExtrinsicCallDef::Block(block)))) @@ -200,7 +201,7 @@ impl BenchmarkDef { }) else { return Err(Error::new( item_fn.block.brace_token.span, - "No valid #[extrinsic_call] annotation could be found in benchmark function body.", + "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body.", )) }; From f226fc8d6bedac96b0a77aa6cd9bb541bafe91c3 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 02:01:25 -0500 Subject: [PATCH 092/146] refactor ExtrinsicCallDef => BenchmarkCallDef --- frame/support/procedural/src/benchmark.rs | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index c246220cb3657..e9e0f3968e08c 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -122,9 +122,9 @@ impl syn::parse::Parse for BenchmarkAttrs { /// Represents the parsed extrinsic call for a benchmark #[derive(Clone)] -enum ExtrinsicCallDef { - Encoded { origin: Expr, expr_call: ExprCall }, // no block, just an extrinsic call - Block(ExprBlock), // call is somewhere in the block +enum BenchmarkCallDef { + ExtrinsicCall { origin: Expr, expr_call: ExprCall }, // #[extrinsic_call] + Block(ExprBlock), // #[block] } /// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. @@ -132,7 +132,7 @@ enum ExtrinsicCallDef { struct BenchmarkDef { params: Vec, setup_stmts: Vec, - extrinsic_call: ExtrinsicCallDef, + call_def: BenchmarkCallDef, verify_stmts: Vec, extra: bool, skip_meta: bool, @@ -164,8 +164,8 @@ impl BenchmarkDef { params.push(ParamDef { name, typ: typ.clone(), start, end }); } - // #[extrinsic_call] handling - let Some(Ok((i, extrinsic_call))) = (&item_fn.block.stmts).iter().enumerate().find_map(|(i, child)| { + // #[extrinsic_call] / #[block] handling + let Some(Ok((i, call_def))) = (&item_fn.block.stmts).iter().enumerate().find_map(|(i, child)| { match child { Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case (&expr_call.attrs).iter().enumerate().find_map(|(k, attr)| { @@ -181,7 +181,7 @@ impl BenchmarkDef { Some(arg) => arg.clone(), None => return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))), }; - Some(Ok((i, ExtrinsicCallDef::Encoded { origin, expr_call }))) + Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call }))) }) }, Stmt::Expr(Expr::Block(block)) => { // #[block] case @@ -193,7 +193,7 @@ impl BenchmarkDef { // consume #[block] tokens block.attrs.remove(k); - Some(Ok((i, ExtrinsicCallDef::Block(block)))) + Some(Ok((i, BenchmarkCallDef::Block(block)))) }) }, _ => None @@ -208,7 +208,7 @@ impl BenchmarkDef { Ok(BenchmarkDef { params, setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - extrinsic_call, + call_def, verify_stmts: Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), extra, skip_meta, @@ -604,8 +604,8 @@ fn expand_benchmark( true => quote!(T: Config, I: 'static), }; - let (pre_call, post_call) = match benchmark_def.extrinsic_call { - ExtrinsicCallDef::Encoded { origin, expr_call } => { + let (pre_call, post_call) = match benchmark_def.call_def { + BenchmarkCallDef::ExtrinsicCall { origin, expr_call } => { let mut expr_call = expr_call.clone(); // remove first arg from expr_call @@ -647,7 +647,7 @@ fn expand_benchmark( }, ) }, - ExtrinsicCallDef::Block(block) => (quote!(), quote!(#block)), + BenchmarkCallDef::Block(block) => (quote!(), quote!(#block)), }; // generate final quoted tokens From 401eef536e6a3c11b9d5bf7d2bfc7f3b0938ffab Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 02:36:21 -0500 Subject: [PATCH 093/146] update docs with info about #[block] --- frame/support/procedural/src/benchmark.rs | 2 +- frame/support/procedural/src/lib.rs | 3 +- frame/support/src/lib.rs | 58 +++++++++++++---------- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index e9e0f3968e08c..e4157a81deeb2 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -684,7 +684,7 @@ fn expand_benchmark( .1; )* - // benchmark setup code (stuff before #[extrinsic_call]) + // benchmark setup code #( #setup_stmts )* diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 01caa8ab0a300..b6d2e315f2fab 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -499,7 +499,8 @@ pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStrea } /// An attribute macro used to declare a benchmark within a benchmarking module. Must be -/// attached to a function definition containing an `#[extrinsic_call]` attribute. +/// attached to a function definition containing an `#[extrinsic_call]` or `#[block]` +/// attribute. /// /// See `frame_support::benchmarking` for more info. #[proc_macro_attribute] diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index b3f8c8d413ce1..925febc63f5fd 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2794,14 +2794,14 @@ pub mod pallet_macros { /// // setup code /// let caller = whitelisted_caller(); /// -/// #[extrinsic_call] +/// #[block] /// { /// something(some, thing); /// my_extrinsic(RawOrigin::Signed(caller), some, argument); /// something_else(foo, bar); /// } /// -/// // verification_code +/// // verification code /// assert_eq!(MyPallet::::something(), 37); /// } /// } @@ -2827,29 +2827,39 @@ pub mod pallet_macros { /// 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. +/// 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]` /// -/// 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. +/// 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. +/// +/// 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 /// From 1016153082aa7104b8b6a01338f0a41d9addfb47 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 02:58:49 -0500 Subject: [PATCH 094/146] add macro stub for #[extrinsic_call] --- frame/support/procedural/src/lib.rs | 13 +++++++++++++ frame/support/src/lib.rs | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index b6d2e315f2fab..cb76de0ec64e3 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -511,6 +511,19 @@ pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { .into() } +/// An attribute macro used to specify the extrinsic call inside a benchmark function, and 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. +#[proc_macro_attribute] +pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[extrinsic_call]` must be in a module labeled with #[benchmarks] or #[instance_benchmarks]." + )) + .into() +} + /// Execute the annotated function in a new storage transaction. /// /// The return type of the annotated function must be `Result`. All changes to storage performed diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 925febc63f5fd..bc13ee1b6c425 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2918,7 +2918,9 @@ pub mod pallet_macros { /// } /// ``` pub mod benchmarking { - pub use frame_support_procedural::{benchmark, benchmarks, instance_benchmarks}; + pub use frame_support_procedural::{ + benchmark, benchmarks, extrinsic_call, instance_benchmarks, + }; // Used in #[benchmark] implementation to ensure that benchmark function arguments // implement [`ParamRange`]. From 47b679a482d95ae50909f041a21a51a7c8d23f56 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 03:14:21 -0500 Subject: [PATCH 095/146] fix docs and add stub for #[block] as well --- frame/support/procedural/src/lib.rs | 23 ++++++++++++++++++----- frame/support/src/lib.rs | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index cb76de0ec64e3..6427a61b7b298 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -489,10 +489,10 @@ pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { benchmark::benchmarks(attr, tokens, false) } -// 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. +/// 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. #[proc_macro_attribute] pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { benchmark::benchmarks(attr, tokens, true) @@ -519,7 +519,20 @@ pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { quote!(compile_error!( - "`#[extrinsic_call]` must be in a module labeled with #[benchmarks] or #[instance_benchmarks]." + "`#[extrinsic_call]` must be in a benchmark function definition labeled with `#[benchmark]`." + )) + .into() +} + +/// An attribute macro used to specify that a block should be the measured portion of the +/// 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. +#[proc_macro_attribute] +pub fn block(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[block]` must be in a benchmark function definition labeled with `#[benchmark]`." )) .into() } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index bc13ee1b6c425..4a12721239ccc 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2919,7 +2919,7 @@ pub mod pallet_macros { /// ``` pub mod benchmarking { pub use frame_support_procedural::{ - benchmark, benchmarks, extrinsic_call, instance_benchmarks, + benchmark, benchmarks, block, extrinsic_call, instance_benchmarks, }; // Used in #[benchmark] implementation to ensure that benchmark function arguments From dd8fc6ff57a50bbed73331c5eb4bab6208237507 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 03:17:42 -0500 Subject: [PATCH 096/146] remove unused extern crate line --- frame/support/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 4a12721239ccc..02e8bc1458532 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -83,8 +83,6 @@ pub mod migrations; pub mod traits; pub mod weights; -extern crate static_assertions; - #[doc(hidden)] pub mod unsigned { #[doc(hidden)] From fac2217a2f04c915502ce2af0855f9b724c2e304 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 03:31:36 -0500 Subject: [PATCH 097/146] fix clippy nits --- frame/support/procedural/src/benchmark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index e4157a81deeb2..ad563a7e5037c 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -219,7 +219,7 @@ impl BenchmarkDef { /// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> TokenStream { let module = parse_macro_input!(tokens as ItemMod); - let mod_span = module.span().clone(); + let mod_span = module.span(); let where_clause = match syn::parse::(attrs.clone()) { Ok(_) => quote!(), Err(_) => parse_macro_input!(attrs as WhereClause).predicates.to_token_stream(), @@ -617,7 +617,7 @@ fn expand_benchmark( expr_call.args = final_args; // modify extrinsic call to be prefixed with new_call_variant - let expr_span = expr_call.span().clone(); + let expr_span = expr_call.span(); let call_err = || { quote_spanned!(expr_span=> "Extrinsic call must be a function call".to_compile_error()).into() }; From bd229d020fb0c06869e1774f82b3c5fe06bd740a Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 12 Jan 2023 13:55:41 +0100 Subject: [PATCH 098/146] Use V2 bench syntax in pallet-example-basic Just testing the dev-ex... Signed-off-by: Oliver Tale-Yazdi --- frame/examples/basic/src/benchmarking.rs | 63 +++++++++++++++++------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/frame/examples/basic/src/benchmarking.rs b/frame/examples/basic/src/benchmarking.rs index 13f069c23e27b..7fa757d13b2a3 100644 --- a/frame/examples/basic/src/benchmarking.rs +++ b/frame/examples/basic/src/benchmarking.rs @@ -15,12 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Benchmarking for pallet-example-basic. +//! Benchmarking for `pallet-example-basic`. +// Only enable this module for benchmarking. #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::benchmarking::{benchmarks, Linear}; use frame_system::RawOrigin; // To actually run this benchmark on pallet-example-basic, we need to put this pallet into the @@ -33,14 +35,19 @@ use frame_system::RawOrigin; // Details on using the benchmarks macro can be seen at: // https://paritytech.github.io/substrate/master/frame_benchmarking/trait.Benchmarking.html#tymethod.benchmarks -benchmarks! { +#[benchmarks] +mod benchmarks { + use super::*; + // This will measure the execution time of `set_dummy`. - set_dummy_benchmark { + #[benchmark] + fn set_dummy_benchmark() { // This is the benchmark setup phase. // `set_dummy` is a constant time function, hence we hard-code some random value here. let value = 1000u32.into(); - }: set_dummy(RawOrigin::Root, value) // The execution phase is just running `set_dummy` extrinsic call - verify { + #[extrinsic_call] + set_dummy(RawOrigin::Root, value); // The execution phase is just running `set_dummy` extrinsic call + // This is the optional benchmark verification phase, asserting certain states. assert_eq!(Pallet::::dummy(), Some(value)) } @@ -49,22 +56,44 @@ benchmarks! { // 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. - accumulate_dummy { + #[benchmark] + fn accumulate_dummy() { let value = 1000u32.into(); // The caller account is whitelisted for DB reads/write by the benchmarking macro. let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), value) + + // You can use `_` if the name of the Call matches the benchmark name. + #[extrinsic_call] + // TODO use `_` + accumulate_dummy(RawOrigin::Signed(caller), value); + } + + /// You can write helper functions in here since its a normal Rust module. + fn setup_vector(len: u32) -> Vec { + let mut vector = Vec::::new(); + for i in (0..len).rev() { + vector.push(i); + } + vector + } // This will measure the execution time of sorting a vector. - sort_vector { - let x in 0 .. 10000; - let mut m = Vec::::new(); - for i in (0..x).rev() { - m.push(i); + // + // Define `x` as a linear component with range `[0, =10_000]`. This means that the benchmarking + // will assume that the weight grows at a linear rate depending on `x`. + #[benchmark] + fn sort_vector(x: Linear<0, 10_000>) { + let mut vector = setup_vector(x); + + // The benchmark execution phase could also be a closure with custom code: + #[block] + { + vector.sort(); } - }: { - // The benchmark execution phase could also be a closure with custom code - m.sort(); + + // Check that it was sorted correctly. This will not be benchmarked and is just for + // verification. + vector.windows(2).for_each(|w| assert!(w[0] <= w[1])); } // This line generates test cases for benchmarking, and could be run by: @@ -75,5 +104,5 @@ benchmarks! { // // The line generates three steps per benchmark, with repeat=1 and the three steps are // [low, mid, high] of the range. - impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test) + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); } From 91227f8f0ca32746ee15b34d93c58f6df814d547 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 11:30:27 -0500 Subject: [PATCH 099/146] carry over comment --- frame/balances/src/benchmarking.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index b7ba2acb73add..4f87fd6c53af9 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -34,6 +34,9 @@ const ED_MULTIPLIER: u32 = 10; mod benchmarks { use super::*; + // Benchmark `transfer` extrinsic with the worst possible conditions: + // * Transfer will kill the sender account. + // * Transfer will create the recipient account. #[benchmark] fn transfer() { let existential_deposit = T::ExistentialDeposit::get(); From 0443248054d18e8dae641e0a15a425ef54eee206 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 11:44:58 -0500 Subject: [PATCH 100/146] use curly-brace style for impl_benchmark_test_suite! --- frame/balances/src/benchmarking.rs | 4 ++-- frame/message-queue/src/benchmarking.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 4f87fd6c53af9..b72decdba8160 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -247,9 +247,9 @@ mod benchmarks { assert_eq!(Balances::::free_balance(&user), balance); } - impl_benchmark_test_suite!( + impl_benchmark_test_suite! { Balances, crate::tests_composite::ExtBuilder::default().build(), crate::tests_composite::Test, - ); + } } diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 0aa49937c5f30..4dc1bdf5345fd 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -274,9 +274,9 @@ mod benchmarks { assert!(Pages::::contains_key(&origin, 0), "Page must be updated"); } - impl_benchmark_test_suite!( + impl_benchmark_test_suite! { MessageQueue, crate::mock::new_test_ext::(), crate::integration_test::Test - ); + } } From 25b9093c01b86d698a02e951b72f40b2ec896425 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 11:53:05 -0500 Subject: [PATCH 101/146] remove unneeded parenthesis --- frame/support/procedural/src/benchmark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index ad563a7e5037c..1e3942dc7f4f5 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -165,7 +165,7 @@ impl BenchmarkDef { } // #[extrinsic_call] / #[block] handling - let Some(Ok((i, call_def))) = (&item_fn.block.stmts).iter().enumerate().find_map(|(i, child)| { + let Some(Ok((i, call_def))) = item_fn.block.stmts.iter().enumerate().find_map(|(i, child)| { match child { Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case (&expr_call.attrs).iter().enumerate().find_map(|(k, attr)| { @@ -185,7 +185,7 @@ impl BenchmarkDef { }) }, Stmt::Expr(Expr::Block(block)) => { // #[block] case - (&block.attrs).iter().enumerate().find_map(|(k, attr)| { + block.attrs.iter().enumerate().find_map(|(k, attr)| { let Some(segment) = attr.path.segments.last() else { return None; }; let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { return None; }; let mut block = block.clone(); From 82fa413b15803c328c732fe453249e25a6484e06 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 17:35:24 -0500 Subject: [PATCH 102/146] proper handling of _() extrinsic call style --- frame/message-queue/src/benchmarking.rs | 2 +- frame/support/procedural/src/benchmark.rs | 45 +++++++++++++++++------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 4dc1bdf5345fd..9651c81e5e66d 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -190,7 +190,7 @@ mod benchmarks { assert!(Pages::::contains_key(&origin, 0)); #[extrinsic_call] - reap_page(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); + _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); assert_last_event::(Event::PageReaped { origin: 0.into(), index: 0 }.into()); assert!(!Pages::::contains_key(&origin, 0)); diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 1e3942dc7f4f5..1832de045ea40 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -27,9 +27,9 @@ use syn::{ parse_macro_input, punctuated::Punctuated, spanned::Spanned, - token::{Comma, Gt, Lt, Paren}, - Error, Expr, ExprBlock, ExprCall, FnArg, Item, ItemFn, ItemMod, LitInt, Pat, Result, Stmt, - Token, Type, WhereClause, + token::{Colon2, Comma, Gt, Lt, Paren}, + Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, LitInt, Pat, Path, + PathArguments, PathSegment, Result, Stmt, Token, Type, WhereClause, }; mod keywords { @@ -181,6 +181,7 @@ impl BenchmarkDef { Some(arg) => arg.clone(), None => return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))), }; + Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call }))) }) }, @@ -616,18 +617,38 @@ fn expand_benchmark( } expr_call.args = final_args; - // modify extrinsic call to be prefixed with new_call_variant + // determine call name (handles `_` and normal call syntax) let expr_span = expr_call.span(); let call_err = || { - quote_spanned!(expr_span=> "Extrinsic call must be a function call".to_compile_error()).into() + quote_spanned!(expr_span=> "Extrinsic call must be a function call or `_`".to_compile_error()).into() }; - let Expr::Path(expr_path) = &mut *expr_call.func else { return call_err(); }; - let Some(segment) = expr_path.path.segments.last_mut() else { return call_err(); }; - segment.ident = Ident::new( - // mutation occurs here - format!("new_call_variant_{}", segment.ident.to_string()).as_str(), - Span::call_site(), - ); + let call_name = match *expr_call.func { + Expr::Path(expr_path) => { + // normal function call + let Some(segment) = expr_path.path.segments.last() else { return call_err(); }; + segment.ident.to_string() + }, + Expr::Verbatim(tokens) => { + // `_` style + // replace `_` with fn name + let Ok(_) = syn::parse::(tokens.to_token_stream().into()) else { return call_err(); }; + name.to_string() + }, + _ => return call_err(), + }; + + // modify extrinsic call to be prefixed with "new_call_variant" + let call_name = format!("new_call_variant_{}", call_name); + let mut punct: Punctuated = Punctuated::new(); + punct.push(PathSegment { + arguments: PathArguments::None, + ident: Ident::new(call_name.as_str(), Span::call_site()), + }); + *expr_call.func = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Path { leading_colon: None, segments: punct }, + }); ( // (pre_call, post_call): From 0a371f5f75be189639b7b1325ee87fb41b06906a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 19:41:49 -0500 Subject: [PATCH 103/146] add docs for _() syntax --- frame/support/src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 02e8bc1458532..a4ff17d73f7fb 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2843,6 +2843,17 @@ pub mod pallet_macros { /// 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 From 67c227d0363d436142cd88044bca7bf2040d85cb Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 20:27:39 -0500 Subject: [PATCH 104/146] fix crate access --- frame/support/procedural/src/benchmark.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 1832de045ea40..f51305982e807 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -18,6 +18,7 @@ //! Home of the parsing and expansion code for the new pallet benchmarking syntax use derive_syn_parse::Parse; +use frame_support_procedural_tools::generate_crate_access_2018; use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned, ToTokens}; @@ -293,7 +294,10 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To true => quote!(T: Config, I: 'static), }; - let krate = quote!(::frame_benchmarking); + let krate = match generate_crate_access_2018("frame_benchmarking") { + Ok(ident) => ident, + Err(err) => return err.to_compile_error().into(), + }; let support = quote!(#krate::frame_support); // benchmark name variables @@ -581,8 +585,11 @@ fn expand_benchmark( where_clause: TokenStream2, ) -> TokenStream2 { // set up variables needed during quoting - let krate = quote!(::frame_benchmarking); - let home = quote!(::frame_support::benchmarking); + let krate = match generate_crate_access_2018("frame_benchmarking") { + Ok(ident) => ident, + Err(err) => return err.to_compile_error().into(), + }; + let home = quote!(#krate::frame_support::benchmarking); let codec = quote!(#krate::frame_support::codec); let traits = quote!(#krate::frame_support::traits); let setup_stmts = benchmark_def.setup_stmts; From b8b8d504d63cb52e1135581517a400dfa59b0c3c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 20:29:44 -0500 Subject: [PATCH 105/146] simplify keyword access Co-authored-by: Keith Yeung --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index f51305982e807..ce841018a902c 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -189,7 +189,7 @@ impl BenchmarkDef { Stmt::Expr(Expr::Block(block)) => { // #[block] case block.attrs.iter().enumerate().find_map(|(k, attr)| { let Some(segment) = attr.path.segments.last() else { return None; }; - let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { return None; }; + let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?; let mut block = block.clone(); // consume #[block] tokens From cff3a2a095ce1e29ee63099a7f7b96d2d94d578e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 20:30:55 -0500 Subject: [PATCH 106/146] simplify module content destructuring Co-authored-by: Keith Yeung --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index ce841018a902c..0e492b3ef1417 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -233,7 +233,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); - let Some(mut content) = module.content else { + let Some((_brace, mut content)) = module.content else { // this will compile error already because attributes attached to empty modules are unstable // but including error anyway to make this future-proof return quote_spanned!(mod_span=> "Module cannot be empty!".to_compile_error()).into() From 5de75fee2aa29f6b4c6a17c784d76d4c88a68fcb Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 20:35:14 -0500 Subject: [PATCH 107/146] fix crate access "frame_benchmarking" => "frame-benchmarking", compiles --- frame/support/procedural/src/benchmark.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 0e492b3ef1417..f8435fb5e6d2c 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -238,7 +238,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To // but including error anyway to make this future-proof return quote_spanned!(mod_span=> "Module cannot be empty!".to_compile_error()).into() }; - for stmt in &mut content.1 { + for stmt in &mut content { let mut push_stmt = || { expanded_stmts.push(stmt.to_token_stream()); }; @@ -294,7 +294,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To true => quote!(T: Config, I: 'static), }; - let krate = match generate_crate_access_2018("frame_benchmarking") { + let krate = match generate_crate_access_2018("frame-benchmarking") { Ok(ident) => ident, Err(err) => return err.to_compile_error().into(), }; @@ -585,7 +585,7 @@ fn expand_benchmark( where_clause: TokenStream2, ) -> TokenStream2 { // set up variables needed during quoting - let krate = match generate_crate_access_2018("frame_benchmarking") { + let krate = match generate_crate_access_2018("frame-benchmarking") { Ok(ident) => ident, Err(err) => return err.to_compile_error().into(), }; From b2a24bd192bc3a8660667f2bdb820b56155522bb Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 20:40:43 -0500 Subject: [PATCH 108/146] use _() extrinsic call syntax where possible in balances --- frame/balances/src/benchmarking.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index b72decdba8160..f85ff43b715fe 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -54,7 +54,7 @@ mod benchmarks { existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); #[extrinsic_call] - transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); @@ -101,7 +101,7 @@ mod benchmarks { let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); #[extrinsic_call] - transfer_keep_alive(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); assert!(!Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); @@ -163,7 +163,7 @@ mod benchmarks { existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); #[extrinsic_call] - force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); + _(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); assert_eq!(Balances::::free_balance(&source), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); @@ -219,7 +219,7 @@ mod benchmarks { let _ = as Currency<_>>::make_free_balance_be(&caller, balance); #[extrinsic_call] - transfer_all(RawOrigin::Signed(caller.clone()), recipient_lookup, false); + _(RawOrigin::Signed(caller.clone()), recipient_lookup, false); assert!(Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), balance); @@ -241,7 +241,7 @@ mod benchmarks { assert!(Balances::::free_balance(&user).is_zero()); #[extrinsic_call] - force_unreserve(RawOrigin::Root, user_lookup, balance); + _(RawOrigin::Root, user_lookup, balance); assert!(Balances::::reserved_balance(&user).is_zero()); assert_eq!(Balances::::free_balance(&user), balance); From c588411bedd18ee3dcf3fefc1fec657f1bd402f2 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 20:44:14 -0500 Subject: [PATCH 109/146] simplify attr.path.segments.last() Co-authored-by: Keith Yeung --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index f8435fb5e6d2c..166ec295756d4 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -188,7 +188,7 @@ impl BenchmarkDef { }, Stmt::Expr(Expr::Block(block)) => { // #[block] case block.attrs.iter().enumerate().find_map(|(k, attr)| { - let Some(segment) = attr.path.segments.last() else { return None; }; + let segment = attr.path.segments.last()?; let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?; let mut block = block.clone(); From d6e29b2ab029c298f35fd1cad80f0a21529b4540 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 21:35:27 -0500 Subject: [PATCH 110/146] fix compile error being suppressed --- frame/support/procedural/src/benchmark.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 166ec295756d4..74f39a6629d42 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -166,7 +166,7 @@ impl BenchmarkDef { } // #[extrinsic_call] / #[block] handling - let Some(Ok((i, call_def))) = item_fn.block.stmts.iter().enumerate().find_map(|(i, child)| { + let (i, call_def) = match item_fn.block.stmts.iter().enumerate().find_map(|(i, child)| { match child { Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case (&expr_call.attrs).iter().enumerate().find_map(|(k, attr)| { @@ -200,11 +200,15 @@ impl BenchmarkDef { }, _ => None } - }) else { - return Err(Error::new( - item_fn.block.brace_token.span, - "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body.", - )) + }) { + Some(Ok((i, call_def))) => (i, call_def), + Some(Err(err)) => return Err(err), + _ => { + return Err(Error::new( + item_fn.block.brace_token.span, + "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body.", + )) + } }; Ok(BenchmarkDef { From 48fe1dac525aaa2512146bf8cfbdce45d77fb9bc Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 21:38:43 -0500 Subject: [PATCH 111/146] simplify extrinsic call keyword parsing Co-authored-by: Keith Yeung --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 74f39a6629d42..23f258dac1d7e 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -171,7 +171,7 @@ impl BenchmarkDef { Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case (&expr_call.attrs).iter().enumerate().find_map(|(k, attr)| { let Some(segment) = attr.path.segments.last() else { return None; }; - let Ok(_) = syn::parse::(segment.ident.to_token_stream().into()) else { return None; }; + let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; let mut expr_call = expr_call.clone(); // consume #[extrinsic_call] tokens From ce712165ab03f470f678c1b566861bb0a8ffff6a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 21:39:57 -0500 Subject: [PATCH 112/146] use ? operator instead of return None Co-authored-by: Keith Yeung --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 23f258dac1d7e..e0751ab4480f4 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -170,7 +170,7 @@ impl BenchmarkDef { match child { Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case (&expr_call.attrs).iter().enumerate().find_map(|(k, attr)| { - let Some(segment) = attr.path.segments.last() else { return None; }; + let segment = attr.path.segments.last()?; let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; let mut expr_call = expr_call.clone(); From 675f2e80e5ff31cc1d8c941e442130dcb7f1f8c3 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 12 Jan 2023 21:45:35 -0500 Subject: [PATCH 113/146] rename generics => type_use_generics rename full_generics => type_impl_generics --- frame/support/procedural/src/benchmark.rs | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index e0751ab4480f4..b69d4a3e3d3be 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -289,11 +289,11 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To } // generics - let generics = match instance { + let type_use_generics = match instance { false => quote!(T), true => quote!(T, I), }; - let full_generics = match instance { + let type_impl_generics = match instance { false => quote!(T: Config), true => quote!(T: Config, I: 'static), }; @@ -336,12 +336,12 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To * } - impl<#full_generics> #krate::BenchmarkingSetup<#generics> for SelectedBenchmark where #where_clause { + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { match self { #( Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup<#generics>>::components(&#benchmark_names) + <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) } ) * @@ -360,7 +360,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To #( Self::#benchmark_names => { <#benchmark_names as #krate::BenchmarkingSetup< - #generics + #type_use_generics >>::instance(&#benchmark_names, components, verify) } ) @@ -369,7 +369,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To } } #[cfg(any(feature = "runtime-benchmarks", test))] - impl<#full_generics> #krate::Benchmarking for Pallet<#generics> + impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> where T: frame_system::Config, #where_clause { fn benchmarks( @@ -392,7 +392,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To *, _ => panic!("all benchmarks should be selectable") }; - let components = >::components(&selected_benchmark); + let components = >::components(&selected_benchmark); #krate::BenchmarkMetadata { name: benchmark.as_bytes().to_vec(), components, @@ -435,7 +435,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To // Set up the externalities environment for the setup we want to // benchmark. let closure_to_benchmark = < - SelectedBenchmark as #krate::BenchmarkingSetup<#generics> + SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> >::instance(&selected_benchmark, c, verify)?; // Set the block number to at least 1 so events are deposited. @@ -516,7 +516,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To } #[cfg(test)] - impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config, #where_clause { + impl<#type_impl_generics> Pallet<#type_use_generics> 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 @@ -606,12 +606,12 @@ fn expand_benchmark( let param_ranges = unrolled.param_ranges; let param_types = unrolled.param_types; - let generics = match is_instance { + let type_use_generics = match is_instance { false => quote!(T), true => quote!(T, I), }; - let full_generics = match is_instance { + let type_impl_generics = match is_instance { false => quote!(T: Config), true => quote!(T: Config, I: 'static), }; @@ -664,15 +664,15 @@ fn expand_benchmark( ( // (pre_call, post_call): quote! { - let __call = Call::<#generics>::#expr_call; + let __call = Call::<#type_use_generics>::#expr_call; let __benchmarked_call_encoded = #codec::Encode::encode(&__call); }, quote! { - let __call_decoded = as #codec::Decode> + let __call_decoded = as #codec::Decode> ::decode(&mut &__benchmarked_call_encoded[..]) .expect("call is encoded above, encoding must be correct"); let __origin = #origin.into(); - as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( __call_decoded, __origin, )?; @@ -693,7 +693,7 @@ fn expand_benchmark( struct #name; #[allow(unused_variables)] - impl<#full_generics> #krate::BenchmarkingSetup<#generics> + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for #name where #where_clause { fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { #krate::vec! [ @@ -734,7 +734,7 @@ fn expand_benchmark( } #[cfg(test)] - impl<#full_generics> Pallet<#generics> where T: ::frame_system::Config, #where_clause { + impl<#type_impl_generics> Pallet<#type_use_generics> where T: ::frame_system::Config, #where_clause { #[allow(unused)] fn #test_ident() -> Result<(), #krate::BenchmarkError> { let selected_benchmark = SelectedBenchmark::#name; From 2171d271395de0533dd078921f0be6be47246b14 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 00:11:03 -0500 Subject: [PATCH 114/146] simplify extrinsic call extraction with transpose --- frame/support/procedural/src/benchmark.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index b69d4a3e3d3be..5ee47ed5d89fc 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -166,7 +166,7 @@ impl BenchmarkDef { } // #[extrinsic_call] / #[block] handling - let (i, call_def) = match item_fn.block.stmts.iter().enumerate().find_map(|(i, child)| { + let maybe_call_def = item_fn.block.stmts.iter().enumerate().find_map(|(i, child)| { match child { Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case (&expr_call.attrs).iter().enumerate().find_map(|(k, attr)| { @@ -200,15 +200,12 @@ impl BenchmarkDef { }, _ => None } - }) { - Some(Ok((i, call_def))) => (i, call_def), - Some(Err(err)) => return Err(err), - _ => { - return Err(Error::new( - item_fn.block.brace_token.span, - "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body.", - )) - } + }); + let Some((i, call_def)) = maybe_call_def.transpose()? else { + return Err(Error::new( + item_fn.block.brace_token.span, + "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body.", + )) }; Ok(BenchmarkDef { From 37216c94da05e8da5b5f06fa6fc1b37f7a4b7665 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 00:17:53 -0500 Subject: [PATCH 115/146] bump CI From 14ef35fc3d5f0a30813069fad0d2c6c3dee8b520 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 00:32:50 -0500 Subject: [PATCH 116/146] nit --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 5ee47ed5d89fc..e71b88fa88720 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -169,7 +169,7 @@ impl BenchmarkDef { let maybe_call_def = item_fn.block.stmts.iter().enumerate().find_map(|(i, child)| { match child { Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case - (&expr_call.attrs).iter().enumerate().find_map(|(k, attr)| { + expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { let segment = attr.path.segments.last()?; let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; let mut expr_call = expr_call.clone(); From ee570d33ff08840877311ef944cbb6620e9a5a07 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 02:14:04 -0500 Subject: [PATCH 117/146] proper handling of too many + too few block/extrinsic call annotations --- frame/support/procedural/src/benchmark.rs | 39 ++++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index e71b88fa88720..d4116d862d9b7 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -124,8 +124,18 @@ impl syn::parse::Parse for BenchmarkAttrs { /// Represents the parsed extrinsic call for a benchmark #[derive(Clone)] enum BenchmarkCallDef { - ExtrinsicCall { origin: Expr, expr_call: ExprCall }, // #[extrinsic_call] - Block(ExprBlock), // #[block] + ExtrinsicCall { origin: Expr, expr_call: ExprCall, attr_span: Span }, // #[extrinsic_call] + Block { block: ExprBlock, attr_span: Span }, // #[block] +} + +impl BenchmarkCallDef { + /// Returns the `span()` for attribute + fn attr_span(&self) -> Span { + match self { + BenchmarkCallDef::ExtrinsicCall { origin: _, expr_call: _, attr_span } => *attr_span, + BenchmarkCallDef::Block { block: _, attr_span } => *attr_span, + } + } } /// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. @@ -166,7 +176,7 @@ impl BenchmarkDef { } // #[extrinsic_call] / #[block] handling - let maybe_call_def = item_fn.block.stmts.iter().enumerate().find_map(|(i, child)| { + let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { match child { Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { @@ -183,7 +193,7 @@ impl BenchmarkDef { None => return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))), }; - Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call }))) + Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) }) }, Stmt::Expr(Expr::Block(block)) => { // #[block] case @@ -195,17 +205,22 @@ impl BenchmarkDef { // consume #[block] tokens block.attrs.remove(k); - Some(Ok((i, BenchmarkCallDef::Block(block)))) + Some(Ok((i, BenchmarkCallDef::Block { block, attr_span: attr.span() }))) }) }, _ => None } - }); - let Some((i, call_def)) = maybe_call_def.transpose()? else { - return Err(Error::new( + }).collect::>>()?; + let (i, call_def) = match &call_defs[..] { + [(i, call_def)] => (*i, call_def.clone()), // = 1 + [] => return Err(Error::new( // = 0 item_fn.block.brace_token.span, - "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body.", - )) + "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." + )), + _ => return Err(Error::new( // > 1 + call_defs[1].1.attr_span(), + "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark." + )), }; Ok(BenchmarkDef { @@ -614,7 +629,7 @@ fn expand_benchmark( }; let (pre_call, post_call) = match benchmark_def.call_def { - BenchmarkCallDef::ExtrinsicCall { origin, expr_call } => { + BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => { let mut expr_call = expr_call.clone(); // remove first arg from expr_call @@ -676,7 +691,7 @@ fn expand_benchmark( }, ) }, - BenchmarkCallDef::Block(block) => (quote!(), quote!(#block)), + BenchmarkCallDef::Block { block, attr_span: _ } => (quote!(), quote!(#block)), }; // generate final quoted tokens From 9660e5ed084f38d821c82c1e3408bb4a3452fc62 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 09:22:00 -0500 Subject: [PATCH 118/146] change to B >= A Co-authored-by: Oliver Tale-Yazdi --- frame/support/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index a4ff17d73f7fb..40bc878cff365 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2817,7 +2817,7 @@ pub mod pallet_macros { /// [`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`. +/// 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 From 1d559fbd02b5b5f3ffb468fc91c46880b521ba9e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 09:23:16 -0500 Subject: [PATCH 119/146] remove unneeded ignore Co-authored-by: Oliver Tale-Yazdi --- frame/support/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 40bc878cff365..46f20d20ffd0f 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2914,7 +2914,7 @@ pub mod pallet_macros { /// 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::*; From d261bc536ff8569cf028a547344d252c4ffc1326 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 09:24:06 -0500 Subject: [PATCH 120/146] remove another ignore Co-authored-by: Oliver Tale-Yazdi --- frame/support/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 46f20d20ffd0f..5bc3f6d5ed88d 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2763,7 +2763,7 @@ pub mod pallet_macros { /// /// ## General Syntax /// -/// ```ignore +/// ``` /// #![cfg(feature = "runtime-benchmarks")] /// /// use super::{mock_helpers::*, Pallet as MyPallet}; From 88c8ada11027a3473095f181df1f81aa7cef2f0d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 13:43:16 -0500 Subject: [PATCH 121/146] add ui tests --- Cargo.lock | 1 + frame/support/test/Cargo.toml | 2 ++ frame/support/test/tests/benchmark_ui.rs | 36 +++++++++++++++++++ .../test/tests/benchmark_ui/bad_param_name.rs | 18 ++++++++++ .../tests/benchmark_ui/bad_param_name.stderr | 7 ++++ .../test/tests/benchmark_ui/bad_params.rs | 18 ++++++++++ .../test/tests/benchmark_ui/bad_params.stderr | 5 +++ .../test/tests/benchmark_ui/dup_block.rs | 20 +++++++++++ .../test/tests/benchmark_ui/dup_block.stderr | 5 +++ .../tests/benchmark_ui/dup_extrinsic_call.rs | 20 +++++++++++ .../benchmark_ui/dup_extrinsic_call.stderr | 5 +++ .../test/tests/benchmark_ui/extra_extra.rs | 16 +++++++++ .../tests/benchmark_ui/extra_extra.stderr | 5 +++ .../tests/benchmark_ui/extra_skip_meta.rs | 16 +++++++++ .../tests/benchmark_ui/extra_skip_meta.stderr | 5 +++ .../benchmark_ui/extrinsic_call_out_of_fn.rs | 6 ++++ .../extrinsic_call_out_of_fn.stderr | 15 ++++++++ .../test/tests/benchmark_ui/missing_call.rs | 13 +++++++ .../tests/benchmark_ui/missing_call.stderr | 5 +++ .../test/tests/benchmark_ui/missing_origin.rs | 16 +++++++++ .../tests/benchmark_ui/missing_origin.stderr | 7 ++++ .../tests/benchmark_ui/pass/valid_basic.rs | 17 +++++++++ .../tests/benchmark_ui/unrecognized_option.rs | 16 +++++++++ .../benchmark_ui/unrecognized_option.stderr | 5 +++ 24 files changed, 279 insertions(+) create mode 100644 frame/support/test/tests/benchmark_ui.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_params.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_params.stderr create mode 100644 frame/support/test/tests/benchmark_ui/dup_block.rs create mode 100644 frame/support/test/tests/benchmark_ui/dup_block.stderr create mode 100644 frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs create mode 100644 frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr create mode 100644 frame/support/test/tests/benchmark_ui/extra_extra.rs create mode 100644 frame/support/test/tests/benchmark_ui/extra_extra.stderr create mode 100644 frame/support/test/tests/benchmark_ui/extra_skip_meta.rs create mode 100644 frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr create mode 100644 frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs create mode 100644 frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr create mode 100644 frame/support/test/tests/benchmark_ui/missing_call.rs create mode 100644 frame/support/test/tests/benchmark_ui/missing_call.stderr create mode 100644 frame/support/test/tests/benchmark_ui/missing_origin.rs create mode 100644 frame/support/test/tests/benchmark_ui/missing_origin.stderr create mode 100644 frame/support/test/tests/benchmark_ui/pass/valid_basic.rs create mode 100644 frame/support/test/tests/benchmark_ui/unrecognized_option.rs create mode 100644 frame/support/test/tests/benchmark_ui/unrecognized_option.stderr diff --git a/Cargo.lock b/Cargo.lock index bbf7f63aef1b6..6c0c5cc602b97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2367,6 +2367,7 @@ dependencies = [ name = "frame-support-test" version = "3.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-support-test-pallet", "frame-system", diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index 7fe1bcb5674bb..ccf0143df5884 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -19,6 +19,7 @@ sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../../ sp-io = { version = "7.0.0", path = "../../../primitives/io", default-features = false } sp-state-machine = { version = "0.13.0", optional = true, path = "../../../primitives/state-machine" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../" } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } @@ -36,6 +37,7 @@ std = [ "serde/std", "codec/std", "scale-info/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "sp-core/std", diff --git a/frame/support/test/tests/benchmark_ui.rs b/frame/support/test/tests/benchmark_ui.rs new file mode 100644 index 0000000000000..243030bd07fcd --- /dev/null +++ b/frame/support/test/tests/benchmark_ui.rs @@ -0,0 +1,36 @@ +// 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. + +#[rustversion::attr(not(stable), ignore)] +#[cfg(not(feature = "disable-ui-tests"))] +#[test] +fn benchmark_ui() { + // Only run the ui tests when `RUN_UI_TESTS` is set. + if std::env::var("RUN_UI_TESTS").is_err() { + return + } + + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("SKIP_WASM_BUILD", "1"); + + // Deny all warnings since we emit warnings as part of a Pallet's UI. + std::env::set_var("RUSTFLAGS", "--deny warnings"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/benchmark_ui/*.rs"); + t.pass("tests/benchmark_ui/pass/*.rs"); +} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.rs b/frame/support/test/tests/benchmark_ui/bad_param_name.rs new file mode 100644 index 0000000000000..ad4db4f0a4ba4 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.rs @@ -0,0 +1,18 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(winton: Linear<1, 2>) { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name.stderr new file mode 100644 index 0000000000000..10eb44a0fa84a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.stderr @@ -0,0 +1,7 @@ +error[E0599]: no variant or associated item named `winton` found for enum `BenchmarkParameter` in the current scope + --> tests/benchmark_ui/bad_param_name.rs:5:1 + | +5 | #[benchmarks] + | ^^^^^^^^^^^^^ variant or associated item not found in `BenchmarkParameter` + | + = note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/benchmark_ui/bad_params.rs b/frame/support/test/tests/benchmark_ui/bad_params.rs new file mode 100644 index 0000000000000..a0c9236982c62 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_params.rs @@ -0,0 +1,18 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(y: Linear<1, 2>, x: u32) { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_params.stderr b/frame/support/test/tests/benchmark_ui/bad_params.stderr new file mode 100644 index 0000000000000..db899e0bb2e97 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_params.stderr @@ -0,0 +1,5 @@ +error: Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`. + --> tests/benchmark_ui/bad_params.rs:10:28 + | +10 | fn bench(y: Linear<1, 2>, x: u32) { + | ^ diff --git a/frame/support/test/tests/benchmark_ui/dup_block.rs b/frame/support/test/tests/benchmark_ui/dup_block.rs new file mode 100644 index 0000000000000..4c4a8fc11d30a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_block.rs @@ -0,0 +1,20 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + let a = 2 + 2; + #[block] + {} + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/dup_block.stderr b/frame/support/test/tests/benchmark_ui/dup_block.stderr new file mode 100644 index 0000000000000..3d73c3d6609b1 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_block.stderr @@ -0,0 +1,5 @@ +error: Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark. + --> tests/benchmark_ui/dup_block.rs:14:3 + | +14 | #[block] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs new file mode 100644 index 0000000000000..1a91b7c16d6a5 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs @@ -0,0 +1,20 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + let a = 2 + 2; + #[extrinsic_call] + _(stuff); + #[extrinsic_call] + _(other_stuff); + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr new file mode 100644 index 0000000000000..593f7072bfa51 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr @@ -0,0 +1,5 @@ +error: Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark. + --> tests/benchmark_ui/dup_extrinsic_call.rs:14:3 + | +14 | #[extrinsic_call] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/extra_extra.rs b/frame/support/test/tests/benchmark_ui/extra_extra.rs new file mode 100644 index 0000000000000..021106c7afc85 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_extra.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(extra, extra)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extra_extra.stderr b/frame/support/test/tests/benchmark_ui/extra_extra.stderr new file mode 100644 index 0000000000000..bf36b4f08054a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_extra.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, `extra` can only be specified once + --> tests/benchmark_ui/extra_extra.rs:9:26 + | +9 | #[benchmark(extra, extra)] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs new file mode 100644 index 0000000000000..1940f4cf1f040 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, skip_meta)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr b/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr new file mode 100644 index 0000000000000..4d48a8ad77a45 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, `skip_meta` can only be specified once + --> tests/benchmark_ui/extra_skip_meta.rs:9:34 + | +9 | #[benchmark(skip_meta, skip_meta)] + | ^ 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 new file mode 100644 index 0000000000000..4cb6bfc34c58e --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs @@ -0,0 +1,6 @@ +use frame_support::benchmarking::*; + +#[extrinsic_call] +mod stuff {} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr new file mode 100644 index 0000000000000..f831c50c0ca3c --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr @@ -0,0 +1,15 @@ +error: macros that expand to items must be delimited with braces or followed by a semicolon + --> tests/benchmark_ui/extrinsic_call_out_of_fn.rs:3:1 + | +3 | #[extrinsic_call] + | ^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `extrinsic_call` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[extrinsic_call]` must be in a benchmark function definition labeled with `#[benchmark]`. + --> tests/benchmark_ui/extrinsic_call_out_of_fn.rs:3:1 + | +3 | #[extrinsic_call] + | ^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `extrinsic_call` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/benchmark_ui/missing_call.rs b/frame/support/test/tests/benchmark_ui/missing_call.rs new file mode 100644 index 0000000000000..74370493542c4 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_call.rs @@ -0,0 +1,13 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() {} +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/missing_call.stderr b/frame/support/test/tests/benchmark_ui/missing_call.stderr new file mode 100644 index 0000000000000..3b55f77d42562 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_call.stderr @@ -0,0 +1,5 @@ +error: No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body. + --> tests/benchmark_ui/missing_call.rs:10:13 + | +10 | fn bench() {} + | ^^ diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.rs b/frame/support/test/tests/benchmark_ui/missing_origin.rs new file mode 100644 index 0000000000000..aad91bc79f825 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_origin.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + #[extrinsic_call] + thing(); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.stderr b/frame/support/test/tests/benchmark_ui/missing_origin.stderr new file mode 100644 index 0000000000000..ae6bc514b504d --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_origin.stderr @@ -0,0 +1,7 @@ +error: Single-item extrinsic calls must specify their origin as the first argument. + --> tests/benchmark_ui/missing_origin.rs:5:1 + | +5 | #[benchmarks] + | ^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs new file mode 100644 index 0000000000000..5c84d7f76d874 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs @@ -0,0 +1,17 @@ +use frame_support::benchmarking::*; +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, extra)] + fn bench() { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/unrecognized_option.rs b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs new file mode 100644 index 0000000000000..4c2cea139f80c --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, extra, bad)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr b/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr new file mode 100644 index 0000000000000..5cebe9eab05e9 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr @@ -0,0 +1,5 @@ +error: expected `extra` or `skip_meta` + --> tests/benchmark_ui/unrecognized_option.rs:9:32 + | +9 | #[benchmark(skip_meta, extra, bad)] + | ^^^ From 93613eeaba56dc20711484c519a44818eb4cee0e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 14:15:35 -0500 Subject: [PATCH 122/146] use _() style extrinsic call on accumulate_dummy Co-authored-by: Oliver Tale-Yazdi --- frame/examples/basic/src/benchmarking.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/examples/basic/src/benchmarking.rs b/frame/examples/basic/src/benchmarking.rs index 7fa757d13b2a3..699e56685fbe9 100644 --- a/frame/examples/basic/src/benchmarking.rs +++ b/frame/examples/basic/src/benchmarking.rs @@ -64,8 +64,7 @@ mod benchmarks { // You can use `_` if the name of the Call matches the benchmark name. #[extrinsic_call] - // TODO use `_` - accumulate_dummy(RawOrigin::Signed(caller), value); + _(RawOrigin::Signed(caller), value); } /// You can write helper functions in here since its a normal Rust module. From 287168643092954dd1b5058f3936f35e04ce955b Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 14:23:46 -0500 Subject: [PATCH 123/146] add range check to ParamRange --- frame/support/procedural/src/benchmark.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index d4116d862d9b7..29d46865ef9b4 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -172,6 +172,13 @@ impl BenchmarkDef { let Ok(start) = args.start.base10_parse::() else { return invalid_param() }; let Ok(end) = args.end.base10_parse::() else { return invalid_param() }; + if end < start { + return Err(Error::new( + span, + "The start of a `ParamRange` must be less than or equal to the end", + )) + } + params.push(ParamDef { name, typ: typ.clone(), start, end }); } From 16d3581c174b422c15f03435cb44df47f8152667 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 14:28:59 -0500 Subject: [PATCH 124/146] ui test for bad param ranges --- .../test/tests/benchmark_ui/bad_param_range.rs | 16 ++++++++++++++++ .../tests/benchmark_ui/bad_param_range.stderr | 5 +++++ 2 files changed, 21 insertions(+) create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_range.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_range.stderr diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.rs b/frame/support/test/tests/benchmark_ui/bad_param_range.rs new file mode 100644 index 0000000000000..aabb9fa7403a1 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(x: Linear<3, 1>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.stderr b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr new file mode 100644 index 0000000000000..135b868b64ef4 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr @@ -0,0 +1,5 @@ +error: The start of a `ParamRange` must be less than or equal to the end + --> tests/benchmark_ui/bad_param_range.rs:10:11 + | +10 | fn bench(x: Linear<3, 1>) { + | ^ From 68e271446e69e42fd6d1b7bc69cd571e4dc4fe60 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 15:10:06 -0500 Subject: [PATCH 125/146] fix failing example --- frame/support/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 5bc3f6d5ed88d..a6b47410bc390 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2914,7 +2914,7 @@ pub mod pallet_macros { /// 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::*; From 29308ac194cb69e6e24d7b4592be54b88a86e6a7 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 15:36:35 -0500 Subject: [PATCH 126/146] add ignore back to other failing example --- frame/support/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index a6b47410bc390..40bc878cff365 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2763,7 +2763,7 @@ pub mod pallet_macros { /// /// ## General Syntax /// -/// ``` +/// ```ignore /// #![cfg(feature = "runtime-benchmarks")] /// /// use super::{mock_helpers::*, Pallet as MyPallet}; From be0439bf09081e96a34dab5e11eabf91c8fe304b Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 19:30:55 -0500 Subject: [PATCH 127/146] tweak expr_call span Co-authored-by: Keith Yeung --- frame/support/procedural/src/benchmark.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 29d46865ef9b4..1c9c0ce48db47 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -195,7 +195,9 @@ impl BenchmarkDef { expr_call.attrs.remove(k); // extract origin from expr_call - let origin = match expr_call.args.first() { + let Some(origin) = expr_call.args.cloned().first() else { + return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))) + }; Some(arg) => arg.clone(), None => return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))), }; From 9d0e9196295cdf66c5fae15171285f6ee42eb582 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 13 Jan 2023 21:00:29 -0500 Subject: [PATCH 128/146] fix typo --- frame/support/procedural/src/benchmark.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 1c9c0ce48db47..23097597957e1 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -195,11 +195,8 @@ impl BenchmarkDef { expr_call.attrs.remove(k); // extract origin from expr_call - let Some(origin) = expr_call.args.cloned().first() else { - return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))) - }; - Some(arg) => arg.clone(), - None => return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))), + let Some(origin) = expr_call.args.first().cloned() else { + return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))) }; Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) From 232d4ca3222632081860f03bdd9bd60f66057cd4 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 15 Jan 2023 23:42:29 -0500 Subject: [PATCH 129/146] eliminate a match Co-authored-by: Keith Yeung --- frame/support/procedural/src/benchmark.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 23097597957e1..c3c22e511312a 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -275,10 +275,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To func.attrs.remove(i); // parse benchmark def - let benchmark_def = match BenchmarkDef::from(&func, args.extra, args.skip_meta) { - Ok(def) => def, - Err(err) => return err.to_compile_error().into(), - }; + let benchmark_def = BenchmarkDef::from(&func, args.extra, args.skip_meta).map_err(syn::parse::Error::to_compile_error)?; // expand benchmark let expanded = expand_benchmark( From 186ce019b9219d05e49c82bb97af077580b9438d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 16 Jan 2023 00:44:42 -0500 Subject: [PATCH 130/146] change pub fn benchmarks to return Result --- frame/support/procedural/src/benchmark.rs | 29 ++++++++++------------- frame/support/procedural/src/lib.rs | 10 ++++++-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index c3c22e511312a..a8ca78eb3af2a 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -25,7 +25,6 @@ use quote::{quote, quote_spanned, ToTokens}; use syn::{ parenthesized, parse::{Nothing, ParseStream}, - parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::{Colon2, Comma, Gt, Lt, Paren}, @@ -241,12 +240,16 @@ impl BenchmarkDef { } /// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation -pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> TokenStream { - let module = parse_macro_input!(tokens as ItemMod); +pub fn benchmarks( + attrs: TokenStream, + tokens: TokenStream, + instance: bool, +) -> syn::Result { + let module: ItemMod = syn::parse(tokens)?; let mod_span = module.span(); let where_clause = match syn::parse::(attrs.clone()) { Ok(_) => quote!(), - Err(_) => parse_macro_input!(attrs as WhereClause).predicates.to_token_stream(), + Err(_) => syn::parse::(attrs)?.predicates.to_token_stream(), }; let mod_vis = module.vis; let mod_name = module.ident; @@ -255,11 +258,8 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); - let Some((_brace, mut content)) = module.content else { - // this will compile error already because attributes attached to empty modules are unstable - // but including error anyway to make this future-proof - return quote_spanned!(mod_span=> "Module cannot be empty!".to_compile_error()).into() - }; + let (_brace, mut content) = + module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; for stmt in &mut content { let mut push_stmt = || { expanded_stmts.push(stmt.to_token_stream()); @@ -269,13 +269,13 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To let Some(seg) = attr.path.segments.last() else { push_stmt(); continue; }; let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) else { push_stmt(); continue; }; let tokens = attr.tokens.to_token_stream().into(); - let args = parse_macro_input!(tokens as BenchmarkAttrs); + let args: BenchmarkAttrs = syn::parse(tokens)?; // consume #[benchmark] attr func.attrs.remove(i); // parse benchmark def - let benchmark_def = BenchmarkDef::from(&func, args.extra, args.skip_meta).map_err(syn::parse::Error::to_compile_error)?; + let benchmark_def = BenchmarkDef::from(&func, args.extra, args.skip_meta)?; // expand benchmark let expanded = expand_benchmark( @@ -313,10 +313,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To true => quote!(T: Config, I: 'static), }; - let krate = match generate_crate_access_2018("frame-benchmarking") { - Ok(ident) => ident, - Err(err) => return err.to_compile_error().into(), - }; + let krate = generate_crate_access_2018("frame-benchmarking")?; let support = quote!(#krate::frame_support); // benchmark name variables @@ -555,7 +552,7 @@ pub fn benchmarks(attrs: TokenStream, tokens: TokenStream, instance: bool) -> To } #mod_vis use #mod_name::*; }; - res.into() + Ok(res.into()) } /// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 6427a61b7b298..badbbed1f7379 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -486,7 +486,10 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { /// See `frame_support::benchmarking` for more info. #[proc_macro_attribute] pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { - benchmark::benchmarks(attr, tokens, false) + match benchmark::benchmarks(attr, tokens, false) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error().into(), + } } /// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will @@ -495,7 +498,10 @@ pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { /// See `frame_support::benchmarking` for more info. #[proc_macro_attribute] pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { - benchmark::benchmarks(attr, tokens, true) + match benchmark::benchmarks(attr, tokens, true) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error().into(), + } } /// An attribute macro used to declare a benchmark within a benchmarking module. Must be From b21ac54510442723eec1d40cadd35294882e9892 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 16 Jan 2023 10:46:33 -0500 Subject: [PATCH 131/146] fix origin error span --- frame/support/procedural/src/benchmark.rs | 3 ++- .../test/tests/benchmark_ui/missing_origin.stderr | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index a8ca78eb3af2a..6045de75976ef 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -188,6 +188,7 @@ impl BenchmarkDef { expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { let segment = attr.path.segments.last()?; let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let span = expr_call.span(); let mut expr_call = expr_call.clone(); // consume #[extrinsic_call] tokens @@ -195,7 +196,7 @@ impl BenchmarkDef { // extract origin from expr_call let Some(origin) = expr_call.args.first().cloned() else { - return Some(Err(Error::new(expr_call.args.span(), "Single-item extrinsic calls must specify their origin as the first argument."))) + return Some(Err(Error::new(span, "Single-item extrinsic calls must specify their origin as the first argument."))) }; Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.stderr b/frame/support/test/tests/benchmark_ui/missing_origin.stderr index ae6bc514b504d..ed0ac6c0e42c8 100644 --- a/frame/support/test/tests/benchmark_ui/missing_origin.stderr +++ b/frame/support/test/tests/benchmark_ui/missing_origin.stderr @@ -1,7 +1,5 @@ error: Single-item extrinsic calls must specify their origin as the first argument. - --> tests/benchmark_ui/missing_origin.rs:5:1 - | -5 | #[benchmarks] - | ^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info) + --> tests/benchmark_ui/missing_origin.rs:11:3 + | +11 | #[extrinsic_call] + | ^ From 7fa5fe1f5ca15b2ac11d0bbc87d9502bbcdde8e0 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 16 Jan 2023 12:57:07 -0500 Subject: [PATCH 132/146] more informative error for invalid benchmark parameter name --- frame/support/procedural/src/benchmark.rs | 18 ++++++++++++++++++ .../tests/benchmark_ui/bad_param_name.stderr | 12 +++++------- .../benchmark_ui/bad_param_name_too_long.rs | 14 ++++++++++++++ .../bad_param_name_too_long.stderr | 5 +++++ .../benchmark_ui/bad_param_name_upper_case.rs | 14 ++++++++++++++ .../bad_param_name_upper_case.stderr | 5 +++++ 6 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs create mode 100644 frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 6045de75976ef..0e006a927a955 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -162,7 +162,25 @@ impl BenchmarkDef { let FnArg::Typed(arg) = arg else { return invalid_param() }; let Pat::Ident(ident) = &*arg.pat else { return invalid_param() }; + + // check param name + let var_span = ident.span(); + let invalid_param_name = || { + return Err(Error::new( + var_span, + "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", + )) + }; let name = ident.ident.to_token_stream().to_string(); + if name.len() > 1 { + return invalid_param_name() + }; + let Some(name_char) = name.chars().next() else { return invalid_param_name() }; + if !name_char.is_alphabetic() || !name_char.is_lowercase() { + return invalid_param_name() + } + + // parse type let typ = &*arg.ty; let Type::Path(tpath) = typ else { return invalid_param() }; let Some(segment) = tpath.path.segments.last() else { return invalid_param() }; diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name.stderr index 10eb44a0fa84a..4e2d63a6b5030 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_name.stderr +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.stderr @@ -1,7 +1,5 @@ -error[E0599]: no variant or associated item named `winton` found for enum `BenchmarkParameter` in the current scope - --> tests/benchmark_ui/bad_param_name.rs:5:1 - | -5 | #[benchmarks] - | ^^^^^^^^^^^^^ variant or associated item not found in `BenchmarkParameter` - | - = note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info) +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name.rs:10:11 + | +10 | fn bench(winton: Linear<1, 2>) { + | ^^^^^^ 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 new file mode 100644 index 0000000000000..50e4dc6fd4609 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs @@ -0,0 +1,14 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + #[benchmark] + fn bench(xx: Linear<1, 2>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr new file mode 100644 index 0000000000000..32f6bf8e47d09 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr @@ -0,0 +1,5 @@ +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name_too_long.rs:8:11 + | +8 | fn bench(xx: Linear<1, 2>) { + | ^^ 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 new file mode 100644 index 0000000000000..0270582b3b85b --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs @@ -0,0 +1,14 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + #[benchmark] + fn bench(D: Linear<1, 2>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr new file mode 100644 index 0000000000000..48dd41d3262d7 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr @@ -0,0 +1,5 @@ +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name_upper_case.rs:8:11 + | +8 | fn bench(D: Linear<1, 2>) { + | ^ From 014d527a03fb30e7c43d2290f8457de7387c5b11 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 16 Jan 2023 13:15:20 -0500 Subject: [PATCH 133/146] fix spans on a few benchmark errors --- frame/support/procedural/src/benchmark.rs | 19 +++++++++---------- .../tests/benchmark_ui/bad_param_range.stderr | 4 ++-- .../test/tests/benchmark_ui/bad_params.stderr | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 0e006a927a955..cee8821d54b95 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -155,13 +155,12 @@ impl BenchmarkDef { // parse params such as "x: Linear<0, 1>" for arg in &item_fn.sig.inputs { - let span = arg.span(); - let invalid_param = || { + let invalid_param = |span| { return Err(Error::new(span, "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", )) }; - let FnArg::Typed(arg) = arg else { return invalid_param() }; - let Pat::Ident(ident) = &*arg.pat else { return invalid_param() }; + let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; + let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; // check param name let var_span = ident.span(); @@ -182,16 +181,16 @@ impl BenchmarkDef { // parse type let typ = &*arg.ty; - let Type::Path(tpath) = typ else { return invalid_param() }; - let Some(segment) = tpath.path.segments.last() else { return invalid_param() }; + let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; + let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; let args = segment.arguments.to_token_stream().into(); - let Ok(args) = syn::parse::(args) else { return invalid_param() }; - let Ok(start) = args.start.base10_parse::() else { return invalid_param() }; - let Ok(end) = args.end.base10_parse::() else { return invalid_param() }; + let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; + let Ok(start) = args.start.base10_parse::() else { return invalid_param(args.start.span()) }; + let Ok(end) = args.end.base10_parse::() else { return invalid_param(args.end.span()) }; if end < start { return Err(Error::new( - span, + args.start.span(), "The start of a `ParamRange` must be less than or equal to the end", )) } diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.stderr b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr index 135b868b64ef4..1347af0a07b8e 100644 --- a/frame/support/test/tests/benchmark_ui/bad_param_range.stderr +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr @@ -1,5 +1,5 @@ error: The start of a `ParamRange` must be less than or equal to the end - --> tests/benchmark_ui/bad_param_range.rs:10:11 + --> tests/benchmark_ui/bad_param_range.rs:10:21 | 10 | fn bench(x: Linear<3, 1>) { - | ^ + | ^ diff --git a/frame/support/test/tests/benchmark_ui/bad_params.stderr b/frame/support/test/tests/benchmark_ui/bad_params.stderr index db899e0bb2e97..068eaedd531b9 100644 --- a/frame/support/test/tests/benchmark_ui/bad_params.stderr +++ b/frame/support/test/tests/benchmark_ui/bad_params.stderr @@ -1,5 +1,5 @@ error: Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`. - --> tests/benchmark_ui/bad_params.rs:10:28 + --> tests/benchmark_ui/bad_params.rs:10:31 | 10 | fn bench(y: Linear<1, 2>, x: u32) { - | ^ + | ^^^ From b3f1ab074cec9a95a31da5f953001e05e2660c11 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 16 Jan 2023 13:25:14 -0500 Subject: [PATCH 134/146] remove unneeded clone --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index cee8821d54b95..6ff30905348c5 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -283,7 +283,7 @@ pub fn benchmarks( expanded_stmts.push(stmt.to_token_stream()); }; let Item::Fn(mut func) = stmt.clone() else { push_stmt(); continue; }; - for (i, attr) in (&func.attrs.clone()).iter().enumerate() { + for (i, attr) in func.attrs.iter().enumerate() { let Some(seg) = attr.path.segments.last() else { push_stmt(); continue; }; let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) else { push_stmt(); continue; }; let tokens = attr.tokens.to_token_stream().into(); From b9472f285974f4ab03403e1469701fd98a55cc57 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 16 Jan 2023 15:10:03 -0500 Subject: [PATCH 135/146] refactor inner loop of benchmark function parsing --- frame/support/procedural/src/benchmark.rs | 75 +++++++++++++---------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 6ff30905348c5..8dbefd41ade26 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -282,43 +282,50 @@ pub fn benchmarks( let mut push_stmt = || { expanded_stmts.push(stmt.to_token_stream()); }; + + // parse as a function def first let Item::Fn(mut func) = stmt.clone() else { push_stmt(); continue; }; - for (i, attr) in func.attrs.iter().enumerate() { - let Some(seg) = attr.path.segments.last() else { push_stmt(); continue; }; - let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) else { push_stmt(); continue; }; - let tokens = attr.tokens.to_token_stream().into(); - let args: BenchmarkAttrs = syn::parse(tokens)?; - - // consume #[benchmark] attr - func.attrs.remove(i); - - // parse benchmark def - let benchmark_def = BenchmarkDef::from(&func, args.extra, args.skip_meta)?; - - // expand benchmark - let expanded = expand_benchmark( - benchmark_def.clone(), - &func.sig.ident, - instance, - where_clause.clone(), - ); - - // record benchmark name - let name = func.sig.ident; - - // process name vecs - benchmark_names.push(name.clone()); - if benchmark_def.extra { - extra_benchmark_names.push(name.clone()); - } - if benchmark_def.skip_meta { - skip_meta_benchmark_names.push(name.clone()) - } - expanded_stmts.push(expanded); - benchmark_defs.push(benchmark_def); - break + // find #[benchmark] attribute on function def + let Some((attr_index, benchmark_attr)) = func.attrs.iter().enumerate().find_map(|(i, attr)| { + let Some(seg) = attr.path.segments.last() else { return None }; + let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) else { return None }; + Some((i, attr)) + }) else { push_stmt(); continue; }; + + // parse any args provided to #[benchmark] + let attr_tokens = benchmark_attr.tokens.to_token_stream().into(); + let benchmark_args: BenchmarkAttrs = syn::parse(attr_tokens)?; + + // consume #[benchmark] attribute + func.attrs.remove(attr_index); + + // parse benchmark def + let benchmark_def = + BenchmarkDef::from(&func, benchmark_args.extra, benchmark_args.skip_meta)?; + + // expand benchmark + let expanded = expand_benchmark( + benchmark_def.clone(), + &func.sig.ident, + instance, + where_clause.clone(), + ); + + // record benchmark name + let name = func.sig.ident; + + // process name vecs + benchmark_names.push(name.clone()); + if benchmark_def.extra { + extra_benchmark_names.push(name.clone()); } + if benchmark_def.skip_meta { + skip_meta_benchmark_names.push(name.clone()) + } + + expanded_stmts.push(expanded); + benchmark_defs.push(benchmark_def); } // generics From 0663c887bae89d9ab004efce47e5a5720a8cec38 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 16 Jan 2023 15:53:24 -0500 Subject: [PATCH 136/146] preserve mod attributes --- frame/support/procedural/src/benchmark.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 8dbefd41ade26..0df82810ac541 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -28,14 +28,15 @@ use syn::{ punctuated::Punctuated, spanned::Spanned, token::{Colon2, Comma, Gt, Lt, Paren}, - Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, LitInt, Pat, Path, - PathArguments, PathSegment, Result, Stmt, Token, Type, WhereClause, + Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, LitInt, + Pat, Path, PathArguments, PathSegment, Result, Stmt, Token, Type, WhereClause, }; mod keywords { use syn::custom_keyword; custom_keyword!(benchmark); + custom_keyword!(benchmarks); custom_keyword!(block); custom_keyword!(extra); custom_keyword!(extrinsic_call); @@ -263,6 +264,7 @@ pub fn benchmarks( tokens: TokenStream, instance: bool, ) -> syn::Result { + // gather module info let module: ItemMod = syn::parse(tokens)?; let mod_span = module.span(); let where_clause = match syn::parse::(attrs.clone()) { @@ -271,13 +273,24 @@ pub fn benchmarks( }; let mod_vis = module.vis; let mod_name = module.ident; + + // consume #[benchmarks] attribute by exclusing it from mod_attrs + let mod_attrs: Vec<&Attribute> = module + .attrs + .iter() + .filter(|attr| !syn::parse2::(attr.to_token_stream()).is_ok()) + .collect(); + let mut expanded_stmts: Vec = Vec::new(); let mut benchmark_defs: Vec = Vec::new(); let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); + let (_brace, mut content) = module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; + + // process benchmark defs in module content for stmt in &mut content { let mut push_stmt = || { expanded_stmts.push(stmt.to_token_stream()); @@ -363,6 +376,8 @@ pub fn benchmarks( // emit final quoted tokens let res = quote! { + #(#mod_attrs) + * #mod_vis mod #mod_name { #(#expanded_stmts) * From 7bcb5ce282322e156cf0dc0eefa0267dce08938c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Jan 2023 02:43:48 -0500 Subject: [PATCH 137/146] refactor outer loop of benchmark def parsing code, greatly simplified --- frame/support/procedural/src/benchmark.rs | 38 ++++++++++------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 0df82810ac541..9c5fb71f03844 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -281,8 +281,6 @@ pub fn benchmarks( .filter(|attr| !syn::parse2::(attr.to_token_stream()).is_ok()) .collect(); - let mut expanded_stmts: Vec = Vec::new(); - let mut benchmark_defs: Vec = Vec::new(); let mut benchmark_names: Vec = Vec::new(); let mut extra_benchmark_names: Vec = Vec::new(); let mut skip_meta_benchmark_names: Vec = Vec::new(); @@ -290,29 +288,27 @@ pub fn benchmarks( let (_brace, mut content) = module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; - // process benchmark defs in module content - for stmt in &mut content { - let mut push_stmt = || { - expanded_stmts.push(stmt.to_token_stream()); - }; - + // find all function defs marked with #[benchmark] + let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { // parse as a function def first - let Item::Fn(mut func) = stmt.clone() else { push_stmt(); continue; }; + let Item::Fn(func) = stmt else { return None }; // find #[benchmark] attribute on function def - let Some((attr_index, benchmark_attr)) = func.attrs.iter().enumerate().find_map(|(i, attr)| { + let Some(benchmark_attr) = func.attrs.iter().find_map(|attr| { let Some(seg) = attr.path.segments.last() else { return None }; let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) else { return None }; - Some((i, attr)) - }) else { push_stmt(); continue; }; + Some(attr.clone()) + }) else { return None }; + + Some((benchmark_attr.clone(), func.clone(), stmt)) + }); + // parse individual benchmark defs and args + for (benchmark_attr, func, stmt) in benchmark_fn_metas { // parse any args provided to #[benchmark] let attr_tokens = benchmark_attr.tokens.to_token_stream().into(); let benchmark_args: BenchmarkAttrs = syn::parse(attr_tokens)?; - // consume #[benchmark] attribute - func.attrs.remove(attr_index); - // parse benchmark def let benchmark_def = BenchmarkDef::from(&func, benchmark_args.extra, benchmark_args.skip_meta)?; @@ -326,10 +322,10 @@ pub fn benchmarks( ); // record benchmark name - let name = func.sig.ident; - - // process name vecs + let name = &func.sig.ident; benchmark_names.push(name.clone()); + + // record name sets if benchmark_def.extra { extra_benchmark_names.push(name.clone()); } @@ -337,8 +333,8 @@ pub fn benchmarks( skip_meta_benchmark_names.push(name.clone()) } - expanded_stmts.push(expanded); - benchmark_defs.push(benchmark_def); + // replace original function def with expanded code + *stmt = Item::Verbatim(expanded); } // generics @@ -379,7 +375,7 @@ pub fn benchmarks( #(#mod_attrs) * #mod_vis mod #mod_name { - #(#expanded_stmts) + #(#content) * #[allow(non_camel_case_types)] From 30fc19eb5e4ea57ba2fc44239ed1cdadabb0cf0d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Jan 2023 03:02:16 -0500 Subject: [PATCH 138/146] simplify to use a ? operator when parsing benchmark attr path --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 9c5fb71f03844..2cf4fdc60cef6 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -295,7 +295,7 @@ pub fn benchmarks( // find #[benchmark] attribute on function def let Some(benchmark_attr) = func.attrs.iter().find_map(|attr| { - let Some(seg) = attr.path.segments.last() else { return None }; + let seg = attr.path.segments.last()?; let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) else { return None }; Some(attr.clone()) }) else { return None }; From 4a6520ba34ddc832a6769a74cb8ddf01decab9ed Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Jan 2023 03:10:18 -0500 Subject: [PATCH 139/146] fix another ? operator --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 2cf4fdc60cef6..6ad57485a52e5 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -296,7 +296,7 @@ pub fn benchmarks( // find #[benchmark] attribute on function def let Some(benchmark_attr) = func.attrs.iter().find_map(|attr| { let seg = attr.path.segments.last()?; - let Ok(_) = syn::parse::(seg.ident.to_token_stream().into()) else { return None }; + syn::parse::(seg.ident.to_token_stream().into()).ok()?; Some(attr.clone()) }) else { return None }; From c720703df2430069538be29118f5e83302c6f546 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Jan 2023 03:23:15 -0500 Subject: [PATCH 140/146] further simplify benchmark function attr parsing with more ? ops --- frame/support/procedural/src/benchmark.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 6ad57485a52e5..19c15b02f394e 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -294,11 +294,11 @@ pub fn benchmarks( let Item::Fn(func) = stmt else { return None }; // find #[benchmark] attribute on function def - let Some(benchmark_attr) = func.attrs.iter().find_map(|attr| { + let benchmark_attr = func.attrs.iter().find_map(|attr| { let seg = attr.path.segments.last()?; syn::parse::(seg.ident.to_token_stream().into()).ok()?; - Some(attr.clone()) - }) else { return None }; + Some(attr) + })?; Some((benchmark_attr.clone(), func.clone(), stmt)) }); From 8ecf30c02191e68874848e74314860144ed23f25 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Jan 2023 04:10:16 -0500 Subject: [PATCH 141/146] refactor extrinsic call handling to use if let rather than match --- frame/support/procedural/src/benchmark.rs | 62 +++++++++++------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 19c15b02f394e..2f98db1f99ca2 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -201,38 +201,38 @@ impl BenchmarkDef { // #[extrinsic_call] / #[block] handling let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { - match child { - Stmt::Semi(Expr::Call(expr_call), _semi) => { // #[extrinsic_call] case - expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { - let segment = attr.path.segments.last()?; - let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; - let span = expr_call.span(); - let mut expr_call = expr_call.clone(); - - // consume #[extrinsic_call] tokens - expr_call.attrs.remove(k); - - // extract origin from expr_call - let Some(origin) = expr_call.args.first().cloned() else { - return Some(Err(Error::new(span, "Single-item extrinsic calls must specify their origin as the first argument."))) - }; - - Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) - }) - }, - Stmt::Expr(Expr::Block(block)) => { // #[block] case - block.attrs.iter().enumerate().find_map(|(k, attr)| { - let segment = attr.path.segments.last()?; - let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?; - let mut block = block.clone(); - - // consume #[block] tokens - block.attrs.remove(k); + if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { + // #[extrinsic_call] case + expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { + let segment = attr.path.segments.last()?; + let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let span = expr_call.span(); + let mut expr_call = expr_call.clone(); + + // consume #[extrinsic_call] tokens + expr_call.attrs.remove(k); + + // extract origin from expr_call + let Some(origin) = expr_call.args.first().cloned() else { + return Some(Err(Error::new(span, "Single-item extrinsic calls must specify their origin as the first argument."))) + }; - Some(Ok((i, BenchmarkCallDef::Block { block, attr_span: attr.span() }))) - }) - }, - _ => None + Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) + }) + } else if let Stmt::Expr(Expr::Block(block)) = child { + // #[block] case + block.attrs.iter().enumerate().find_map(|(k, attr)| { + let segment = attr.path.segments.last()?; + let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let mut block = block.clone(); + + // consume #[block] tokens + block.attrs.remove(k); + + Some(Ok((i, BenchmarkCallDef::Block { block, attr_span: attr.span() }))) + }) + } else { + None } }).collect::>>()?; let (i, call_def) = match &call_defs[..] { From bd67fc011de8116bf0b49125f75272019c6aab27 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Jan 2023 04:11:14 -0500 Subject: [PATCH 142/146] replace is_ok => is_err Co-authored-by: Keith Yeung --- frame/support/procedural/src/benchmark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 2f98db1f99ca2..d8a40be8eb617 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -278,7 +278,7 @@ pub fn benchmarks( let mod_attrs: Vec<&Attribute> = module .attrs .iter() - .filter(|attr| !syn::parse2::(attr.to_token_stream()).is_ok()) + .filter(|attr| syn::parse2::(attr.to_token_stream()).is_err()) .collect(); let mut benchmark_names: Vec = Vec::new(); From 252bf6f4a49306b3175562bd7d1ba1beb0eafd94 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Jan 2023 10:39:34 -0500 Subject: [PATCH 143/146] re-use name during expansion of benchmark def --- frame/support/procedural/src/benchmark.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index d8a40be8eb617..33e1bdfe44d5f 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -313,14 +313,6 @@ pub fn benchmarks( let benchmark_def = BenchmarkDef::from(&func, benchmark_args.extra, benchmark_args.skip_meta)?; - // expand benchmark - let expanded = expand_benchmark( - benchmark_def.clone(), - &func.sig.ident, - instance, - where_clause.clone(), - ); - // record benchmark name let name = &func.sig.ident; benchmark_names.push(name.clone()); @@ -333,6 +325,10 @@ pub fn benchmarks( skip_meta_benchmark_names.push(name.clone()) } + // expand benchmark + let expanded = + expand_benchmark(benchmark_def.clone(), name, instance, where_clause.clone()); + // replace original function def with expanded code *stmt = Item::Verbatim(expanded); } From d6de5a541f1992a46b87a14825f66aac6449e3f9 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Jan 2023 10:41:43 -0500 Subject: [PATCH 144/146] remove unneeded clone --- frame/support/procedural/src/benchmark.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index 33e1bdfe44d5f..d437fd76d4877 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -326,8 +326,7 @@ pub fn benchmarks( } // expand benchmark - let expanded = - expand_benchmark(benchmark_def.clone(), name, instance, where_clause.clone()); + let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); // replace original function def with expanded code *stmt = Item::Verbatim(expanded); From 091825b6ab166304bde37ea1f69435b79efb854f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 20 Jan 2023 18:15:32 -0500 Subject: [PATCH 145/146] fix span for origin missing error --- frame/support/procedural/src/benchmark.rs | 3 +-- frame/support/test/tests/benchmark_ui/missing_origin.stderr | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs index d437fd76d4877..43e3e47de5259 100644 --- a/frame/support/procedural/src/benchmark.rs +++ b/frame/support/procedural/src/benchmark.rs @@ -206,7 +206,6 @@ impl BenchmarkDef { expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { let segment = attr.path.segments.last()?; let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; - let span = expr_call.span(); let mut expr_call = expr_call.clone(); // consume #[extrinsic_call] tokens @@ -214,7 +213,7 @@ impl BenchmarkDef { // extract origin from expr_call let Some(origin) = expr_call.args.first().cloned() else { - return Some(Err(Error::new(span, "Single-item extrinsic calls must specify their origin as the first argument."))) + return Some(Err(Error::new(expr_call.span(), "Single-item extrinsic calls must specify their origin as the first argument."))) }; Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.stderr b/frame/support/test/tests/benchmark_ui/missing_origin.stderr index ed0ac6c0e42c8..0e72bff4747a3 100644 --- a/frame/support/test/tests/benchmark_ui/missing_origin.stderr +++ b/frame/support/test/tests/benchmark_ui/missing_origin.stderr @@ -1,5 +1,5 @@ error: Single-item extrinsic calls must specify their origin as the first argument. - --> tests/benchmark_ui/missing_origin.rs:11:3 + --> tests/benchmark_ui/missing_origin.rs:12:3 | -11 | #[extrinsic_call] - | ^ +12 | thing(); + | ^^^^^ From 43ee067ae56a9bcbc4efc147352aa5b5f069ae31 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 20 Jan 2023 18:26:58 -0500 Subject: [PATCH 146/146] fix missing semi --- frame/support/procedural/src/lib.rs | 2 +- .../tests/benchmark_ui/extrinsic_call_out_of_fn.stderr | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index badbbed1f7379..9bdbbd1f27bb2 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -526,7 +526,7 @@ pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { quote!(compile_error!( "`#[extrinsic_call]` must be in a benchmark function definition labeled with `#[benchmark]`." - )) + );) .into() } diff --git a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr index f831c50c0ca3c..c5194d7a66502 100644 --- a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr @@ -1,11 +1,3 @@ -error: macros that expand to items must be delimited with braces or followed by a semicolon - --> tests/benchmark_ui/extrinsic_call_out_of_fn.rs:3:1 - | -3 | #[extrinsic_call] - | ^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `extrinsic_call` (in Nightly builds, run with -Z macro-backtrace for more info) - error: `#[extrinsic_call]` must be in a benchmark function definition labeled with `#[benchmark]`. --> tests/benchmark_ui/extrinsic_call_out_of_fn.rs:3:1 |