12
12
using BenchmarkDotNet . Portability ;
13
13
using BenchmarkDotNet . Reports ;
14
14
using BenchmarkDotNet . Tests . XUnit ;
15
+ using BenchmarkDotNet . Toolchains ;
16
+ using BenchmarkDotNet . Toolchains . CsProj ;
15
17
using BenchmarkDotNet . Toolchains . InProcess . Emit ;
16
18
using Perfolizer ;
17
19
using Perfolizer . Horology ;
@@ -26,11 +28,23 @@ namespace BenchmarkDotNet.IntegrationTests.ManualRunning
26
28
{
27
29
public class ExpectedBenchmarkResultsTests ( ITestOutputHelper output ) : BenchmarkTestExecutor ( output )
28
30
{
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
-
32
31
private static readonly TimeInterval FallbackCpuResolutionValue = TimeInterval . FromNanoseconds ( 0.2d ) ;
33
32
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
+
34
48
private static IEnumerable < Type > EmptyBenchmarkTypes ( ) =>
35
49
[
36
50
typeof ( EmptyVoid ) ,
@@ -49,73 +63,32 @@ private static IEnumerable<Type> EmptyBenchmarkTypes() =>
49
63
typeof ( EmptyClass )
50
64
] ;
51
65
52
- public static IEnumerable < object [ ] > InProcessData ( )
66
+ public static IEnumerable < object [ ] > GetEmptyArgs ( )
53
67
{
54
68
foreach ( var type in EmptyBenchmarkTypes ( ) )
55
69
{
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
+ }
75
76
}
76
77
}
77
78
78
79
[ 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 )
111
82
{
112
- var summary = CanExecute ( benchmarkType , config
83
+ var config = ManualConfig . CreateEmpty ( )
84
+ . AddJob ( Job . Default . WithToolchain ( GetToolchain ( toolchain ) ) )
113
85
. 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 ) ;
116
89
117
90
var cpuResolution = CpuDetector . Cpu ? . MaxFrequency ( ) ? . ToResolution ( ) ?? FallbackCpuResolutionValue ;
118
- var threshold = new NumberValue ( cpuResolution . Nanoseconds * multiplyThresholdBy ) . ToThreshold ( ) ;
91
+ var threshold = new NumberValue ( cpuResolution . Nanoseconds ) . ToThreshold ( ) ;
119
92
120
93
foreach ( var report in summary . Reports )
121
94
{
@@ -131,80 +104,41 @@ private void AssertZeroResults(Type benchmarkType, IConfig config, int multiplyT
131
104
132
105
private static IEnumerable < Type > NonEmptyBenchmarkTypes ( ) =>
133
106
[
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),
135
109
typeof ( ActualWork )
136
110
] ;
137
111
138
- public static IEnumerable < object [ ] > NonEmptyInProcessData ( )
112
+ public static IEnumerable < object [ ] > GetNonEmptyArgs ( )
139
113
{
140
114
foreach ( var type in NonEmptyBenchmarkTypes ( ) )
141
115
{
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
+ }
161
123
}
162
124
}
163
125
164
126
[ 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 )
196
129
{
197
- var summary = CanExecute ( benchmarkType , config
130
+ var config = ManualConfig . CreateEmpty ( )
131
+ . AddJob ( Job . Default . WithToolchain ( GetToolchain ( toolchain ) ) )
198
132
. 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 ) ;
201
136
202
137
var cpuResolution = CpuDetector . Cpu ? . MaxFrequency ( ) ? . ToResolution ( ) ?? FallbackCpuResolutionValue ;
203
138
// Modern cpus can execute multiple instructions per clock cycle,
204
139
// resulting in measurements greater than 0 but less than 1 clock cycle.
205
140
// (example: Intel Core i9-9880H CPU 2.30GHz reports 0.2852 ns for `_field++;`)
206
141
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
208
142
var overheadSubtraction = cpuResolution . Nanoseconds * subtractOverheadByClocks ;
209
143
210
144
foreach ( var report in summary . Reports )
0 commit comments