99using BenchmarkDotNet . ConsoleArguments . ListBenchmarks ;
1010using BenchmarkDotNet . Environments ;
1111using BenchmarkDotNet . Extensions ;
12+ using BenchmarkDotNet . Jobs ;
1213using BenchmarkDotNet . Loggers ;
14+ using BenchmarkDotNet . Parameters ;
1315using BenchmarkDotNet . Reports ;
1416using JetBrains . Annotations ;
17+ using Perfolizer . Mathematics . OutlierDetection ;
1518
1619namespace BenchmarkDotNet . Running
1720{
@@ -109,6 +112,11 @@ internal IEnumerable<Summary> RunWithDirtyAssemblyResolveHelper(string[] args, I
109112 ? allAvailableTypesWithRunnableBenchmarks
110113 : userInteraction . AskUser ( allAvailableTypesWithRunnableBenchmarks , logger ) ;
111114
115+ if ( effectiveConfig . Options . HasFlag ( ConfigOptions . ApplesToApples ) )
116+ {
117+ return ApplesToApples ( effectiveConfig , benchmarksToFilter , logger , options ) ;
118+ }
119+
112120 var filteredBenchmarks = TypeFilter . Filter ( effectiveConfig , benchmarksToFilter ) ;
113121
114122 if ( filteredBenchmarks . IsEmpty ( ) )
@@ -131,5 +139,74 @@ private static void PrintList(ILogger nonNullLogger, IConfig effectiveConfig, IR
131139
132140 printer . Print ( testNames , nonNullLogger ) ;
133141 }
142+
143+ private IEnumerable < Summary > ApplesToApples ( ManualConfig effectiveConfig , IReadOnlyList < Type > benchmarksToFilter , ILogger logger , CommandLineOptions options )
144+ {
145+ var jobs = effectiveConfig . GetJobs ( ) . ToArray ( ) ;
146+ if ( jobs . Length <= 1 )
147+ {
148+ logger . WriteError ( "To use apples-to-apples comparison you must specify at least two Job objects." ) ;
149+ return Array . Empty < Summary > ( ) ;
150+ }
151+ var baselineJob = jobs . SingleOrDefault ( job => job . Meta . Baseline ) ;
152+ if ( baselineJob == default )
153+ {
154+ logger . WriteError ( "To use apples-to-apples comparison you must specify exactly ONE baseline Job object." ) ;
155+ return Array . Empty < Summary > ( ) ;
156+ }
157+ else if ( jobs . Any ( job => ! job . Run . HasValue ( RunMode . IterationCountCharacteristic ) ) )
158+ {
159+ logger . WriteError ( "To use apples-to-apples comparison you must specify the number of iterations in explicit way." ) ;
160+ return Array . Empty < Summary > ( ) ;
161+ }
162+
163+ Job invocationCountJob = baselineJob
164+ . WithWarmupCount ( 1 )
165+ . WithIterationCount ( 1 )
166+ . WithEvaluateOverhead ( false ) ;
167+
168+ ManualConfig invocationCountConfig = ManualConfig . Create ( effectiveConfig ) ;
169+ invocationCountConfig . RemoveAllJobs ( ) ;
170+ invocationCountConfig . RemoveAllDiagnosers ( ) ;
171+ invocationCountConfig . AddJob ( invocationCountJob ) ;
172+
173+ var invocationCountBenchmarks = TypeFilter . Filter ( invocationCountConfig , benchmarksToFilter ) ;
174+ if ( invocationCountBenchmarks . IsEmpty ( ) )
175+ {
176+ userInteraction . PrintWrongFilterInfo ( benchmarksToFilter , logger , options . Filters . ToArray ( ) ) ;
177+ return Array . Empty < Summary > ( ) ;
178+ }
179+
180+ logger . WriteLineHeader ( "Each benchmark is going to be executed just once to get invocation counts." ) ;
181+ Summary [ ] invocationCountSummaries = BenchmarkRunnerClean . Run ( invocationCountBenchmarks ) ;
182+
183+ Dictionary < ( Descriptor Descriptor , ParameterInstances Parameters ) , Measurement > dictionary = invocationCountSummaries
184+ . SelectMany ( summary => summary . Reports )
185+ . ToDictionary (
186+ report => ( report . BenchmarkCase . Descriptor , report . BenchmarkCase . Parameters ) ,
187+ report => report . AllMeasurements . Single ( measurement => measurement . IsWorkload ( ) && measurement . IterationStage == Engines . IterationStage . Actual ) ) ;
188+
189+ int iterationCount = baselineJob . Run . IterationCount ;
190+ BenchmarkRunInfo [ ] benchmarksWithoutInvocationCount = TypeFilter . Filter ( effectiveConfig , benchmarksToFilter ) ;
191+ BenchmarkRunInfo [ ] benchmarksWithInvocationCount = benchmarksWithoutInvocationCount
192+ . Select ( benchmarkInfo => new BenchmarkRunInfo (
193+ benchmarkInfo . BenchmarksCases . Select ( benchmark =>
194+ new BenchmarkCase (
195+ benchmark . Descriptor ,
196+ benchmark . Job
197+ . WithIterationCount ( iterationCount )
198+ . WithEvaluateOverhead ( false )
199+ . WithWarmupCount ( 1 )
200+ . WithOutlierMode ( OutlierMode . DontRemove )
201+ . WithInvocationCount ( dictionary [ ( benchmark . Descriptor , benchmark . Parameters ) ] . Operations )
202+ . WithUnrollFactor ( dictionary [ ( benchmark . Descriptor , benchmark . Parameters ) ] . Operations % 16 == 0 ? 16 : 1 ) ,
203+ benchmark . Parameters ,
204+ benchmark . Config ) ) . ToArray ( ) ,
205+ benchmarkInfo . Type , benchmarkInfo . Config ) )
206+ . ToArray ( ) ;
207+
208+ logger . WriteLineHeader ( "Actual benchmarking is going to happen now!" ) ;
209+ return BenchmarkRunnerClean . Run ( benchmarksWithInvocationCount ) ;
210+ }
134211 }
135212}
0 commit comments