diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 67346ad86b..a7fdd2b4c9 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1456,6 +1456,8 @@ dependencies = [ name = "frame-benchmarking" version = "2.0.0-alpha.3" dependencies = [ + "frame-support", + "frame-system", "parity-scale-codec", "sp-api", "sp-io", @@ -4119,6 +4121,7 @@ dependencies = [ name = "pallet-example" version = "2.0.0-alpha.3" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-balances", diff --git a/substrate/frame/benchmarking/Cargo.toml b/substrate/frame/benchmarking/Cargo.toml index ecaf987bb0..b39031a6b7 100644 --- a/substrate/frame/benchmarking/Cargo.toml +++ b/substrate/frame/benchmarking/Cargo.toml @@ -15,7 +15,17 @@ sp-runtime-interface = { version = "2.0.0-alpha.2", path = "../../primitives/run sp-runtime = { version = "2.0.0-alpha.2", path = "../../primitives/runtime", default-features = false } sp-std = { version = "2.0.0-alpha.2", path = "../../primitives/std", default-features = false } sp-io = { path = "../../primitives/io", default-features = false, version = "2.0.0-alpha.2" } +frame-support = { version = "2.0.0-alpha.2", default-features = false, path = "../support" } +frame-system = { version = "2.0.0-alpha.2", default-features = false, path = "../system" } [features] default = [ "std" ] -std = [ "sp-runtime-interface/std", "sp-runtime/std", "sp-api/std", "codec/std", "sp-std/std" ] +std = [ + "codec/std", + "sp-runtime-interface/std", + "sp-runtime/std", + "sp-api/std", + "sp-std/std", + "frame-support/std", + "frame-system/std", +] diff --git a/substrate/frame/benchmarking/src/lib.rs b/substrate/frame/benchmarking/src/lib.rs index d979000432..f7e98336b2 100644 --- a/substrate/frame/benchmarking/src/lib.rs +++ b/substrate/frame/benchmarking/src/lib.rs @@ -18,6 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +mod tests; mod utils; pub use utils::*; #[doc(hidden)] @@ -82,15 +83,14 @@ pub use sp_io::storage::root as storage_root; /// foo { /// let caller = account::(b"caller", 0, benchmarks_seed); /// let l = ...; -/// } _(Origin::Signed(caller), vec![0u8; l]) +/// }: _(Origin::Signed(caller), vec![0u8; l]) /// /// // second dispatchable: bar; this is a root dispatchable and accepts a `u8` vector of size -/// // `l`. We don't want it preininitialised like before so we override using the `=> ()` -/// // notation. +/// // `l`. We don't want it preininitialised like before so we override using the `=> ()` notation. /// // In this case, we explicitly name the call using `bar` instead of `_`. /// bar { /// let l = _ .. _ => (); -/// } bar(Origin::Root, vec![0u8; l]) +/// }: bar(Origin::Root, vec![0u8; l]) /// /// // third dispatchable: baz; this is a user dispatchable. It isn't dependent on length like the /// // other two but has its own complexity `c` that needs setting up. It uses `caller` (in the @@ -100,13 +100,13 @@ pub use sp_io::storage::root as storage_root; /// baz1 { /// let caller = account::(b"caller", 0, benchmarks_seed); /// let c = 0 .. 10 => setup_c(&caller, c); -/// } baz(Origin::Signed(caller)) +/// }: baz(Origin::Signed(caller)) /// /// // this is a second benchmark of the baz dispatchable with a different setup. /// baz2 { /// let caller = account::(b"caller", 0, benchmarks_seed); /// let c = 0 .. 10 => setup_c_in_some_other_way(&caller, c); -/// } baz(Origin::Signed(caller)) +/// }: baz(Origin::Signed(caller)) /// /// // this is benchmarking some code that is not a dispatchable. /// populate_a_set { @@ -115,7 +115,7 @@ pub use sp_io::storage::root as storage_root; /// for i in 0..x { /// m.insert(i); /// } -/// } { m.into_iter().collect::() } +/// }: { m.into_iter().collect::() } /// } /// ``` #[macro_export] @@ -134,97 +134,6 @@ macro_rules! benchmarks { } } -#[macro_export] -macro_rules! impl_benchmark { - ( - $( $name:ident ),* - ) => { - impl $crate::Benchmarking<$crate::BenchmarkResults> for Module { - fn run_benchmark( - extrinsic: Vec, - lowest_range_values: Vec, - highest_range_values: Vec, - steps: Vec, - repeat: u32, - ) -> Result, &'static str> { - // Map the input to the selected benchmark. - let extrinsic = sp_std::str::from_utf8(extrinsic.as_slice()) - .map_err(|_| "Could not find extrinsic")?; - let selected_benchmark = match extrinsic { - $( stringify!($name) => SelectedBenchmark::$name, )* - _ => return Err("Could not find extrinsic."), - }; - - // Warm up the DB - $crate::benchmarking::commit_db(); - $crate::benchmarking::wipe_db(); - - let components = , RawOrigin>>::components(&selected_benchmark); - let mut results: Vec<$crate::BenchmarkResults> = Vec::new(); - - // Default number of steps for a component. - let mut prev_steps = 10; - - // Select the component we will be benchmarking. Each component will be benchmarked. - for (idx, (name, low, high)) in components.iter().enumerate() { - // Get the number of steps for this component. - let steps = steps.get(idx).cloned().unwrap_or(prev_steps); - prev_steps = steps; - - let lowest = lowest_range_values.get(idx).cloned().unwrap_or(*low); - let highest = highest_range_values.get(idx).cloned().unwrap_or(*high); - - let diff = highest - lowest; - - // Create up to `STEPS` steps for that component between high and low. - let step_size = (diff / steps).max(1); - let num_of_steps = diff / step_size + 1; - - for s in 0..num_of_steps { - // This is the value we will be testing for component `name` - let component_value = lowest + step_size * s; - - // Select the max value for all the other components. - let c: Vec<($crate::BenchmarkParameter, u32)> = components.iter() - .enumerate() - .map(|(idx, (n, l, h))| - if n == name { - (*n, component_value) - } else { - (*n, *highest_range_values.get(idx).unwrap_or(h)) - } - ) - .collect(); - - // Run the benchmark `repeat` times. - for _ in 0..repeat { - // Set up the externalities environment for the setup we want to benchmark. - 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. - $crate::benchmarking::commit_db(); - // Time the extrinsic logic. - let start_extrinsic = $crate::benchmarking::current_time(); - call.dispatch(caller.into())?; - let finish_extrinsic = $crate::benchmarking::current_time(); - let elapsed_extrinsic = finish_extrinsic - start_extrinsic; - // Time the storage root recalculation. - let start_storage_root = $crate::benchmarking::current_time(); - $crate::storage_root(); - let finish_storage_root = $crate::benchmarking::current_time(); - let elapsed_storage_root = finish_storage_root - start_storage_root; - results.push((c.clone(), elapsed_extrinsic, elapsed_storage_root)); - // Wipe the DB back to the genesis state. - $crate::benchmarking::wipe_db(); - } - } - } - return Ok(results); - } - } - } -} - #[macro_export] #[allow(missing_docs)] macro_rules! benchmarks_iter { @@ -247,14 +156,16 @@ macro_rules! benchmarks_iter { $( $rest:tt )* ) => { $crate::benchmarks_iter! { - { $( $common )* } ( $( $names )* ) $name { $( $code )* }: { Ok((crate::Call::::$dispatch($($arg),*), $origin)) } $( $rest )* + { $( $common )* } ( $( $names )* ) $name { $( $code )* }: { + Call::::$dispatch($($arg),*).dispatch($origin.into())?; + } $( $rest )* } }; // iteration arm: ( { $( $common:tt )* } ( $( $names:ident )* ) - $name:ident { $( $code:tt )* }: { $eval:expr } + $name:ident { $( $code:tt )* }: $eval:block $( $rest:tt )* ) => { $crate::benchmark_backend! { @@ -277,7 +188,7 @@ macro_rules! benchmark_backend { $( $common:tt )* } { $( PRE { $( $pre_parsed:tt )* } )* - } { $eval:expr } { + } { $eval:block } { let $pre_id:tt : $pre_ty:ty = $pre_ex:expr; $( $rest:tt )* } ) => { @@ -292,7 +203,7 @@ macro_rules! benchmark_backend { $( $common:tt )* } { $( $parsed:tt )* - } { $eval:expr } { + } { $eval:block } { let $param:ident in ( $param_from:expr ) .. $param_to:expr => $param_instancer:expr; $( $rest:tt )* }) => { @@ -308,7 +219,7 @@ macro_rules! benchmark_backend { $( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )* } { $( $parsed:tt )* - } { $eval:expr } { + } { $eval:block } { let $param:ident in ...; $( $rest:tt )* }) => { @@ -331,7 +242,7 @@ macro_rules! benchmark_backend { $( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )* } { $( $parsed:tt )* - } { $eval:expr } { + } { $eval:block } { let $param:ident in _ .. _ => $param_instancer:expr ; $( $rest:tt )* }) => { @@ -354,7 +265,7 @@ macro_rules! benchmark_backend { $( $common:tt )* } { $( $parsed:tt )* - } { $eval:expr } { + } { $eval:block } { let $param:ident in $param_from:tt .. $param_to:expr => $param_instancer:expr ; $( $rest:tt )* }) => { @@ -370,7 +281,7 @@ macro_rules! benchmark_backend { $( $common:tt )* } { $( $parsed:tt )* - } { $eval:expr } { + } { $eval:block } { let $param:ident in $param_from:tt .. $param_to:expr; $( $rest:tt )* }) => { @@ -386,7 +297,7 @@ macro_rules! benchmark_backend { $( $common:tt )* } { $( $parsed:tt )* - } { $eval:expr } { + } { $eval:block } { let $pre_id:tt = $pre_ex:expr; $( $rest:tt )* }) => { @@ -403,11 +314,11 @@ macro_rules! benchmark_backend { } { $( PRE { $pre_id:tt , $pre_ty:ty , $pre_ex:expr } )* $( PARAM { $param:ident , $param_from:expr , $param_to:expr , $param_instancer:expr } )* - } { $eval:expr } { $( $post:tt )* } ) => { + } { $eval:block } { $( $post:tt )* } ) => { #[allow(non_camel_case_types)] struct $name; #[allow(unused_variables)] - impl $crate::BenchmarkingSetup, RawOrigin> for $name { + impl $crate::BenchmarkingSetup for $name { fn components(&self) -> Vec<($crate::BenchmarkParameter, u32, u32)> { vec! [ $( @@ -417,7 +328,7 @@ macro_rules! benchmark_backend { } fn instance(&self, components: &[($crate::BenchmarkParameter, u32)]) - -> Result<(crate::Call, RawOrigin), &'static str> + -> Result Result<(), &'static str>>, &'static str> { $( let $common = $common_from; @@ -431,7 +342,8 @@ macro_rules! benchmark_backend { )* $( $param_instancer ; )* $( $post )* - $eval + + Ok(Box::new(move || -> Result<(), &'static str> { $eval; Ok(()) })) } } } @@ -463,28 +375,116 @@ macro_rules! selected_benchmark { } // Allow us to select a benchmark from the list of available benchmarks. - impl $crate::BenchmarkingSetup, RawOrigin> for SelectedBenchmark { + impl $crate::BenchmarkingSetup for SelectedBenchmark { fn components(&self) -> Vec<($crate::BenchmarkParameter, u32, u32)> { match self { - $( Self::$bench => <$bench as $crate::BenchmarkingSetup< - T, - Call, - RawOrigin, - >>::components(&$bench), )* + $( Self::$bench => <$bench as $crate::BenchmarkingSetup>::components(&$bench), )* } } fn instance(&self, components: &[($crate::BenchmarkParameter, u32)]) - -> Result<(Call, RawOrigin), &'static str> + -> Result Result<(), &'static str>>, &'static str> { match self { - $( Self::$bench => <$bench as $crate::BenchmarkingSetup< - T, - Call, - RawOrigin, - >>::instance(&$bench, components), )* + $( Self::$bench => <$bench as $crate::BenchmarkingSetup>::instance(&$bench, components), )* } } } }; } + +#[macro_export] +macro_rules! impl_benchmark { + ( + $( $name:ident ),* + ) => { + impl $crate::Benchmarking<$crate::BenchmarkResults> for Module { + fn run_benchmark( + extrinsic: Vec, + lowest_range_values: Vec, + highest_range_values: Vec, + steps: Vec, + repeat: u32, + ) -> Result, &'static str> { + // Map the input to the selected benchmark. + let extrinsic = sp_std::str::from_utf8(extrinsic.as_slice()) + .map_err(|_| "`extrinsic` is not a valid utf8 string!")?; + let selected_benchmark = match extrinsic { + $( stringify!($name) => SelectedBenchmark::$name, )* + _ => return Err("Could not find extrinsic."), + }; + + // Warm up the DB + $crate::benchmarking::commit_db(); + $crate::benchmarking::wipe_db(); + + let components = >::components(&selected_benchmark); + let mut results: Vec<$crate::BenchmarkResults> = Vec::new(); + + // Default number of steps for a component. + let mut prev_steps = 10; + + // Select the component we will be benchmarking. Each component will be benchmarked. + for (idx, (name, low, high)) in components.iter().enumerate() { + // Get the number of steps for this component. + let steps = steps.get(idx).cloned().unwrap_or(prev_steps); + prev_steps = steps; + + let lowest = lowest_range_values.get(idx).cloned().unwrap_or(*low); + let highest = highest_range_values.get(idx).cloned().unwrap_or(*high); + + let diff = highest - lowest; + + // Create up to `STEPS` steps for that component between high and low. + let step_size = (diff / steps).max(1); + let num_of_steps = diff / step_size + 1; + + for s in 0..num_of_steps { + // This is the value we will be testing for component `name` + let component_value = lowest + step_size * s; + + // Select the max value for all the other components. + let c: Vec<($crate::BenchmarkParameter, u32)> = components.iter() + .enumerate() + .map(|(idx, (n, _, h))| + if n == name { + (*n, component_value) + } else { + (*n, *highest_range_values.get(idx).unwrap_or(h)) + } + ) + .collect(); + + // Run the benchmark `repeat` times. + for _ in 0..repeat { + // Set up the externalities environment for the setup we want to benchmark. + let closure_to_benchmark = >::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. + $crate::benchmarking::commit_db(); + + // Time the extrinsic logic. + let start_extrinsic = $crate::benchmarking::current_time(); + closure_to_benchmark()?; + let finish_extrinsic = $crate::benchmarking::current_time(); + let elapsed_extrinsic = finish_extrinsic - start_extrinsic; + + // Time the storage root recalculation. + let start_storage_root = $crate::benchmarking::current_time(); + $crate::storage_root(); + let finish_storage_root = $crate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + results.push((c.clone(), elapsed_extrinsic, elapsed_storage_root)); + + // Wipe the DB back to the genesis state. + $crate::benchmarking::wipe_db(); + } + } + } + return Ok(results); + } + } + } +} diff --git a/substrate/frame/benchmarking/src/tests.rs b/substrate/frame/benchmarking/src/tests.rs new file mode 100644 index 0000000000..d47488604e --- /dev/null +++ b/substrate/frame/benchmarking/src/tests.rs @@ -0,0 +1,169 @@ +// 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 . + +//! Tests for the module. + +#![cfg(test)] + +use super::*; +use codec::Decode; +use sp_std::prelude::*; +use sp_runtime::{ + traits::{Dispatchable, BlakeTwo256, IdentityLookup}, + testing::{H256, Header}, +}; +use frame_support::{dispatch::DispatchResult, decl_module, impl_outer_origin}; +use frame_system::{RawOrigin, ensure_signed, ensure_none}; + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn dummy(origin, _n: u32) -> DispatchResult { + let _sender = ensure_signed(origin)?; + Ok(()) + } + + fn other_dummy(origin, _n: u32) -> DispatchResult { + let _sender = ensure_none(origin)?; + Ok(()) + } + } +} + +impl_outer_origin! { + pub enum Origin for Test where system = frame_system {} +} + +pub trait Trait { + type Event; + type BlockNumber; + type AccountId: 'static + Default + Decode; + type Origin: From> + Into, Self::Origin>>; +} + +#[derive(Clone, Eq, PartialEq)] +pub struct Test; + +impl frame_system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = (); + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = (); + type MaximumBlockWeight = (); + type MaximumBlockLength = (); + type AvailableBlockRatio = (); + type Version = (); + type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); +} + +impl Trait for Test { + type Event = (); + type BlockNumber = u32; + type Origin = Origin; + type AccountId = u64; +} + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default().build_storage::().unwrap().into() +} + +benchmarks!{ + _ { + // Define a common range for `b`. + let b in 1 .. 1000 => (); + } + + dummy { + let b in ...; + let caller = account("caller", 0, 0); + }: _ (RawOrigin::Signed(caller), b.into()) + + other_name { + let b in ...; + let caller = account("caller", 0, 0); + }: other_dummy (RawOrigin::Signed(caller), b.into()) + + sort_vector { + let x in 0 .. 10000; + let mut m = Vec::::new(); + for i in 0..x { + m.push(i); + } + }: { + m.sort(); + } +} + +#[test] +fn benchmarks_macro_works() { + // Check benchmark creation for `dummy`. + let selected_benchmark = SelectedBenchmark::dummy; + + let components = >::components(&selected_benchmark); + assert_eq!(components, vec![(BenchmarkParameter::b, 1, 1000)]); + + let closure = >::instance( + &selected_benchmark, + &[(BenchmarkParameter::b, 1)], + ).expect("failed to create closure"); + + new_test_ext().execute_with(|| { + assert_eq!(closure(), Ok(())); + }); +} + +#[test] +fn benchmarks_macro_rename_works() { + // Check benchmark creation for `other_dummy`. + let selected_benchmark = SelectedBenchmark::other_name; + let components = >::components(&selected_benchmark); + assert_eq!(components, vec![(BenchmarkParameter::b, 1, 1000)]); + + let closure = >::instance( + &selected_benchmark, + &[(BenchmarkParameter::b, 1)], + ).expect("failed to create closure"); + + new_test_ext().execute_with(|| { + assert_eq!(closure(), Err("Bad origin")); + }); +} + +#[test] +fn benchmarks_macro_works_for_non_dispatchable() { + let selected_benchmark = SelectedBenchmark::sort_vector; + + let components = >::components(&selected_benchmark); + assert_eq!(components, vec![(BenchmarkParameter::x, 0, 10000)]); + + let closure = >::instance( + &selected_benchmark, + &[(BenchmarkParameter::x, 1)], + ).expect("failed to create closure"); + + assert_eq!(closure(), Ok(())); +} \ No newline at end of file diff --git a/substrate/frame/benchmarking/src/utils.rs b/substrate/frame/benchmarking/src/utils.rs index 87996c3f57..bc6cfbcc86 100644 --- a/substrate/frame/benchmarking/src/utils.rs +++ b/substrate/frame/benchmarking/src/utils.rs @@ -17,7 +17,7 @@ //! Interfaces, types and utils for benchmarking a FRAME runtime. use codec::{Encode, Decode}; -use sp_std::vec::Vec; +use sp_std::{vec::Vec, prelude::Box}; use sp_io::hashing::blake2_256; use sp_runtime::RuntimeString; @@ -93,12 +93,12 @@ pub trait Benchmarking { } /// The required setup for creating a benchmark. -pub trait BenchmarkingSetup { +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>; + /// Set up the storage, and prepare a closure to test in a single run of the benchmark. + fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result Result<(), &'static str>>, &'static str>; } /// Grab an account, seeded by a name and index. diff --git a/substrate/frame/example/Cargo.toml b/substrate/frame/example/Cargo.toml index 48c1fd57dc..515d1fd8d7 100644 --- a/substrate/frame/example/Cargo.toml +++ b/substrate/frame/example/Cargo.toml @@ -11,6 +11,7 @@ description = "FRAME example pallet" [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.2.0", default-features = false } +frame-benchmarking = { version = "2.0.0-alpha.2", default-features = false, path = "../benchmarking" } frame-support = { version = "2.0.0-alpha.2", default-features = false, path = "../support" } frame-system = { version = "2.0.0-alpha.2", default-features = false, path = "../system" } pallet-balances = { version = "2.0.0-alpha.2", default-features = false, path = "../balances" } @@ -27,6 +28,7 @@ std = [ "serde", "codec/std", "sp-runtime/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "pallet-balances/std", diff --git a/substrate/frame/example/src/lib.rs b/substrate/frame/example/src/lib.rs index f4139a310a..57d6c97729 100644 --- a/substrate/frame/example/src/lib.rs +++ b/substrate/frame/example/src/lib.rs @@ -258,10 +258,12 @@ use frame_support::{ dispatch::DispatchResult, decl_module, decl_storage, decl_event, weights::{SimpleDispatchInfo, DispatchInfo, DispatchClass, ClassifyDispatch, WeighData, Weight, PaysFee}, }; -use frame_system::{self as system, ensure_signed, ensure_root}; +use sp_std::prelude::*; +use frame_benchmarking::{benchmarks, account}; +use frame_system::{self as system, ensure_signed, ensure_root, RawOrigin}; use codec::{Encode, Decode}; use sp_runtime::{ - traits::{SignedExtension, Bounded, SaturatedConversion}, + traits::{SignedExtension, Bounded, SaturatedConversion, Dispatchable}, transaction_validity::{ ValidTransaction, TransactionValidityError, InvalidTransaction, TransactionValidity, }, @@ -642,6 +644,42 @@ impl SignedExtension for WatchDummy { } } +benchmarks!{ + _ { + // Define a common range for `b`. + let b in 1 .. 1000 => (); + } + + // This will measure the execution time of `accumulate_dummy` for b in [1..1000] range. + accumulate_dummy { + let b in ...; + let caller = account("caller", 0, 0); + }: _ (RawOrigin::Signed(caller), b.into()) + + // This will measure the execution time of `set_dummy` for b in [1..1000] range. + set_dummy { + let b in ...; + let caller = account("caller", 0, 0); + }: set_dummy (RawOrigin::Signed(caller), b.into()) + + // This will measure the execution time of `set_dummy` for b in [1..10] range. + another_set_dummy { + let b in 1 .. 10; + let caller = account("caller", 0, 0); + }: set_dummy (RawOrigin::Signed(caller), b.into()) + + // 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 { + m.push(i); + } + }: { + m.sort(); + } +} + #[cfg(test)] mod tests { use super::*;