2020use super :: * ;
2121use crate :: { Pallet as MultiPhase , unsigned:: IndexAssignmentOf } ;
2222use frame_benchmarking:: { account, impl_benchmark_test_suite} ;
23- use frame_support:: { assert_ok, traits:: OnInitialize } ;
23+ use frame_support:: { assert_ok, traits:: Hooks } ;
2424use frame_system:: RawOrigin ;
2525use rand:: { prelude:: SliceRandom , rngs:: SmallRng , SeedableRng } ;
26- use frame_election_provider_support:: Assignment ;
2726use sp_arithmetic:: { per_things:: Percent , traits:: One } ;
2827use sp_npos_elections:: IndexAssignment ;
2928use sp_runtime:: InnerOf ;
@@ -38,14 +37,14 @@ fn solution_with_size<T: Config>(
3837 size : SolutionOrSnapshotSize ,
3938 active_voters_count : u32 ,
4039 desired_targets : u32 ,
41- ) -> RawSolution < CompactOf < T > > {
42- assert ! ( size. targets >= desired_targets, "must have enough targets" ) ;
43- assert ! (
40+ ) -> Result < RawSolution < CompactOf < T > > , & ' static str > {
41+ ensure ! ( size. targets >= desired_targets, "must have enough targets" ) ;
42+ ensure ! (
4443 size. targets >= ( <CompactOf <T >>:: LIMIT * 2 ) as u32 ,
4544 "must have enough targets for unique votes."
4645 ) ;
47- assert ! ( size. voters >= active_voters_count, "must have enough voters" ) ;
48- assert ! (
46+ ensure ! ( size. voters >= active_voters_count, "must have enough voters" ) ;
47+ ensure ! (
4948 ( <CompactOf <T >>:: LIMIT as u32 ) < desired_targets,
5049 "must have enough winners to give them votes."
5150 ) ;
@@ -125,7 +124,7 @@ fn solution_with_size<T: Config>(
125124 . map ( |( voter, _stake, votes) | {
126125 let percent_per_edge: InnerOf < CompactAccuracyOf < T > > =
127126 ( 100 / votes. len ( ) ) . try_into ( ) . unwrap_or_else ( |_| panic ! ( "failed to convert" ) ) ;
128- Assignment {
127+ crate :: unsigned :: Assignment :: < T > {
129128 who : voter. clone ( ) ,
130129 distribution : votes
131130 . iter ( )
@@ -141,7 +140,31 @@ fn solution_with_size<T: Config>(
141140 let round = <MultiPhase < T > >:: round ( ) ;
142141
143142 assert ! ( score[ 0 ] > 0 , "score is zero, this probably means that the stakes are not set." ) ;
144- RawSolution { compact, score, round }
143+ Ok ( RawSolution { compact, score, round } )
144+ }
145+
146+ fn set_up_data_provider < T : Config > ( v : u32 , t : u32 ) {
147+ // number of votes in snapshot.
148+
149+ T :: DataProvider :: clear ( ) ;
150+ log ! ( info, "setting up with voters = {} [degree = {}], targets = {}" , v, T :: DataProvider :: MAXIMUM_VOTES_PER_VOTER , t) ;
151+
152+ // fill targets.
153+ let mut targets = ( 0 ..t) . map ( |i| {
154+ let target = frame_benchmarking:: account :: < T :: AccountId > ( "Target" , i, SEED ) ;
155+ T :: DataProvider :: add_target ( target. clone ( ) ) ;
156+ target
157+ } ) . collect :: < Vec < _ > > ( ) ;
158+ // we should always have enough voters to fill.
159+ assert ! ( targets. len( ) > T :: DataProvider :: MAXIMUM_VOTES_PER_VOTER as usize ) ;
160+ targets. truncate ( T :: DataProvider :: MAXIMUM_VOTES_PER_VOTER as usize ) ;
161+
162+ // fill voters.
163+ ( 0 ..v) . for_each ( |i| {
164+ let voter = frame_benchmarking:: account :: < T :: AccountId > ( "Voter" , i, SEED ) ;
165+ let weight = T :: Currency :: minimum_balance ( ) . saturated_into :: < u64 > ( ) * 1000 ;
166+ T :: DataProvider :: add_voter ( voter, weight, targets. clone ( ) ) ;
167+ } ) ;
145168}
146169
147170frame_benchmarking:: benchmarks! {
@@ -223,14 +246,18 @@ frame_benchmarking::benchmarks! {
223246
224247 // a call to `<Pallet as ElectionProvider>::elect` where we only return the queued solution.
225248 elect_queued {
226- // assume largest values for the election status. These will merely affect the decoding.
227- let v = T :: BenchmarkingConfig :: VOTERS [ 1 ] ;
228- let t = T :: BenchmarkingConfig :: TARGETS [ 1 ] ;
229- let a = T :: BenchmarkingConfig :: ACTIVE_VOTERS [ 1 ] ;
230- let d = T :: BenchmarkingConfig :: DESIRED_TARGETS [ 1 ] ;
249+ // number of votes in snapshot.
250+ let v in ( T :: BenchmarkingConfig :: VOTERS [ 0 ] ) .. T :: BenchmarkingConfig :: VOTERS [ 1 ] ;
251+ // number of targets in snapshot.
252+ let t in ( T :: BenchmarkingConfig :: TARGETS [ 0 ] ) .. T :: BenchmarkingConfig :: TARGETS [ 1 ] ;
253+ // number of assignments, i.e. compact.len(). This means the active nominators, thus must be
254+ // a subset of `v` component.
255+ let a in ( T :: BenchmarkingConfig :: ACTIVE_VOTERS [ 0 ] ) .. T :: BenchmarkingConfig :: ACTIVE_VOTERS [ 1 ] ;
256+ // number of desired targets. Must be a subset of `t` component.
257+ let d in ( T :: BenchmarkingConfig :: DESIRED_TARGETS [ 0 ] ) .. T :: BenchmarkingConfig :: DESIRED_TARGETS [ 1 ] ;
231258
232259 let witness = SolutionOrSnapshotSize { voters: v, targets: t } ;
233- let raw_solution = solution_with_size:: <T >( witness, a, d) ;
260+ let raw_solution = solution_with_size:: <T >( witness, a, d) ? ;
234261 let ready_solution =
235262 <MultiPhase <T >>:: feasibility_check( raw_solution, ElectionCompute :: Signed ) . unwrap( ) ;
236263
@@ -251,15 +278,6 @@ frame_benchmarking::benchmarks! {
251278 assert_eq!( <CurrentPhase <T >>:: get( ) , <Phase <T :: BlockNumber >>:: Off ) ;
252279 }
253280
254- #[ extra]
255- create_snapshot {
256- assert!( <MultiPhase <T >>:: snapshot( ) . is_none( ) ) ;
257- } : {
258- <MultiPhase :: <T >>:: create_snapshot( ) . unwrap( )
259- } verify {
260- assert!( <MultiPhase <T >>:: snapshot( ) . is_some( ) ) ;
261- }
262-
263281 submit {
264282 let c in 1 .. ( T :: SignedMaxSubmissions :: get( ) - 1 ) ;
265283
@@ -307,7 +325,7 @@ frame_benchmarking::benchmarks! {
307325 T :: BenchmarkingConfig :: DESIRED_TARGETS [ 1 ] ;
308326
309327 let witness = SolutionOrSnapshotSize { voters: v, targets: t } ;
310- let raw_solution = solution_with_size:: <T >( witness, a, d) ;
328+ let raw_solution = solution_with_size:: <T >( witness, a, d) ? ;
311329
312330 assert!( <MultiPhase <T >>:: queued_solution( ) . is_none( ) ) ;
313331 <CurrentPhase <T >>:: put( Phase :: Unsigned ( ( true , 1u32 . into( ) ) ) ) ;
@@ -324,6 +342,84 @@ frame_benchmarking::benchmarks! {
324342 assert!( <MultiPhase <T >>:: queued_solution( ) . is_some( ) ) ;
325343 }
326344
345+ // This is checking a valid solution. The worse case is indeed a valid solution.
346+ feasibility_check {
347+ // number of votes in snapshot.
348+ let v in ( T :: BenchmarkingConfig :: VOTERS [ 0 ] ) .. T :: BenchmarkingConfig :: VOTERS [ 1 ] ;
349+ // number of targets in snapshot.
350+ let t in ( T :: BenchmarkingConfig :: TARGETS [ 0 ] ) .. T :: BenchmarkingConfig :: TARGETS [ 1 ] ;
351+ // number of assignments, i.e. compact.len(). This means the active nominators, thus must be
352+ // a subset of `v` component.
353+ let a in ( T :: BenchmarkingConfig :: ACTIVE_VOTERS [ 0 ] ) .. T :: BenchmarkingConfig :: ACTIVE_VOTERS [ 1 ] ;
354+ // number of desired targets. Must be a subset of `t` component.
355+ let d in ( T :: BenchmarkingConfig :: DESIRED_TARGETS [ 0 ] ) .. T :: BenchmarkingConfig :: DESIRED_TARGETS [ 1 ] ;
356+
357+ let size = SolutionOrSnapshotSize { voters: v, targets: t } ;
358+ let raw_solution = solution_with_size:: <T >( size, a, d) ?;
359+
360+ assert_eq!( raw_solution. compact. voter_count( ) as u32 , a) ;
361+ assert_eq!( raw_solution. compact. unique_targets( ) . len( ) as u32 , d) ;
362+
363+ // encode the most significant storage item that needs to be decoded in the dispatch.
364+ let encoded_snapshot = <MultiPhase <T >>:: snapshot( ) . unwrap( ) . encode( ) ;
365+ } : {
366+ assert_ok!( <MultiPhase <T >>:: feasibility_check( raw_solution, ElectionCompute :: Unsigned ) ) ;
367+ let _decoded_snap = <RoundSnapshot <T :: AccountId > as Decode >:: decode( & mut & * encoded_snapshot) . unwrap( ) ;
368+ }
369+
370+ // NOTE: this weight is not used anywhere, but the fact that it should succeed when execution in
371+ // isolation is vital to ensure memory-safety. For the same reason, we don't care about the
372+ // components iterating, we merely check that this operation will work with the "maximum"
373+ // numbers.
374+ //
375+ // ONLY run this benchmark in isolation, and pass the `--extra` flag to enable it.
376+ //
377+ // NOTE: If this benchmark does not run out of memory with a given heap pages, it means that the
378+ // OCW process can SURELY succeed with the given configuration, but the opposite is not true.
379+ // This benchmark is doing more work than a raw call to `OffchainWorker_offchain_worker` runtime
380+ // api call, since it is also setting up some mock data, which will itself exhaust the heap to
381+ // some extent.
382+ #[ extra]
383+ mine_solution_offchain_memory {
384+ // number of votes in snapshot. Fixed to maximum.
385+ let v = T :: BenchmarkingConfig :: MINER_MAXIMUM_VOTERS ;
386+ // number of targets in snapshot. Fixed to maximum.
387+ let t = T :: BenchmarkingConfig :: MAXIMUM_TARGETS ;
388+
389+ T :: DataProvider :: clear( ) ;
390+ set_up_data_provider:: <T >( v, t) ;
391+ let now = frame_system:: Pallet :: <T >:: block_number( ) ;
392+ <CurrentPhase <T >>:: put( Phase :: Unsigned ( ( true , now) ) ) ;
393+ <MultiPhase :: <T >>:: create_snapshot( ) . unwrap( ) ;
394+ } : {
395+ // we can't really verify this as it won't write anything to state, check logs.
396+ <MultiPhase :: <T >>:: offchain_worker( now)
397+ }
398+
399+ // NOTE: this weight is not used anywhere, but the fact that it should succeed when execution in
400+ // isolation is vital to ensure memory-safety. For the same reason, we don't care about the
401+ // components iterating, we merely check that this operation will work with the "maximum"
402+ // numbers.
403+ //
404+ // ONLY run this benchmark in isolation, and pass the `--extra` flag to enable it.
405+ #[ extra]
406+ create_snapshot_memory {
407+ // number of votes in snapshot. Fixed to maximum.
408+ let v = T :: BenchmarkingConfig :: SNAPSHOT_MAXIMUM_VOTERS ;
409+ // number of targets in snapshot. Fixed to maximum.
410+ let t = T :: BenchmarkingConfig :: MAXIMUM_TARGETS ;
411+
412+ T :: DataProvider :: clear( ) ;
413+ set_up_data_provider:: <T >( v, t) ;
414+ assert!( <MultiPhase <T >>:: snapshot( ) . is_none( ) ) ;
415+ } : {
416+ <MultiPhase :: <T >>:: create_snapshot( ) . unwrap( )
417+ } verify {
418+ assert!( <MultiPhase <T >>:: snapshot( ) . is_some( ) ) ;
419+ assert_eq!( <MultiPhase <T >>:: snapshot_metadata( ) . unwrap( ) . voters, v + t) ;
420+ assert_eq!( <MultiPhase <T >>:: snapshot_metadata( ) . unwrap( ) . targets, t) ;
421+ }
422+
327423 #[ extra]
328424 trim_assignments_length {
329425 // number of votes in snapshot.
@@ -344,7 +440,7 @@ frame_benchmarking::benchmarks! {
344440 // Compute a random solution, then work backwards to get the lists of voters, targets, and
345441 // assignments
346442 let witness = SolutionOrSnapshotSize { voters: v, targets: t } ;
347- let RawSolution { compact, .. } = solution_with_size:: <T >( witness, a, d) ;
443+ let RawSolution { compact, .. } = solution_with_size:: <T >( witness, a, d) ? ;
348444 let RoundSnapshot { voters, targets } = MultiPhase :: <T >:: snapshot( ) . unwrap( ) ;
349445 let voter_at = helpers:: voter_at_fn:: <T >( & voters) ;
350446 let target_at = helpers:: target_at_fn:: <T >( & targets) ;
@@ -394,39 +490,10 @@ frame_benchmarking::benchmarks! {
394490 log!( trace, "actual encoded size = {}" , encoding. len( ) ) ;
395491 assert!( encoding. len( ) <= desired_size) ;
396492 }
397-
398- // This is checking a valid solution. The worse case is indeed a valid solution.
399- feasibility_check {
400- // number of votes in snapshot.
401- let v in ( T :: BenchmarkingConfig :: VOTERS [ 0 ] ) .. T :: BenchmarkingConfig :: VOTERS [ 1 ] ;
402- // number of targets in snapshot.
403- let t in ( T :: BenchmarkingConfig :: TARGETS [ 0 ] ) .. T :: BenchmarkingConfig :: TARGETS [ 1 ] ;
404- // number of assignments, i.e. compact.len(). This means the active nominators, thus must be
405- // a subset of `v` component.
406- let a in
407- ( T :: BenchmarkingConfig :: ACTIVE_VOTERS [ 0 ] ) .. T :: BenchmarkingConfig :: ACTIVE_VOTERS [ 1 ] ;
408- // number of desired targets. Must be a subset of `t` component.
409- let d in
410- ( T :: BenchmarkingConfig :: DESIRED_TARGETS [ 0 ] ) ..
411- T :: BenchmarkingConfig :: DESIRED_TARGETS [ 1 ] ;
412-
413- let size = SolutionOrSnapshotSize { voters: v, targets: t } ;
414- let raw_solution = solution_with_size:: <T >( size, a, d) ;
415-
416- assert_eq!( raw_solution. compact. voter_count( ) as u32 , a) ;
417- assert_eq!( raw_solution. compact. unique_targets( ) . len( ) as u32 , d) ;
418-
419- // encode the most significant storage item that needs to be decoded in the dispatch.
420- let encoded_snapshot = <MultiPhase <T >>:: snapshot( ) . unwrap( ) . encode( ) ;
421- } : {
422- assert_ok!( <MultiPhase <T >>:: feasibility_check( raw_solution, ElectionCompute :: Unsigned ) ) ;
423- let _decoded_snap = <RoundSnapshot <T :: AccountId > as Decode >:: decode( & mut & * encoded_snapshot)
424- . unwrap( ) ;
425- }
426493}
427494
428495impl_benchmark_test_suite ! (
429496 MultiPhase ,
430- crate :: mock:: ExtBuilder :: default ( ) . build ( ) ,
497+ crate :: mock:: ExtBuilder :: default ( ) . build_offchainify ( 10 ) . 0 ,
431498 crate :: mock:: Runtime ,
432499) ;
0 commit comments