11using System ;
22using System . Collections . Generic ;
33using System . Threading ;
4+ using BenchmarkDotNet . Helpers ;
45using BenchmarkDotNet . Jobs ;
56using BenchmarkDotNet . Portability ;
67using BenchmarkDotNet . Reports ;
@@ -27,27 +28,35 @@ internal sealed class EngineFirstJitStage : EngineJitStage
2728 // It is not worth spending a long time in jit stage for macro-benchmarks.
2829 private static readonly TimeInterval MaxTieringTime = TimeInterval . FromSeconds ( 10 ) ;
2930
31+ // Jit call counting delay is only for when the app starts up. We don't need to wait for every benchmark if multiple benchmarks are ran in-process.
32+ private static TimeSpan tieredDelay = JitInfo . TieredDelay ;
33+
3034 internal bool didStopEarly = false ;
3135 internal Measurement lastMeasurement ;
3236
3337 private readonly IEnumerator < IterationData > enumerator ;
38+ private readonly bool evaluateOverhead ;
3439
35- internal EngineFirstJitStage ( EngineParameters parameters ) : base ( parameters )
40+ internal EngineFirstJitStage ( bool evaluateOverhead , EngineParameters parameters ) : base ( parameters )
3641 {
3742 enumerator = EnumerateIterations ( ) ;
43+ this . evaluateOverhead = evaluateOverhead ;
3844 }
3945
4046 internal override List < Measurement > GetMeasurementList ( ) => new ( GetMaxMeasurementCount ( ) ) ;
4147
42- private static int GetMaxMeasurementCount ( )
48+ private int GetMaxMeasurementCount ( )
4349 {
44- int tieredCallCountThreshold = JitInfo . TieredCallCountThreshold ;
45- if ( JitInfo . IsDPGO )
50+ if ( ! JitInfo . IsTiered )
4651 {
47- tieredCallCountThreshold *= 2 ;
52+ return 1 ;
4853 }
49- // +1 for first jit, x2 for overhead + workload
50- return ( tieredCallCountThreshold + 1 ) * 2 ;
54+ int count = JitInfo . MaxTierPromotions * JitInfo . TieredCallCountThreshold + 2 ;
55+ if ( evaluateOverhead )
56+ {
57+ count *= 2 ;
58+ }
59+ return count ;
5160 }
5261
5362 internal override bool GetShouldRunIteration ( List < Measurement > measurements , out IterationData iterationData )
@@ -73,8 +82,11 @@ internal override bool GetShouldRunIteration(List<Measurement> measurements, out
7382 private IEnumerator < IterationData > EnumerateIterations ( )
7483 {
7584 ++ iterationIndex ;
76- yield return GetDummyIterationData ( dummy1Action ) ;
77- yield return GetOverheadIterationData ( ) ;
85+ if ( evaluateOverhead )
86+ {
87+ yield return GetDummyIterationData ( dummy1Action ) ;
88+ yield return GetOverheadIterationData ( ) ;
89+ }
7890 yield return GetDummyIterationData ( dummy2Action ) ;
7991 yield return GetWorkloadIterationData ( ) ;
8092 yield return GetDummyIterationData ( dummy3Action ) ;
@@ -86,12 +98,14 @@ private IEnumerator<IterationData> EnumerateIterations()
8698 }
8799
88100 // Wait enough time for jit call counting to begin.
89- MaybeSleep ( JitInfo . TieredDelay ) ;
101+ SleepHelper . SleepIfPositive ( tieredDelay ) ;
102+ // Don't make the next jit stage wait if it's ran in the same process.
103+ tieredDelay = TimeSpan . Zero ;
90104
91105 // Attempt to promote methods to tier1, but don't spend too much time in jit stage.
92106 StartedClock startedClock = parameters . TargetJob . ResolveValue ( InfrastructureMode . ClockCharacteristic , parameters . Resolver ) . Start ( ) ;
93107
94- int remainingTiers = JitInfo . IsDPGO ? 2 : 1 ;
108+ int remainingTiers = JitInfo . MaxTierPromotions ;
95109 while ( remainingTiers > 0 )
96110 {
97111 -- remainingTiers ;
@@ -100,7 +114,10 @@ private IEnumerator<IterationData> EnumerateIterations()
100114 {
101115 -- remainingCalls ;
102116 ++ iterationIndex ;
103- yield return GetOverheadIterationData ( ) ;
117+ if ( evaluateOverhead )
118+ {
119+ yield return GetOverheadIterationData ( ) ;
120+ }
104121 yield return GetWorkloadIterationData ( ) ;
105122
106123 if ( ( remainingTiers + remainingCalls ) > 0
@@ -111,13 +128,16 @@ private IEnumerator<IterationData> EnumerateIterations()
111128 }
112129 }
113130
114- MaybeSleep ( JitInfo . BackgroundCompilationDelay ) ;
131+ SleepHelper . SleepIfPositive ( JitInfo . BackgroundCompilationDelay ) ;
115132 }
116133
117- // Empirical evidence shows that the first call after the method is tiered up takes longer,
134+ // Empirical evidence shows that the first call after the method is tiered up may take longer,
118135 // so we run an extra iteration to ensure the next stage gets a stable measurement.
119136 ++ iterationIndex ;
120- yield return GetOverheadIterationData ( ) ;
137+ if ( evaluateOverhead )
138+ {
139+ yield return GetOverheadIterationData ( ) ;
140+ }
121141 yield return GetWorkloadIterationData ( ) ;
122142 }
123143
@@ -126,32 +146,22 @@ private IterationData GetOverheadIterationData()
126146
127147 private IterationData GetWorkloadIterationData ( )
128148 => new ( IterationMode . Workload , IterationStage . Jitting , iterationIndex , 1 , 1 , parameters . IterationSetupAction , parameters . IterationCleanupAction , parameters . WorkloadActionNoUnroll ) ;
129-
130- private static void MaybeSleep ( TimeSpan timeSpan )
131- {
132- if ( timeSpan > TimeSpan . Zero )
133- {
134- Thread . Sleep ( timeSpan ) ;
135- }
136- }
137149 }
138150
139- internal sealed class EngineSecondJitStage ( int unrollFactor , EngineParameters parameters ) : EngineJitStage ( parameters )
151+ internal sealed class EngineSecondJitStage : EngineJitStage
140152 {
141- private readonly int unrollFactor = unrollFactor ;
153+ private readonly int unrollFactor ;
154+ private readonly bool evaluateOverhead ;
142155
143- internal override List < Measurement > GetMeasurementList ( ) => new ( GetMaxCallCount ( ) ) ;
144-
145- private static int GetMaxCallCount ( )
156+ public EngineSecondJitStage ( int unrollFactor , bool evaluateOverhead , EngineParameters parameters ) : base ( parameters )
146157 {
147- int tieredCallCountThreshold = JitInfo . TieredCallCountThreshold ;
148- if ( JitInfo . IsDPGO )
149- {
150- tieredCallCountThreshold *= 2 ;
151- }
152- return tieredCallCountThreshold + 1 ;
158+ this . unrollFactor = unrollFactor ;
159+ this . evaluateOverhead = evaluateOverhead ;
160+ iterationIndex = evaluateOverhead ? 0 : 2 ;
153161 }
154162
163+ internal override List < Measurement > GetMeasurementList ( ) => new ( evaluateOverhead ? 5 : 3 ) ;
164+
155165 // The benchmark method has already been jitted via *NoUnroll, we only need to jit the *Unroll methods here, which aren't tiered.
156166 internal override bool GetShouldRunIteration ( List < Measurement > measurements , out IterationData iterationData )
157167 {
0 commit comments