@@ -105,14 +105,41 @@ public void Dispose()
105
105
}
106
106
}
107
107
108
+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
109
+ private IEnumerable < ( IterationStage stage , IterationMode mode , IEngineStageEvaluator evaluator ) > EnumerateStages ( )
110
+ {
111
+ if ( Strategy != RunStrategy . ColdStart )
112
+ {
113
+ if ( Strategy != RunStrategy . Monitoring )
114
+ {
115
+ var pilotEvaluator = pilotStage . GetEvaluator ( ) ;
116
+ if ( pilotEvaluator != null )
117
+ {
118
+ yield return ( IterationStage . Pilot , IterationMode . Workload , pilotEvaluator ) ;
119
+ }
120
+
121
+ if ( EvaluateOverhead )
122
+ {
123
+ yield return ( IterationStage . Warmup , IterationMode . Overhead , warmupStage . GetOverheadEvaluator ( ) ) ;
124
+ yield return ( IterationStage . Actual , IterationMode . Overhead , actualStage . GetOverheadEvaluator ( ) ) ;
125
+ }
126
+ }
127
+
128
+ yield return ( IterationStage . Warmup , IterationMode . Workload , warmupStage . GetWorkloadEvaluator ( Strategy ) ) ;
129
+ }
130
+
131
+ Host . BeforeMainRun ( ) ;
132
+
133
+ yield return ( IterationStage . Actual , IterationMode . Workload , actualStage . GetWorkloadEvaluator ( Strategy == RunStrategy . Monitoring ) ) ;
134
+
135
+ Host . AfterMainRun ( ) ;
136
+ }
137
+
108
138
// AggressiveOptimization forces the method to go straight to tier1 JIT, and will never be re-jitted,
109
139
// eliminating tiered JIT as a potential variable in measurements.
110
140
[ MethodImpl ( CodeGenHelper . AggressiveOptimizationOption ) ]
111
141
public RunResults Run ( )
112
142
{
113
- // This method is huge, because all stages are inlined. This ensures the stack size
114
- // remains constant for each benchmark invocation, eliminating stack sizes as a potential variable in measurements.
115
- // #1120
116
143
var measurements = new List < Measurement > ( ) ;
117
144
measurements . AddRange ( jittingMeasurements ) ;
118
145
@@ -121,191 +148,23 @@ public RunResults Run()
121
148
if ( EngineEventSource . Log . IsEnabled ( ) )
122
149
EngineEventSource . Log . BenchmarkStart ( BenchmarkName ) ;
123
150
124
- if ( Strategy != RunStrategy . ColdStart )
125
- {
126
- if ( Strategy != RunStrategy . Monitoring )
127
- {
128
- // Pilot Stage
129
- {
130
- // If InvocationCount is specified, pilot stage should be skipped
131
- if ( TargetJob . HasValue ( RunMode . InvocationCountCharacteristic ) )
132
- {
133
- }
134
- // Here we want to guess "perfect" amount of invocation
135
- else if ( TargetJob . HasValue ( RunMode . IterationTimeCharacteristic ) )
136
- {
137
- // Perfect invocation count
138
- invokeCount = pilotStage . Autocorrect ( MinInvokeCount ) ;
139
-
140
- int iterationCounter = 0 ;
141
-
142
- int downCount = 0 ; // Amount of iterations where newInvokeCount < invokeCount
143
- while ( true )
144
- {
145
- iterationCounter ++ ;
146
- var measurement = RunIteration ( new IterationData ( IterationMode . Workload , IterationStage . Pilot , iterationCounter , invokeCount , UnrollFactor ) ) ;
147
- measurements . Add ( measurement ) ;
148
- double actualIterationTime = measurement . Nanoseconds ;
149
- long newInvokeCount = pilotStage . Autocorrect ( Math . Max ( pilotStage . minInvokeCount , ( long ) Math . Round ( invokeCount * pilotStage . targetIterationTime / actualIterationTime ) ) ) ;
150
-
151
- if ( newInvokeCount < invokeCount )
152
- downCount ++ ;
153
-
154
- if ( Math . Abs ( newInvokeCount - invokeCount ) <= 1 || downCount >= 3 )
155
- break ;
156
-
157
- invokeCount = newInvokeCount ;
158
- }
159
- WriteLine ( ) ;
160
- }
161
- else
162
- {
163
- // A case where we don't have specific iteration time.
164
- invokeCount = pilotStage . Autocorrect ( pilotStage . minInvokeCount ) ;
165
-
166
- int iterationCounter = 0 ;
167
- while ( true )
168
- {
169
- iterationCounter ++ ;
170
- var measurement = RunIteration ( new IterationData ( IterationMode . Workload , IterationStage . Pilot , iterationCounter , invokeCount , UnrollFactor ) ) ;
171
- measurements . Add ( measurement ) ;
172
- double iterationTime = measurement . Nanoseconds ;
173
- double operationError = 2.0 * pilotStage . resolution / invokeCount ; // An operation error which has arisen due to the Chronometer precision
174
-
175
- // Max acceptable operation error
176
- double operationMaxError1 = iterationTime / invokeCount * pilotStage . maxRelativeError ;
177
- double operationMaxError2 = pilotStage . maxAbsoluteError ? . Nanoseconds ?? double . MaxValue ;
178
- double operationMaxError = Math . Min ( operationMaxError1 , operationMaxError2 ) ;
179
-
180
- bool isFinished = operationError < operationMaxError && iterationTime >= pilotStage . minIterationTime . Nanoseconds ;
181
- if ( isFinished )
182
- break ;
183
- if ( invokeCount >= EnginePilotStage . MaxInvokeCount )
184
- break ;
185
-
186
- if ( UnrollFactor == 1 && invokeCount < EnvironmentResolver . DefaultUnrollFactorForThroughput )
187
- invokeCount += 1 ;
188
- else
189
- invokeCount *= 2 ;
190
- }
191
- WriteLine ( ) ;
192
- }
193
- }
194
- // End Pilot Stage
195
-
196
- if ( EvaluateOverhead )
197
- {
198
- // Warmup Overhead
199
- {
200
- var warmupMeasurements = new List < Measurement > ( ) ;
201
-
202
- var criteria = DefaultStoppingCriteriaFactory . Instance . CreateWarmup ( TargetJob , Resolver , IterationMode . Overhead , RunStrategy . Throughput ) ;
203
- int iterationCounter = 0 ;
204
- while ( ! criteria . Evaluate ( warmupMeasurements ) . IsFinished )
205
- {
206
- iterationCounter ++ ;
207
- warmupMeasurements . Add ( RunIteration ( new IterationData ( IterationMode . Overhead , IterationStage . Warmup , iterationCounter , invokeCount , UnrollFactor ) ) ) ;
208
- }
209
- WriteLine ( ) ;
210
-
211
- measurements . AddRange ( warmupMeasurements ) ;
212
- }
213
- // End Warmup Overhead
214
-
215
- // Actual Overhead
216
- {
217
- var measurementsForStatistics = new List < Measurement > ( actualStage . maxIterationCount ) ;
218
-
219
- int iterationCounter = 0 ;
220
- double effectiveMaxRelativeError = EngineActualStage . MaxOverheadRelativeError ;
221
- while ( true )
222
- {
223
- iterationCounter ++ ;
224
- var measurement = RunIteration ( new IterationData ( IterationMode . Overhead , IterationStage . Actual , iterationCounter , invokeCount , UnrollFactor ) ) ;
225
- measurements . Add ( measurement ) ;
226
- measurementsForStatistics . Add ( measurement ) ;
227
-
228
- var statistics = MeasurementsStatistics . Calculate ( measurementsForStatistics , actualStage . outlierMode ) ;
229
- double actualError = statistics . LegacyConfidenceInterval . Margin ;
230
-
231
- double maxError1 = effectiveMaxRelativeError * statistics . Mean ;
232
- double maxError2 = actualStage . maxAbsoluteError ? . Nanoseconds ?? double . MaxValue ;
233
- double maxError = Math . Min ( maxError1 , maxError2 ) ;
234
-
235
- if ( iterationCounter >= actualStage . minIterationCount && actualError < maxError )
236
- break ;
237
-
238
- if ( iterationCounter >= actualStage . maxIterationCount || iterationCounter >= EngineActualStage . MaxOverheadIterationCount )
239
- break ;
240
- }
241
- WriteLine ( ) ;
242
- }
243
- // End Actual Overhead
244
- }
245
- }
246
-
247
- // Warmup Workload
248
- {
249
- var workloadMeasurements = new List < Measurement > ( ) ;
250
-
251
- var criteria = DefaultStoppingCriteriaFactory . Instance . CreateWarmup ( TargetJob , Resolver , IterationMode . Workload , Strategy ) ;
252
- int iterationCounter = 0 ;
253
- while ( ! criteria . Evaluate ( workloadMeasurements ) . IsFinished )
254
- {
255
- iterationCounter ++ ;
256
- workloadMeasurements . Add ( RunIteration ( new IterationData ( IterationMode . Workload , IterationStage . Warmup , iterationCounter , invokeCount , UnrollFactor ) ) ) ;
257
- }
258
- WriteLine ( ) ;
259
-
260
- measurements . AddRange ( workloadMeasurements ) ;
261
- }
262
- // End Warmup Workload
263
- }
264
-
265
- Host . BeforeMainRun ( ) ;
151
+ // Enumerate the stages and run iterations in a loop to ensure each benchmark invocation is called with a constant stack size.
152
+ // #1120
153
+ foreach ( var ( stage , mode , evaluator ) in EnumerateStages ( ) )
154
+ {
155
+ var stageMeasurements = new List < Measurement > ( evaluator . MaxIterationCount ) ;
156
+ int iterationCounter = 0 ;
157
+ while ( ! evaluator . EvaluateShouldStop ( stageMeasurements , ref invokeCount ) )
158
+ {
159
+ // TODO: Not sure why index is 1-based? 0-based is standard.
160
+ ++ iterationCounter ;
161
+ var measurement = RunIteration ( new IterationData ( mode , stage , iterationCounter , invokeCount , UnrollFactor ) ) ;
162
+ stageMeasurements . Add ( measurement ) ;
163
+ }
164
+ measurements . AddRange ( stageMeasurements ) ;
266
165
267
- // Actual Workload
268
- {
269
- if ( actualStage . iterationCount == null && Strategy != RunStrategy . Monitoring )
270
- {
271
- // RunAuto
272
- var measurementsForStatistics = new List < Measurement > ( actualStage . maxIterationCount ) ;
273
-
274
- int iterationCounter = 0 ;
275
- double effectiveMaxRelativeError = actualStage . maxRelativeError ;
276
- while ( true )
277
- {
278
- iterationCounter ++ ;
279
- var measurement = RunIteration ( new IterationData ( IterationMode . Workload , IterationStage . Actual , iterationCounter , invokeCount , UnrollFactor ) ) ;
280
- measurements . Add ( measurement ) ;
281
- measurementsForStatistics . Add ( measurement ) ;
282
-
283
- var statistics = MeasurementsStatistics . Calculate ( measurementsForStatistics , actualStage . outlierMode ) ;
284
- double actualError = statistics . LegacyConfidenceInterval . Margin ;
285
-
286
- double maxError1 = effectiveMaxRelativeError * statistics . Mean ;
287
- double maxError2 = actualStage . maxAbsoluteError ? . Nanoseconds ?? double . MaxValue ;
288
- double maxError = Math . Min ( maxError1 , maxError2 ) ;
289
-
290
- if ( iterationCounter >= actualStage . minIterationCount && actualError < maxError )
291
- break ;
292
-
293
- if ( iterationCounter >= actualStage . maxIterationCount )
294
- break ;
295
- }
296
- }
297
- else
298
- {
299
- // RunSpecific
300
- var iterationCount = actualStage . iterationCount ?? EngineActualStage . DefaultWorkloadCount ;
301
- for ( int i = 0 ; i < iterationCount ; i ++ )
302
- measurements . Add ( RunIteration ( new IterationData ( IterationMode . Workload , IterationStage . Actual , i + 1 , invokeCount , UnrollFactor ) ) ) ;
303
- }
304
166
WriteLine ( ) ;
305
167
}
306
- // End Actual Workload
307
-
308
- Host . AfterMainRun ( ) ;
309
168
310
169
( GcStats workGcHasDone , ThreadingStats threadingStats , double exceptionFrequency ) = includeExtraStats
311
170
? GetExtraStats ( new IterationData ( IterationMode . Workload , IterationStage . Actual , 0 , invokeCount , UnrollFactor ) )
0 commit comments