1212using BenchmarkDotNet . Portability ;
1313using BenchmarkDotNet . Reports ;
1414using BenchmarkDotNet . Tests . XUnit ;
15+ using BenchmarkDotNet . Toolchains ;
16+ using BenchmarkDotNet . Toolchains . CsProj ;
1517using BenchmarkDotNet . Toolchains . InProcess . Emit ;
1618using Perfolizer ;
1719using Perfolizer . Horology ;
@@ -26,11 +28,23 @@ namespace BenchmarkDotNet.IntegrationTests.ManualRunning
2628{
2729 public class ExpectedBenchmarkResultsTests ( ITestOutputHelper output ) : BenchmarkTestExecutor ( output )
2830 {
29- // NativeAot takes a long time to build, so not including it in these tests.
30- // We also don't test InProcessNoEmitToolchain because it is known to be less accurate than code-gen toolchains.
31-
3231 private static readonly TimeInterval FallbackCpuResolutionValue = TimeInterval . FromNanoseconds ( 0.2d ) ;
3332
33+ // Visual Studio Test Explorer doesn't like to display IToolchain params separately, so use an enum instead.
34+ public enum ToolchainType
35+ {
36+ Default ,
37+ InProcess
38+ }
39+
40+ private IToolchain GetToolchain ( ToolchainType toolchain )
41+ => toolchain switch
42+ {
43+ ToolchainType . Default => Job . Default . GetToolchain ( ) ,
44+ ToolchainType . InProcess => InProcessEmitToolchain . Instance ,
45+ _ => throw new NotSupportedException ( )
46+ } ;
47+
3448 private static IEnumerable < Type > EmptyBenchmarkTypes ( ) =>
3549 [
3650 typeof ( EmptyVoid ) ,
@@ -49,73 +63,32 @@ private static IEnumerable<Type> EmptyBenchmarkTypes() =>
4963 typeof ( EmptyClass )
5064 ] ;
5165
52- public static IEnumerable < object [ ] > InProcessData ( )
66+ public static IEnumerable < object [ ] > GetEmptyArgs ( )
5367 {
5468 foreach ( var type in EmptyBenchmarkTypes ( ) )
5569 {
56- yield return new object [ ] { type } ;
57- }
58- }
59-
60- public static IEnumerable < object [ ] > CoreData ( )
61- {
62- foreach ( var type in EmptyBenchmarkTypes ( ) )
63- {
64- yield return new object [ ] { type , RuntimeMoniker . Net80 } ;
65- yield return new object [ ] { type , RuntimeMoniker . Mono80 } ;
66- }
67- }
68-
69- public static IEnumerable < object [ ] > FrameworkData ( )
70- {
71- foreach ( var type in EmptyBenchmarkTypes ( ) )
72- {
73- yield return new object [ ] { type , RuntimeMoniker . Net462 } ;
74- yield return new object [ ] { type , RuntimeMoniker . Mono } ;
70+ yield return new object [ ] { ToolchainType . Default , type } ;
71+ // InProcess overhead measurements are incorrect in Core. https://github.com/dotnet/runtime/issues/89685
72+ if ( ! RuntimeInformation . IsNetCore )
73+ {
74+ yield return new object [ ] { ToolchainType . InProcess , type } ;
75+ }
7576 }
7677 }
7778
7879 [ Theory ]
79- [ MemberData ( nameof ( InProcessData ) ) ]
80- public void EmptyBenchmarkReportsZeroTimeAndAllocated_InProcess ( Type benchmarkType )
81- {
82- AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
83- . AddJob ( Job . Default
84- . WithToolchain ( InProcessEmitToolchain . Instance )
85- // IL Emit has incorrect overhead measurement. https://github.com/dotnet/runtime/issues/89685
86- // We multiply the threshold to account for it.
87- ) , multiplyThresholdBy : RuntimeInformation . IsNetCore ? 3 : 1 ) ;
88- }
89-
90- [ TheoryEnvSpecific ( "To not repeat tests in both Full .NET Framework and Core" , EnvRequirement . DotNetCoreOnly ) ]
91- [ MemberData ( nameof ( CoreData ) ) ]
92- public void EmptyBenchmarkReportsZeroTimeAndAllocated_Core ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
93- {
94- AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
95- . AddJob ( Job . Default
96- . WithRuntime ( runtimeMoniker . GetRuntime ( ) )
97- ) ) ;
98- }
99-
100- [ TheoryEnvSpecific ( "Can only run Full .NET Framework and Mono tests from Framework host" , EnvRequirement . FullFrameworkOnly ) ]
101- [ MemberData ( nameof ( FrameworkData ) ) ]
102- public void EmptyBenchmarkReportsZeroTimeAndAllocated_Framework ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
103- {
104- AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
105- . AddJob ( Job . Default
106- . WithRuntime ( runtimeMoniker . GetRuntime ( ) )
107- ) ) ;
108- }
109-
110- private void AssertZeroResults ( Type benchmarkType , IConfig config , int multiplyThresholdBy = 1 )
80+ [ MemberData ( nameof ( GetEmptyArgs ) ) ]
81+ public void EmptyBenchmarkReportsZeroTimeAndAllocated ( ToolchainType toolchain , Type benchmarkType )
11182 {
112- var summary = CanExecute ( benchmarkType , config
83+ var config = ManualConfig . CreateEmpty ( )
84+ . AddJob ( Job . Default . WithToolchain ( GetToolchain ( toolchain ) ) )
11385 . WithSummaryStyle ( SummaryStyle . Default . WithTimeUnit ( TimeUnit . Nanosecond ) )
114- . AddDiagnoser ( new MemoryDiagnoser ( new MemoryDiagnoserConfig ( false ) ) )
115- ) ;
86+ . AddDiagnoser ( new MemoryDiagnoser ( new MemoryDiagnoserConfig ( false ) ) ) ;
87+
88+ var summary = CanExecute ( benchmarkType , config ) ;
11689
11790 var cpuResolution = CpuDetector . Cpu ? . MaxFrequency ( ) ? . ToResolution ( ) ?? FallbackCpuResolutionValue ;
118- var threshold = new NumberValue ( cpuResolution . Nanoseconds * multiplyThresholdBy ) . ToThreshold ( ) ;
91+ var threshold = new NumberValue ( cpuResolution . Nanoseconds ) . ToThreshold ( ) ;
11992
12093 foreach ( var report in summary . Reports )
12194 {
@@ -131,80 +104,41 @@ private void AssertZeroResults(Type benchmarkType, IConfig config, int multiplyT
131104
132105 private static IEnumerable < Type > NonEmptyBenchmarkTypes ( ) =>
133106 [
134- typeof ( DifferentSizedStructs ) ,
107+ // Structs even as large as Struct128 results in zero measurements on Zen 5, so the test will only pass on older CPU architectures.
108+ //typeof(DifferentSizedStructs),
135109 typeof ( ActualWork )
136110 ] ;
137111
138- public static IEnumerable < object [ ] > NonEmptyInProcessData ( )
112+ public static IEnumerable < object [ ] > GetNonEmptyArgs ( )
139113 {
140114 foreach ( var type in NonEmptyBenchmarkTypes ( ) )
141115 {
142- yield return new object [ ] { type } ;
143- }
144- }
145-
146- public static IEnumerable < object [ ] > NonEmptyCoreData ( )
147- {
148- foreach ( var type in NonEmptyBenchmarkTypes ( ) )
149- {
150- yield return new object [ ] { type , RuntimeMoniker . Net80 } ;
151- yield return new object [ ] { type , RuntimeMoniker . Mono80 } ;
152- }
153- }
154-
155- public static IEnumerable < object [ ] > NonEmptyFrameworkData ( )
156- {
157- foreach ( var type in NonEmptyBenchmarkTypes ( ) )
158- {
159- yield return new object [ ] { type , RuntimeMoniker . Net462 } ;
160- yield return new object [ ] { type , RuntimeMoniker . Mono } ;
116+ // Framework is slightly less accurate than Core.
117+ yield return new object [ ] { ToolchainType . Default , type , RuntimeInformation . IsNetCore ? 0 : 1 } ;
118+ // InProcess overhead measurements are incorrect in Core. https://github.com/dotnet/runtime/issues/89685
119+ if ( ! RuntimeInformation . IsNetCore )
120+ {
121+ yield return new object [ ] { ToolchainType . InProcess , type , 1 } ;
122+ }
161123 }
162124 }
163125
164126 [ Theory ]
165- [ MemberData ( nameof ( NonEmptyInProcessData ) ) ]
166- public void NonEmptyBenchmarkReportsNonZeroTimeAndZeroAllocated_InProcess ( Type benchmarkType )
167- {
168- AssertNonZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
169- . AddJob ( Job . Default
170- . WithToolchain ( InProcessEmitToolchain . Instance )
171- // InProcess overhead measurements are incorrect, so we adjust the results to account for it. https://github.com/dotnet/runtime/issues/89685
172- ) , subtractOverheadByClocks : RuntimeInformation . IsNetCore ? 3 : 1 ) ;
173- }
174-
175- [ TheoryEnvSpecific ( "To not repeat tests in both Full .NET Framework and Core" , EnvRequirement . DotNetCoreOnly ) ]
176- [ MemberData ( nameof ( NonEmptyCoreData ) ) ]
177- public void NonEmptyBenchmarkReportsNonZeroTimeAndZeroAllocated_Core ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
178- {
179- AssertNonZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
180- . AddJob ( Job . Default
181- . WithRuntime ( runtimeMoniker . GetRuntime ( ) )
182- ) ) ;
183- }
184-
185- [ TheoryEnvSpecific ( "Can only run Mono tests from Framework host" , EnvRequirement . FullFrameworkOnly ) ]
186- [ MemberData ( nameof ( NonEmptyFrameworkData ) ) ]
187- public void NonEmptyBenchmarkReportsNonZeroTimeAndZeroAllocated_Framework ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
188- {
189- AssertNonZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
190- . AddJob ( Job . Default
191- . WithRuntime ( runtimeMoniker . GetRuntime ( ) )
192- ) ) ;
193- }
194-
195- private void AssertNonZeroResults ( Type benchmarkType , IConfig config , int subtractOverheadByClocks = 0 )
127+ [ MemberData ( nameof ( GetNonEmptyArgs ) ) ]
128+ public void NonEmptyBenchmarkReportsNonZeroTimeAndZeroAllocated ( ToolchainType toolchain , Type benchmarkType , int subtractOverheadByClocks )
196129 {
197- var summary = CanExecute ( benchmarkType , config
130+ var config = ManualConfig . CreateEmpty ( )
131+ . AddJob ( Job . Default . WithToolchain ( GetToolchain ( toolchain ) ) )
198132 . WithSummaryStyle ( SummaryStyle . Default . WithTimeUnit ( TimeUnit . Nanosecond ) )
199- . AddDiagnoser ( new MemoryDiagnoser ( new MemoryDiagnoserConfig ( false ) ) )
200- ) ;
133+ . AddDiagnoser ( new MemoryDiagnoser ( new MemoryDiagnoserConfig ( false ) ) ) ;
134+
135+ var summary = CanExecute ( benchmarkType , config ) ;
201136
202137 var cpuResolution = CpuDetector . Cpu ? . MaxFrequency ( ) ? . ToResolution ( ) ?? FallbackCpuResolutionValue ;
203138 // Modern cpus can execute multiple instructions per clock cycle,
204139 // resulting in measurements greater than 0 but less than 1 clock cycle.
205140 // (example: Intel Core i9-9880H CPU 2.30GHz reports 0.2852 ns for `_field++;`)
206141 var threshold = new NumberValue ( cpuResolution . Nanoseconds / 4 ) . ToThreshold ( ) ;
207- // InProcess overhead measurements are incorrect, so we adjust the results to account for it. https://github.com/dotnet/runtime/issues/89685
208142 var overheadSubtraction = cpuResolution . Nanoseconds * subtractOverheadByClocks ;
209143
210144 foreach ( var report in summary . Reports )
0 commit comments