diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index b557048f9d..f518377d62 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1441,6 +1441,31 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "frame-benchmarking" +version = "2.0.0" +dependencies = [ + "parity-scale-codec", + "sp-api", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "frame-benchmarking-cli" +version = "2.0.0" +dependencies = [ + "frame-benchmarking", + "parity-scale-codec", + "sc-cli", + "sc-client", + "sc-client-db", + "sc-executor", + "sc-service", + "sp-runtime", + "structopt", +] + [[package]] name = "frame-executive" version = "2.0.0" @@ -3369,6 +3394,7 @@ version = "2.0.0" dependencies = [ "assert_cmd", "browser-utils", + "frame-benchmarking-cli", "frame-support", "frame-system", "futures 0.3.4", @@ -3437,6 +3463,7 @@ name = "node-executor" version = "2.0.0" dependencies = [ "criterion 0.3.1", + "frame-benchmarking", "frame-support", "frame-system", "node-primitives", @@ -3530,6 +3557,7 @@ dependencies = [ name = "node-runtime" version = "2.0.0" dependencies = [ + "frame-benchmarking", "frame-executive", "frame-support", "frame-system", @@ -3966,6 +3994,7 @@ dependencies = [ name = "pallet-balances" version = "2.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-transaction-payment", @@ -4197,6 +4226,7 @@ name = "pallet-identity" version = "2.0.0" dependencies = [ "enumflags2", + "frame-benchmarking", "frame-support", "frame-system", "pallet-balances", @@ -4433,6 +4463,7 @@ dependencies = [ name = "pallet-timestamp" version = "2.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "impl-trait-for-tuples", diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index a42a8e24d0..2dc0c8926c 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -62,6 +62,7 @@ members = [ "frame/authorship", "frame/babe", "frame/balances", + "frame/benchmarking", "frame/collective", "frame/contracts", "frame/contracts/rpc", @@ -156,6 +157,7 @@ members = [ "utils/browser", "utils/build-script-utils", "utils/fork-tree", + "utils/frame/benchmarking-cli", "utils/frame/rpc/support", "utils/frame/rpc/system", "utils/wasm-builder", diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 079e8d13e2..d383e2c05a 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -89,6 +89,7 @@ node-executor = { version = "2.0.0", path = "../executor" } # CLI-specific dependencies sc-cli = { version = "0.8.0", optional = true, path = "../../../client/cli" } +frame-benchmarking-cli = { version = "2.0.0", optional = true, path = "../../../utils/frame/benchmarking-cli" } node-transaction-factory = { version = "0.8.0", optional = true, path = "../transaction-factory" } node-inspect = { version = "0.8.0", optional = true, path = "../inspect" } @@ -112,6 +113,7 @@ build-script-utils = { version = "2.0.0", package = "substrate-build-script-util structopt = { version = "0.3.8", optional = true } node-transaction-factory = { version = "0.8.0", optional = true, path = "../transaction-factory" } node-inspect = { version = "0.8.0", optional = true, path = "../inspect" } +frame-benchmarking-cli = { version = "2.0.0", optional = true, path = "../../../utils/frame/benchmarking-cli" } [build-dependencies.sc-cli] version = "0.8.0" @@ -135,6 +137,7 @@ cli = [ "node-inspect", "node-transaction-factory", "sc-cli", + "frame-benchmarking-cli", "sc-service/rocksdb", "structopt", "vergen", diff --git a/substrate/bin/node/cli/src/cli.rs b/substrate/bin/node/cli/src/cli.rs index 7dcd02699d..40f1dcf6f4 100644 --- a/substrate/bin/node/cli/src/cli.rs +++ b/substrate/bin/node/cli/src/cli.rs @@ -53,6 +53,13 @@ pub enum Subcommand { about = "Decode given block or extrinsic using current native runtime." )] Inspect(node_inspect::cli::InspectCmd), + + /// The custom benchmark subcommmand benchmarking runtime pallets. + #[structopt( + name = "benchmark", + about = "Benchmark runtime pallets." + )] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), } /// The `factory` command used to generate transactions. diff --git a/substrate/bin/node/cli/src/command.rs b/substrate/bin/node/cli/src/command.rs index ba0f2785c1..5a942d964c 100644 --- a/substrate/bin/node/cli/src/command.rs +++ b/substrate/bin/node/cli/src/command.rs @@ -43,12 +43,17 @@ where cmd.init(&mut config, load_spec, &version)?; let client = sc_service::new_full_client::< - node_runtime::Block,node_runtime::RuntimeApi, node_executor::Executor, _, _, + node_runtime::Block, node_runtime::RuntimeApi, node_executor::Executor, _, _, >(&config)?; let inspect = node_inspect::Inspector::::new(client); cmd.run(inspect) }, + Some(Subcommand::Benchmark(cmd)) => { + cmd.init(&mut config, load_spec, &version)?; + + cmd.run::<_, _, node_runtime::Block, node_executor::Executor>(config) + }, Some(Subcommand::Factory(cli_args)) => { sc_cli::init(&cli_args.shared_params, &version)?; sc_cli::init_config(&mut config, &cli_args.shared_params, &version, load_spec)?; diff --git a/substrate/bin/node/executor/Cargo.toml b/substrate/bin/node/executor/Cargo.toml index 1d894e39fa..f55e1dae58 100644 --- a/substrate/bin/node/executor/Cargo.toml +++ b/substrate/bin/node/executor/Cargo.toml @@ -16,6 +16,7 @@ sp-io = { version = "2.0.0", path = "../../../primitives/io" } sp-state-machine = { version = "0.8", path = "../../../primitives/state-machine" } sp-trie = { version = "2.0.0", path = "../../../primitives/trie" } trie-root = "0.16.0" +frame-benchmarking = { version = "2.0.0", path = "../../../frame/benchmarking" } [dev-dependencies] criterion = "0.3.0" diff --git a/substrate/bin/node/executor/src/lib.rs b/substrate/bin/node/executor/src/lib.rs index 72f40b7c1f..bcc7f48507 100644 --- a/substrate/bin/node/executor/src/lib.rs +++ b/substrate/bin/node/executor/src/lib.rs @@ -26,5 +26,5 @@ native_executor_instance!( pub Executor, node_runtime::api::dispatch, node_runtime::native_version, - sp_io::benchmarking::HostFunctions, + frame_benchmarking::benchmarking::HostFunctions, ); diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 3f8e8b6731..8156e4d444 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -33,6 +33,7 @@ sp-version = { version = "2.0.0", default-features = false, path = "../../../pri # frame dependencies frame-executive = { version = "2.0.0", default-features = false, path = "../../../frame/executive" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../../../frame/benchmarking" } frame-support = { version = "2.0.0", default-features = false, path = "../../../frame/support" } frame-system = { version = "2.0.0", default-features = false, path = "../../../frame/system" } frame-system-rpc-runtime-api = { version = "2.0.0", default-features = false, path = "../../../frame/system/rpc/runtime-api/" } @@ -115,6 +116,7 @@ std = [ "sp-session/std", "pallet-sudo/std", "frame-support/std", + "frame-benchmarking/std", "frame-system-rpc-runtime-api/std", "frame-system/std", "pallet-timestamp/std", diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 956fc0bb0e..b60056e045 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -31,14 +31,13 @@ pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; use sp_api::impl_runtime_apis; use sp_runtime::{ - Permill, Perbill, Percent, ApplyExtrinsicResult, BenchmarkResults, - impl_opaque_keys, generic, create_runtime_str, + Permill, Perbill, Percent, ApplyExtrinsicResult, impl_opaque_keys, generic, create_runtime_str, }; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::transaction_validity::TransactionValidity; use sp_runtime::traits::{ self, BlakeTwo256, Block as BlockT, StaticLookup, SaturatedConversion, - ConvertInto, OpaqueKeys, Benchmarking, + ConvertInto, OpaqueKeys, }; use sp_version::RuntimeVersion; #[cfg(any(feature = "std", test))] @@ -816,28 +815,25 @@ impl_runtime_apis! { } } - impl crate::Benchmark for Runtime { - fn dispatch_benchmark(module: Vec, extrinsic: Vec, steps: u32, repeat: u32) - -> Option> - { + impl frame_benchmarking::Benchmark for Runtime { + fn dispatch_benchmark( + module: Vec, + extrinsic: Vec, + steps: u32, + repeat: u32, + ) -> Option> { + use frame_benchmarking::Benchmarking; + match module.as_slice() { b"pallet-balances" | b"balances" => Balances::run_benchmark(extrinsic, steps, repeat).ok(), b"pallet-identity" | b"identity" => Identity::run_benchmark(extrinsic, steps, repeat).ok(), b"pallet-timestamp" | b"timestamp" => Timestamp::run_benchmark(extrinsic, steps, repeat).ok(), - _ => return None, + _ => None, } } } } -sp_api::decl_runtime_apis! { - pub trait Benchmark - { - fn dispatch_benchmark(module: Vec, extrinsic: Vec, steps: u32, repeat: u32) - -> Option>; - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/cli/src/lib.rs b/substrate/client/cli/src/lib.rs index 7495ad8e75..6259c0a21b 100644 --- a/substrate/client/cli/src/lib.rs +++ b/substrate/client/cli/src/lib.rs @@ -57,7 +57,7 @@ use params::{ pub use params::{ SharedParams, ImportParams, ExecutionStrategy, Subcommand, RunCmd, BuildSpecCmd, ExportBlocksCmd, ImportBlocksCmd, CheckBlockCmd, PurgeChainCmd, RevertCmd, - BenchmarkCmd, + WasmExecutionMethod, }; pub use traits::GetSharedParams; use app_dirs::{AppInfo, AppDataType}; diff --git a/substrate/client/cli/src/params.rs b/substrate/client/cli/src/params.rs index d6c437f668..aaa46c0f63 100644 --- a/substrate/client/cli/src/params.rs +++ b/substrate/client/cli/src/params.rs @@ -47,28 +47,34 @@ impl Into for ExecutionStrategy { } } -arg_enum! { - /// How to execute Wasm runtime code - #[allow(missing_docs)] - #[derive(Debug, Clone, Copy)] - pub enum WasmExecutionMethod { - // Uses an interpreter. - Interpreted, - // Uses a compiled runtime. - Compiled, +#[allow(missing_docs)] +mod wasm_execution_method { + use super::*; + + arg_enum! { + /// How to execute Wasm runtime code + #[derive(Debug, Clone, Copy)] + pub enum WasmExecutionMethod { + // Uses an interpreter. + Interpreted, + // Uses a compiled runtime. + Compiled, + } + } + + impl WasmExecutionMethod { + /// Returns list of variants that are not disabled by feature flags. + pub fn enabled_variants() -> Vec<&'static str> { + Self::variants() + .iter() + .cloned() + .filter(|&name| cfg!(feature = "wasmtime") || name != "Compiled") + .collect() + } } } -impl WasmExecutionMethod { - /// Returns list of variants that are not disabled by feature flags. - fn enabled_variants() -> Vec<&'static str> { - Self::variants() - .iter() - .cloned() - .filter(|&name| cfg!(feature = "wasmtime") || name != "Compiled") - .collect() - } -} +pub use wasm_execution_method::WasmExecutionMethod; impl Into for WasmExecutionMethod { fn into(self) -> sc_service::config::WasmExecutionMethod { @@ -849,49 +855,6 @@ pub struct PurgeChainCmd { pub shared_params: SharedParams, } -/// The `benchmark` command used to benchmark FRAME Pallets. -#[derive(Debug, StructOpt, Clone)] -pub struct BenchmarkCmd { - /// Select a FRAME Pallet to benchmark. - #[structopt(short, long)] - pub pallet: String, - - /// Select an extrinsic to benchmark. - #[structopt(short, long)] - pub extrinsic: String, - - /// Select how many samples we should take across the variable components. - #[structopt(short, long, default_value = "1")] - pub steps: u32, - - /// Select how many repetitions of this benchmark should run. - #[structopt(short, long, default_value = "1")] - pub repeat: u32, - - #[allow(missing_docs)] - #[structopt(flatten)] - pub shared_params: SharedParams, - - /// The execution strategy that should be used for benchmarks - #[structopt( - long = "execution", - value_name = "STRATEGY", - possible_values = &ExecutionStrategy::variants(), - case_insensitive = true, - )] - pub execution: Option, - - /// Method for executing Wasm runtime code. - #[structopt( - long = "wasm-execution", - value_name = "METHOD", - possible_values = &WasmExecutionMethod::enabled_variants(), - case_insensitive = true, - default_value = "Interpreted" - )] - pub wasm_method: WasmExecutionMethod, -} - /// All core commands that are provided by default. /// /// The core commands are split into multiple subcommands and `Run` is the default subcommand. From @@ -916,9 +879,6 @@ pub enum Subcommand { /// Remove the whole chain data. PurgeChain(PurgeChainCmd), - - /// Run runtime benchmarks. - Benchmark(BenchmarkCmd), } impl Subcommand { @@ -933,7 +893,6 @@ impl Subcommand { CheckBlock(params) => ¶ms.shared_params, Revert(params) => ¶ms.shared_params, PurgeChain(params) => ¶ms.shared_params, - Benchmark(params) => ¶ms.shared_params, } } @@ -960,7 +919,6 @@ impl Subcommand { Subcommand::ImportBlocks(cmd) => cmd.run(config, builder), Subcommand::CheckBlock(cmd) => cmd.run(config, builder), Subcommand::PurgeChain(cmd) => cmd.run(config), - Subcommand::Benchmark(cmd) => cmd.run(config, builder), Subcommand::Revert(cmd) => cmd.run(config, builder), } } @@ -1238,31 +1196,3 @@ impl RevertCmd { Ok(()) } } - -impl BenchmarkCmd { - /// Runs the command and benchmarks the chain. - pub fn run( - self, - config: Configuration, - _builder: B, - ) -> error::Result<()> - where - B: FnOnce(Configuration) -> Result, - G: RuntimeGenesis, - E: ChainSpecExtension, - BC: ServiceBuilderCommand + Unpin, - BB: sp_runtime::traits::Block + Debug, - <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, - ::Hash: std::str::FromStr, - { - let spec = config.chain_spec.expect("chain_spec is always Some"); - let execution_strategy = self.execution.unwrap_or(ExecutionStrategy::Native).into(); - let wasm_method = self.wasm_method.into(); - let pallet = self.pallet; - let extrinsic = self.extrinsic; - let steps = self.steps; - let repeat = self.repeat; - sc_service::chain_ops::benchmark_runtime::(spec, execution_strategy, wasm_method, pallet, extrinsic, steps, repeat)?; - Ok(()) - } -} diff --git a/substrate/client/service/src/chain_ops.rs b/substrate/client/service/src/chain_ops.rs index 3d77d9c815..30987170f3 100644 --- a/substrate/client/service/src/chain_ops.rs +++ b/substrate/client/service/src/chain_ops.rs @@ -22,20 +22,17 @@ use crate::error::Error; use sc_chain_spec::{ChainSpec, RuntimeGenesis, Extension}; use log::{warn, info}; use futures::{future, prelude::*}; -use sp_runtime::{ - BuildStorage, BenchmarkResults, - traits::{ - Block as BlockT, NumberFor, One, Zero, Header, SaturatedConversion - } +use sp_runtime::traits::{ + Block as BlockT, NumberFor, One, Zero, Header, SaturatedConversion }; use sp_runtime::generic::{BlockId, SignedBlock}; use codec::{Decode, Encode, IoReader}; -use sc_client::{Client, ExecutionStrategy, StateMachine, LocalCallExecutor}; -#[cfg(feature = "rocksdb")] -use sc_client_db::BenchmarkingState; -use sp_consensus::import_queue::{IncomingBlock, Link, BlockImportError, BlockImportResult, ImportQueue}; -use sp_consensus::BlockOrigin; -use sc_executor::{NativeExecutor, NativeExecutionDispatch, WasmExecutionMethod}; +use sc_client::{Client, LocalCallExecutor}; +use sp_consensus::{ + BlockOrigin, + import_queue::{IncomingBlock, Link, BlockImportError, BlockImportResult, ImportQueue}, +}; +use sc_executor::{NativeExecutor, NativeExecutionDispatch}; use std::{io::{Read, Write, Seek}, pin::Pin}; @@ -49,59 +46,6 @@ pub fn build_spec(spec: ChainSpec, raw: bool) -> error::Result ( - spec: ChainSpec, - strategy: ExecutionStrategy, - wasm_method: WasmExecutionMethod, - pallet: String, - extrinsic: String, - steps: u32, - repeat: u32, -) -> error::Result<()> where - TBl: BlockT, - TExecDisp: NativeExecutionDispatch + 'static, - G: RuntimeGenesis, - E: Extension, -{ - let genesis_storage = spec.build_storage()?; - let mut changes = Default::default(); - let state = BenchmarkingState::::new(genesis_storage)?; - let executor = NativeExecutor::::new( - wasm_method, - None, // heap pages - ); - let result = StateMachine::<_, _, NumberFor, _>::new( - &state, - None, - &mut changes, - &executor, - "Benchmark_dispatch_benchmark", - &(&pallet, &extrinsic, steps, repeat).encode(), - Default::default(), - ).execute(strategy).map_err(|e| format!("Error executing runtime benchmark: {:?}", e))?; - let results = > as Decode>::decode(&mut &result[..]).unwrap_or(None); - if let Some(results) = results { - // Print benchmark metadata - println!("Pallet: {:?}, Extrinsic: {:?}, Steps: {:?}, Repeat: {:?}", pallet, extrinsic, steps, repeat); - // Print the table header - results[0].0.iter().for_each(|param| print!("{:?},", param.0)); - print!("time\n"); - // Print the values - results.iter().for_each(|result| { - let parameters = &result.0; - parameters.iter().for_each(|param| print!("{:?},", param.1)); - print!("{:?}\n", result.1); - }); - info!("Done."); - } else { - info!("No Results."); - } - Ok(()) -} - - impl< TBl, TRtApi, TGen, TCSExt, TBackend, TExecDisp, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, diff --git a/substrate/frame/balances/Cargo.toml b/substrate/frame/balances/Cargo.toml index 871290b182..f2bf1069af 100644 --- a/substrate/frame/balances/Cargo.toml +++ b/substrate/frame/balances/Cargo.toml @@ -11,6 +11,7 @@ codec = { package = "parity-scale-codec", version = "1.0.0", default-features = sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking" } frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } @@ -27,6 +28,7 @@ std = [ "sp-std/std", "sp-io/std", "sp-runtime/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", ] diff --git a/substrate/frame/balances/src/benchmarking.rs b/substrate/frame/balances/src/benchmarking.rs index 605561d597..e564824aaa 100644 --- a/substrate/frame/balances/src/benchmarking.rs +++ b/substrate/frame/balances/src/benchmarking.rs @@ -20,8 +20,10 @@ use super::*; use frame_system::RawOrigin; use sp_io::hashing::blake2_256; -use sp_runtime::{BenchmarkResults, BenchmarkParameter}; -use sp_runtime::traits::{Bounded, Benchmarking, BenchmarkingSetup, Dispatchable}; +use frame_benchmarking::{ + BenchmarkResults, BenchmarkParameter, Benchmarking, BenchmarkingSetup, benchmarking, +}; +use sp_runtime::traits::{Bounded, Dispatchable}; use crate::Module as Balances; @@ -271,8 +273,8 @@ impl Benchmarking for Module { }; // Warm up the DB - sp_io::benchmarking::commit_db(); - sp_io::benchmarking::wipe_db(); + benchmarking::commit_db(); + benchmarking::wipe_db(); let components = , RawOrigin>>::components(&selected_benchmark); // results go here @@ -298,11 +300,11 @@ impl Benchmarking for Module { let (call, caller) = , RawOrigin>>::instance(&selected_benchmark, &c)?; // Commit the externalities to the database, flushing the DB cache. // This will enable worst case scenario for reading from the database. - sp_io::benchmarking::commit_db(); + benchmarking::commit_db(); // Run the benchmark. - let start = sp_io::benchmarking::current_time(); + let start = benchmarking::current_time(); call.dispatch(caller.clone().into())?; - let finish = sp_io::benchmarking::current_time(); + let finish = benchmarking::current_time(); let elapsed = finish - start; sp_std::if_std!{ if let RawOrigin::Signed(who) = caller.clone() { @@ -312,7 +314,7 @@ impl Benchmarking for Module { } results.push((c.clone(), elapsed)); // Wipe the DB back to the genesis state. - sp_io::benchmarking::wipe_db(); + benchmarking::wipe_db(); } } } diff --git a/substrate/frame/benchmarking/Cargo.toml b/substrate/frame/benchmarking/Cargo.toml new file mode 100644 index 0000000000..c2748e080d --- /dev/null +++ b/substrate/frame/benchmarking/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "frame-benchmarking" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.1.2", default-features = false } +sp-api = { version = "2.0.0", path = "../../primitives/api", default-features = false } +sp-runtime-interface = { version = "2.0.0", path = "../../primitives/runtime-interface", default-features = false } +sp-std = { version = "2.0.0", path = "../../primitives/std", default-features = false } + +[features] +default = [ "std" ] +std = [ "sp-runtime-interface/std", "sp-api/std", "codec/std", "sp-std/std" ] diff --git a/substrate/frame/benchmarking/src/lib.rs b/substrate/frame/benchmarking/src/lib.rs new file mode 100644 index 0000000000..c57cfb4914 --- /dev/null +++ b/substrate/frame/benchmarking/src/lib.rs @@ -0,0 +1,141 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Interfaces and types for benchmarking a FRAME runtime. + +#![cfg_attr(not(feature = "std"), no_std)] + +use sp_std::vec::Vec; + +/// An alphabet of possible parameters to use for benchmarking. +#[derive(codec::Encode, codec::Decode, Clone, Copy, PartialEq, Debug)] +#[allow(missing_docs)] +pub enum BenchmarkParameter { + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, +} + +/// Results from running benchmarks on a FRAME pallet. +/// Contains duration of the function call in nanoseconds along with the benchmark parameters +/// used for that benchmark result. +pub type BenchmarkResults = (Vec<(BenchmarkParameter, u32)>, u128); + +sp_api::decl_runtime_apis! { + /// Runtime api for benchmarking a FRAME runtime. + pub trait Benchmark { + /// Dispatch the given benchmark. + fn dispatch_benchmark( + module: Vec, + extrinsic: Vec, + steps: u32, + repeat: u32, + ) -> Option>; + } +} + +/// Interface that provides functions for benchmarking the runtime. +#[sp_runtime_interface::runtime_interface] +pub trait Benchmarking { + /// Get the number of nanoseconds passed since the UNIX epoch + /// + /// WARNING! This is a non-deterministic call. Do not use this within + /// consensus critical logic. + fn current_time() -> u128 { + std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH) + .expect("Unix time doesn't go backwards; qed") + .as_nanos() + } + + /// Reset the trie database to the genesis state. + fn wipe_db(&mut self) { + self.wipe() + } + + /// Commit pending storage changes to the trie database and clear the database cache. + fn commit_db(&mut self) { + self.commit() + } +} + +/// The pallet benchmarking trait. +pub trait Benchmarking { + /// Run the benchmarks for this pallet. + /// + /// Parameters + /// - `extrinsic`: The name of extrinsic function you want to benchmark encoded as bytes. + /// - `steps`: The number of sample points you want to take across the range of parameters. + /// - `repeat`: The number of times you want to repeat a benchmark. + fn run_benchmark(extrinsic: Vec, steps: u32, repeat: u32) -> Result, &'static str>; +} + +/// The required setup for creating a benchmark. +pub trait BenchmarkingSetup { + /// Return the components and their ranges which should be tested in this benchmark. + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>; + + /// Set up the storage, and prepare a call and caller to test in a single run of the benchmark. + fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(Call, RawOrigin), &'static str>; +} + +/// Creates a `SelectedBenchmark` enum implementing `BenchmarkingSetup`. +/// +/// Every variant must implement [`BenchmarkingSetup`]. +/// +/// ```nocompile +/// +/// struct Transfer; +/// impl BenchmarkingSetup for Transfer { ... } +/// +/// struct SetBalance; +/// impl BenchmarkingSetup for SetBalance { ... } +/// +/// selected_benchmark!(Transfer, SetBalance); +/// ``` +#[macro_export] +macro_rules! selected_benchmark { + ( + $( $bench:ident ),* + ) => { + // The list of available benchmarks for this pallet. + enum SelectedBenchmark { + $( $bench, )* + } + + // Allow us to select a benchmark from the list of available benchmarks. + impl $crate::BenchmarkingSetup, RawOrigin> for SelectedBenchmark { + fn components(&self) -> Vec<($crate::BenchmarkParameter, u32, u32)> { + match self { + $( Self::$bench => <$bench as $crate::BenchmarkingSetup< + T, + Call, + RawOrigin, + >>::components(&$bench), )* + } + } + + fn instance(&self, components: &[($crate::BenchmarkParameter, u32)]) + -> Result<(Call, RawOrigin), &'static str> + { + match self { + $( Self::$bench => <$bench as $crate::BenchmarkingSetup< + T, + Call, + RawOrigin, + >>::instance(&$bench, components), )* + } + } + } + }; +} diff --git a/substrate/frame/identity/Cargo.toml b/substrate/frame/identity/Cargo.toml index 59e8f721c8..c95d230230 100644 --- a/substrate/frame/identity/Cargo.toml +++ b/substrate/frame/identity/Cargo.toml @@ -12,6 +12,7 @@ enumflags2 = { version = "0.6.2" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking" } frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } @@ -27,6 +28,7 @@ std = [ "sp-std/std", "sp-io/std", "sp-runtime/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", ] diff --git a/substrate/frame/identity/src/benchmarking.rs b/substrate/frame/identity/src/benchmarking.rs index 11e98101ec..c208d32717 100644 --- a/substrate/frame/identity/src/benchmarking.rs +++ b/substrate/frame/identity/src/benchmarking.rs @@ -20,8 +20,11 @@ use super::*; use frame_system::RawOrigin; use sp_io::hashing::blake2_256; -use sp_runtime::{BenchmarkResults, BenchmarkParameter, selected_benchmark}; -use sp_runtime::traits::{Bounded, Benchmarking, BenchmarkingSetup, Dispatchable}; +use frame_benchmarking::{ + BenchmarkResults, BenchmarkParameter, selected_benchmark, benchmarking, Benchmarking, + BenchmarkingSetup, +}; +use sp_runtime::traits::{Bounded, Dispatchable}; use crate::Module as Identity; @@ -102,7 +105,7 @@ impl BenchmarkingSetup, RawOrigin> for { // Add r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // Return the `add_registrar` r + 1 call Ok((crate::Call::::add_registrar(account::("registrar", r + 1)), RawOrigin::Root)) @@ -126,7 +129,7 @@ impl BenchmarkingSetup, RawOrigin> for { // Add r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // The target user let caller = account::("caller", r); @@ -135,7 +138,7 @@ impl BenchmarkingSetup, RawOrigin> for let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Add an initial identity - let initial_info = benchmarking::create_identity_info::(1); + let initial_info = create_identity_info::(1); Identity::::set_identity(caller_origin.clone(), initial_info)?; // User requests judgement from all the registrars, and they approve @@ -152,7 +155,7 @@ impl BenchmarkingSetup, RawOrigin> for // Create identity info with x additional fields let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; // 32 byte data that we reuse below - let info = benchmarking::create_identity_info::(x); + let info = create_identity_info::(x); // Return the `set_identity` call Ok((crate::Call::::set_identity(info), RawOrigin::Signed(caller))) @@ -181,7 +184,7 @@ impl BenchmarkingSetup, RawOrigin> for let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Create their main identity - let info = benchmarking::create_identity_info::(1); + let info = create_identity_info::(1); Identity::::set_identity(caller_origin.clone(), info)?; // Give them s many sub accounts @@ -221,16 +224,16 @@ impl BenchmarkingSetup, RawOrigin> for // Register r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // Create their main identity with x additional fields let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; - let info = benchmarking::create_identity_info::(x); + let info = create_identity_info::(x); Identity::::set_identity(caller_origin.clone(), info)?; // Give them s many sub accounts let s = components.iter().find(|&c| c.0 == BenchmarkParameter::S).unwrap().1; - let _ = benchmarking::add_sub_accounts::(caller.clone(), s)?; + let _ = add_sub_accounts::(caller.clone(), s)?; // User requests judgement from all the registrars, and they approve for i in 0..r { @@ -270,11 +273,11 @@ impl BenchmarkingSetup, RawOrigin> for // Register r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // Create their main identity with x additional fields let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; - let info = benchmarking::create_identity_info::(x); + let info = create_identity_info::(x); Identity::::set_identity(caller_origin.clone(), info)?; // Return the `request_judgement` call @@ -304,11 +307,11 @@ impl BenchmarkingSetup, RawOrigin> for // Register r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // Create their main identity with x additional fields let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; - let info = benchmarking::create_identity_info::(x); + let info = create_identity_info::(x); Identity::::set_identity(caller_origin.clone(), info)?; // Request judgement @@ -337,7 +340,7 @@ impl BenchmarkingSetup, RawOrigin> for // Register r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // Add caller as registrar Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; @@ -366,7 +369,7 @@ impl BenchmarkingSetup, RawOrigin> for // Register r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // Add caller as registrar Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; @@ -395,7 +398,7 @@ impl BenchmarkingSetup, RawOrigin> for // Register r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // Add caller as registrar Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; @@ -428,7 +431,7 @@ impl BenchmarkingSetup, RawOrigin> for { // Add r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // The user let user = account::("user", r); @@ -438,7 +441,7 @@ impl BenchmarkingSetup, RawOrigin> for // Create their main identity with x additional fields let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; - let info = benchmarking::create_identity_info::(x); + let info = create_identity_info::(x); Identity::::set_identity(user_origin.clone(), info)?; // The caller registrar @@ -486,16 +489,16 @@ impl BenchmarkingSetup, RawOrigin> for // Register r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; - benchmarking::add_registrars::(r)?; + add_registrars::(r)?; // Create their main identity with x additional fields let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; - let info = benchmarking::create_identity_info::(x); + let info = create_identity_info::(x); Identity::::set_identity(caller_origin.clone(), info)?; // Give them s many sub accounts let s = components.iter().find(|&c| c.0 == BenchmarkParameter::S).unwrap().1; - let _ = benchmarking::add_sub_accounts::(caller.clone(), s)?; + let _ = add_sub_accounts::(caller.clone(), s)?; // User requests judgement from all the registrars, and they approve for i in 0..r { @@ -547,8 +550,8 @@ impl Benchmarking for Module { }; // Warm up the DB - sp_io::benchmarking::commit_db(); - sp_io::benchmarking::wipe_db(); + benchmarking::commit_db(); + benchmarking::wipe_db(); // first one is set_identity. let components = , RawOrigin>>::components(&selected_benchmark); @@ -575,15 +578,15 @@ impl Benchmarking for Module { let (call, caller) = , RawOrigin>>::instance(&selected_benchmark, &c)?; // Commit the externalities to the database, flushing the DB cache. // This will enable worst case scenario for reading from the database. - sp_io::benchmarking::commit_db(); + benchmarking::commit_db(); // Run the benchmark. - let start = sp_io::benchmarking::current_time(); + let start = benchmarking::current_time(); call.dispatch(caller.into())?; - let finish = sp_io::benchmarking::current_time(); + let finish = benchmarking::current_time(); let elapsed = finish - start; results.push((c.clone(), elapsed)); // Wipe the DB back to the genesis state. - sp_io::benchmarking::wipe_db(); + benchmarking::wipe_db(); } } } diff --git a/substrate/frame/timestamp/Cargo.toml b/substrate/frame/timestamp/Cargo.toml index e68392f9e9..107374799d 100644 --- a/substrate/frame/timestamp/Cargo.toml +++ b/substrate/frame/timestamp/Cargo.toml @@ -12,6 +12,7 @@ sp-std = { version = "2.0.0", default-features = false, path = "../../primitives sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking" } frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } sp-timestamp = { version = "2.0.0", default-features = false, path = "../../primitives/timestamp" } @@ -28,6 +29,7 @@ std = [ "codec/std", "sp-std/std", "sp-runtime/std", + "frame-benchmarking/std", "frame-support/std", "serde", "frame-system/std", diff --git a/substrate/frame/timestamp/src/benchmarking.rs b/substrate/frame/timestamp/src/benchmarking.rs index 55d6d7e046..9310d39dfd 100644 --- a/substrate/frame/timestamp/src/benchmarking.rs +++ b/substrate/frame/timestamp/src/benchmarking.rs @@ -21,8 +21,11 @@ use super::*; use sp_std::prelude::*; use frame_system::RawOrigin; -use sp_runtime::{BenchmarkResults, BenchmarkParameter, selected_benchmark}; -use sp_runtime::traits::{Benchmarking, BenchmarkingSetup, Dispatchable}; +use frame_benchmarking::{ + BenchmarkResults, BenchmarkParameter, selected_benchmark, benchmarking, + Benchmarking, BenchmarkingSetup, +}; +use sp_runtime::traits::Dispatchable; /// Benchmark `set` extrinsic. struct Set; @@ -54,10 +57,10 @@ impl Benchmarking for Module { b"set" => SelectedBenchmark::Set, _ => return Err("Could not find extrinsic."), }; - + // Warm up the DB - sp_io::benchmarking::commit_db(); - sp_io::benchmarking::wipe_db(); + benchmarking::commit_db(); + benchmarking::wipe_db(); let components = , RawOrigin>>::components(&selected_benchmark); let mut results: Vec = Vec::new(); @@ -87,15 +90,15 @@ impl Benchmarking for Module { >>::instance(&selected_benchmark, &c)?; // Commit the externalities to the database, flushing the DB cache. // This will enable worst case scenario for reading from the database. - sp_io::benchmarking::commit_db(); + benchmarking::commit_db(); // Run the benchmark. - let start = sp_io::benchmarking::current_time(); + let start = benchmarking::current_time(); call.dispatch(caller.into())?; - let finish = sp_io::benchmarking::current_time(); + let finish = benchmarking::current_time(); let elapsed = finish - start; results.push((c.clone(), elapsed)); // Wipe the DB back to the genesis state. - sp_io::benchmarking::wipe_db(); + benchmarking::wipe_db(); } } } diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs index a1e9181f28..5d29fe5c94 100644 --- a/substrate/primitives/io/src/lib.rs +++ b/substrate/primitives/io/src/lib.rs @@ -771,30 +771,6 @@ pub trait Logging { } } -/// Interface that provides functions for benchmarking the runtime. -#[runtime_interface] -pub trait Benchmarking { - /// Get the number of nanoseconds passed since the UNIX epoch - /// - /// WARNING! This is a non-deterministic call. Do not use this within - /// consensus critical logic. - fn current_time() -> u128 { - std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH) - .expect("Unix time doesn't go backwards; qed") - .as_nanos() - } - - /// Reset the trie database to the genesis state. - fn wipe_db(&mut self) { - self.wipe() - } - - /// Commit pending storage changes to the trie database and clear the database cache. - fn commit_db(&mut self) { - self.commit() - } -} - /// Wasm-only interface that provides functions for interacting with the sandbox. #[runtime_interface(wasm_only)] pub trait Sandbox { diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index 4d6739bb13..6501dafc0e 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -685,18 +685,6 @@ pub fn print(print: impl traits::Printable) { print.print(); } -/// An alphabet of possible parameters to use for benchmarking. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Debug)] -#[allow(missing_docs)] -pub enum BenchmarkParameter { - A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, -} - -/// Results from running benchmarks on a FRAME pallet. -/// Contains duration of the function call in nanoseconds along with the benchmark parameters -/// used for that benchmark result. -pub type BenchmarkResults = (Vec<(BenchmarkParameter, u32)>, u128); - #[cfg(test)] mod tests { use super::*; diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs index 183df08ab8..f6655f68b4 100644 --- a/substrate/primitives/runtime/src/traits.rs +++ b/substrate/primitives/runtime/src/traits.rs @@ -26,7 +26,6 @@ use std::str::FromStr; #[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; use sp_core::{self, Hasher, Blake2Hasher, TypeId, RuntimeDebug}; -use crate::BenchmarkParameter; use crate::codec::{Codec, Encode, Decode}; use crate::transaction_validity::{ ValidTransaction, TransactionValidity, TransactionValidityError, UnknownTransaction, @@ -1318,75 +1317,6 @@ pub trait BlockIdTo { ) -> Result>, Self::Error>; } -/// The pallet benchmarking trait. -pub trait Benchmarking { - /// Run the benchmarks for this pallet. - /// - /// Parameters - /// - `extrinsic`: The name of extrinsic function you want to benchmark encoded as bytes. - /// - `steps`: The number of sample points you want to take across the range of parameters. - /// - `repeat`: The number of times you want to repeat a benchmark. - fn run_benchmark(extrinsic: Vec, steps: u32, repeat: u32) -> Result, &'static str>; -} - -/// The required setup for creating a benchmark. -pub trait BenchmarkingSetup { - /// Return the components and their ranges which should be tested in this benchmark. - fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>; - - /// Set up the storage, and prepare a call and caller to test in a single run of the benchmark. - fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(Call, RawOrigin), &'static str>; -} - -/// Creates a `SelectedBenchmark` enum implementing `BenchmarkingSetup`. -/// -/// Every variant must implement [`BenchmarkingSetup`](crate::traits::BenchmarkingSetup). -/// -/// ```nocompile -/// -/// struct Transfer; -/// impl BenchmarkingSetup for Transfer { ... } -/// -/// struct SetBalance; -/// impl BenchmarkingSetup for SetBalance { ... } -/// -/// selected_benchmark!(Transfer, SetBalance); -/// ``` -#[macro_export] -macro_rules! selected_benchmark { - ($($bench:ident),*) => { - // The list of available benchmarks for this pallet. - enum SelectedBenchmark { - $( $bench, )* - } - - // Allow us to select a benchmark from the list of available benchmarks. - impl $crate::traits::BenchmarkingSetup, RawOrigin> for SelectedBenchmark { - fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { - match self { - $( Self::$bench => <$bench as $crate::traits::BenchmarkingSetup< - T, - Call, - RawOrigin, - >>::components(&$bench), )* - } - } - - fn instance(&self, components: &[(BenchmarkParameter, u32)]) - -> Result<(Call, RawOrigin), &'static str> - { - match self { - $( Self::$bench => <$bench as $crate::traits::BenchmarkingSetup< - T, - Call, - RawOrigin, - >>::instance(&$bench, components), )* - } - } - } - }; -} - #[cfg(test)] mod tests { use super::*; diff --git a/substrate/utils/frame/benchmarking-cli/Cargo.toml b/substrate/utils/frame/benchmarking-cli/Cargo.toml new file mode 100644 index 0000000000..129104a901 --- /dev/null +++ b/substrate/utils/frame/benchmarking-cli/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "frame-benchmarking-cli" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +frame-benchmarking = { version = "2.0.0", path = "../../../frame/benchmarking" } +sc-service = { version = "0.8.0", path = "../../../client/service" } +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +sc-client = { version = "0.8.0", path = "../../../client" } +sc-client-db = { version = "0.8.0", path = "../../../client/db" } +sc-executor = { version = "0.8.0", path = "../../../client/executor" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +structopt = "0.3.8" +codec = { version = "1.1.2", package = "parity-scale-codec" } diff --git a/substrate/utils/frame/benchmarking-cli/src/lib.rs b/substrate/utils/frame/benchmarking-cli/src/lib.rs new file mode 100644 index 0000000000..a8303beb0f --- /dev/null +++ b/substrate/utils/frame/benchmarking-cli/src/lib.rs @@ -0,0 +1,152 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use sp_runtime::{BuildStorage, traits::{Block as BlockT, Header as HeaderT, NumberFor}}; +use sc_client::StateMachine; +use sc_cli::{ExecutionStrategy, WasmExecutionMethod}; +use sc_client_db::BenchmarkingState; +use sc_service::{RuntimeGenesis, ChainSpecExtension}; +use sc_executor::{NativeExecutor, NativeExecutionDispatch}; +use std::fmt::Debug; +use codec::{Encode, Decode}; +use frame_benchmarking::BenchmarkResults; + +/// The `benchmark` command used to benchmark FRAME Pallets. +#[derive(Debug, structopt::StructOpt, Clone)] +pub struct BenchmarkCmd { + /// Select a FRAME Pallet to benchmark. + #[structopt(short, long)] + pub pallet: String, + + /// Select an extrinsic to benchmark. + #[structopt(short, long)] + pub extrinsic: String, + + /// Select how many samples we should take across the variable components. + #[structopt(short, long, default_value = "1")] + pub steps: u32, + + /// Select how many repetitions of this benchmark should run. + #[structopt(short, long, default_value = "1")] + pub repeat: u32, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub shared_params: sc_cli::SharedParams, + + /// The execution strategy that should be used for benchmarks + #[structopt( + long = "execution", + value_name = "STRATEGY", + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + )] + pub execution: Option, + + /// Method for executing Wasm runtime code. + #[structopt( + long = "wasm-execution", + value_name = "METHOD", + possible_values = &WasmExecutionMethod::enabled_variants(), + case_insensitive = true, + default_value = "Interpreted" + )] + pub wasm_method: WasmExecutionMethod, +} + +impl BenchmarkCmd { + /// Parse CLI arguments and initialize given config. + pub fn init( + &self, + config: &mut sc_service::config::Configuration, + spec_factory: impl FnOnce(&str) -> Result>, String>, + version: &sc_cli::VersionInfo, + ) -> sc_cli::error::Result<()> where + G: sc_service::RuntimeGenesis, + E: sc_service::ChainSpecExtension, + { + sc_cli::init_config(config, &self.shared_params, version, spec_factory)?; + // make sure to configure keystore + sc_cli::fill_config_keystore_in_memory(config).map_err(Into::into) + } + + /// Runs the command and benchmarks the chain. + pub fn run( + self, + config: sc_service::Configuration, + ) -> sc_cli::error::Result<()> + where + G: RuntimeGenesis, + E: ChainSpecExtension, + BB: BlockT + Debug, + <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + ::Hash: std::str::FromStr, + ExecDispatch: NativeExecutionDispatch + 'static, + { + let spec = config.chain_spec.expect("chain_spec is always Some"); + let wasm_method = self.wasm_method.into(); + let strategy = self.execution.unwrap_or(ExecutionStrategy::Native); + + let genesis_storage = spec.build_storage()?; + let mut changes = Default::default(); + let state = BenchmarkingState::::new(genesis_storage)?; + let executor = NativeExecutor::::new( + wasm_method, + None, // heap pages + ); + let result = StateMachine::<_, _, NumberFor, _>::new( + &state, + None, + &mut changes, + &executor, + "Benchmark_dispatch_benchmark", + &(&self.pallet, &self.extrinsic, self.steps, self.repeat).encode(), + Default::default(), + ) + .execute(strategy.into()) + .map_err(|e| format!("Error executing runtime benchmark: {:?}", e))?; + let results = > as Decode>::decode(&mut &result[..]) + .unwrap_or(None); + + if let Some(results) = results { + // Print benchmark metadata + println!( + "Pallet: {:?}, Extrinsic: {:?}, Steps: {:?}, Repeat: {:?}", + self.pallet, + self.extrinsic, + self.steps, + self.repeat, + ); + + // Print the table header + results[0].0.iter().for_each(|param| print!("{:?},", param.0)); + + print!("time\n"); + // Print the values + results.iter().for_each(|result| { + let parameters = &result.0; + parameters.iter().for_each(|param| print!("{:?},", param.1)); + print!("{:?}\n", result.1); + }); + + eprintln!("Done."); + } else { + eprintln!("No Results."); + } + + Ok(()) + } +}