77using BenchmarkDotNet . Jobs ;
88using BenchmarkDotNet . Running ;
99using JetBrains . Annotations ;
10+ using Perfolizer . Horology ;
1011using Xunit ;
1112
1213namespace BenchmarkDotNet . Tests . Engine
@@ -33,6 +34,14 @@ private void VeryTimeConsumingSingle(long _)
3334 Thread . Sleep ( IterationTime ) ;
3435 }
3536
37+ private void TimeConsumingOnlyForTheFirstCall ( long _ )
38+ {
39+ if ( timesBenchmarkCalled ++ == 0 )
40+ {
41+ Thread . Sleep ( IterationTime ) ;
42+ }
43+ }
44+
3645 private void InstantNoUnroll ( long invocationCount ) => timesBenchmarkCalled += ( int ) invocationCount ;
3746 private void InstantUnroll ( long _ ) => timesBenchmarkCalled += 16 ;
3847
@@ -78,10 +87,10 @@ public void ForDefaultSettingsVeryTimeConsumingBenchmarksAreExecutedOncePerItera
7887 var engine = new EngineFactory ( ) . CreateReadyToRun ( engineParameters ) ;
7988
8089 Assert . Equal ( 1 , timesGlobalSetupCalled ) ;
81- Assert . Equal ( 1 , timesIterationSetupCalled ) ; // 1x for Target
82- Assert . Equal ( 1 , timesBenchmarkCalled ) ;
83- Assert . Equal ( 1 , timesOverheadCalled ) ;
84- Assert . Equal ( 1 , timesIterationCleanupCalled ) ; // 1x for Target
90+ Assert . Equal ( 2 , timesIterationSetupCalled ) ; // 2x for Target
91+ Assert . Equal ( 2 , timesBenchmarkCalled ) ;
92+ Assert . Equal ( 2 , timesOverheadCalled ) ;
93+ Assert . Equal ( 2 , timesIterationCleanupCalled ) ; // 2x for Target
8594 Assert . Equal ( 0 , timesGlobalCleanupCalled ) ; // cleanup is called as part of dispose
8695
8796 Assert . Equal ( 1 , engine . TargetJob . Run . InvocationCount ) ; // call the benchmark once per iteration
@@ -95,6 +104,39 @@ public void ForDefaultSettingsVeryTimeConsumingBenchmarksAreExecutedOncePerItera
95104 Assert . Equal ( 1 , timesGlobalCleanupCalled ) ;
96105 }
97106
107+ [ Theory ]
108+ [ InlineData ( 120 ) ] // 120 ms as in the bug report
109+ [ InlineData ( 250 ) ] // 250 ms as configured in dotnet/performance repo
110+ [ InlineData ( EngineResolver . DefaultIterationTime ) ] // 500 ms - the default BDN setting
111+ public void BenchmarksThatRunLongerThanIterationTimeOnlyDuringFirstInvocationAreNotInvokedOncePerIteration ( int iterationTime )
112+ {
113+ var engineParameters = CreateEngineParameters (
114+ mainNoUnroll : TimeConsumingOnlyForTheFirstCall ,
115+ mainUnroll : InstantUnroll ,
116+ job : Job . Default . WithIterationTime ( TimeInterval . FromMilliseconds ( iterationTime ) ) ) ;
117+
118+ var engine = new EngineFactory ( ) . CreateReadyToRun ( engineParameters ) ;
119+
120+ Assert . Equal ( 1 , timesGlobalSetupCalled ) ;
121+ // the factory should call the benchmark:
122+ // 1st time with unroll factor to JIT the code
123+ // one more to check that the Jitting has not dominated the reported time
124+ // and one more time to JIT the 16 unroll factor case as it turned out that Jitting has dominated the time
125+ Assert . Equal ( 1 + 1 + 1 , timesIterationSetupCalled ) ;
126+ Assert . Equal ( 1 + 1 + 16 , timesBenchmarkCalled ) ;
127+ Assert . Equal ( 1 + 1 + 16 , timesOverheadCalled ) ;
128+ Assert . Equal ( 1 + 1 + 1 , timesIterationCleanupCalled ) ; // 2x for Target
129+ Assert . Equal ( 0 , timesGlobalCleanupCalled ) ; // cleanup is called as part of dispose
130+
131+ Assert . False ( engine . TargetJob . Run . HasValue ( RunMode . InvocationCountCharacteristic ) ) ; // we need pilot stage
132+
133+ Assert . False ( engine . TargetJob . Run . HasValue ( AccuracyMode . EvaluateOverheadCharacteristic ) ) ;
134+
135+ engine . Dispose ( ) ; // cleanup is called as part of dispose
136+
137+ Assert . Equal ( 1 , timesGlobalCleanupCalled ) ;
138+ }
139+
98140 [ Fact ]
99141 public void ForJobsWithExplicitUnrollFactorTheGlobalSetupIsCalledAndMultiActionCodeGetsJitted ( )
100142 => AssertGlobalSetupWasCalledAndMultiActionGotJitted ( Job . Default . WithUnrollFactor ( 16 ) ) ;
0 commit comments