8
8
using BenchmarkDotNet . Diagnosers ;
9
9
using BenchmarkDotNet . Engines ;
10
10
using BenchmarkDotNet . Extensions ;
11
+ using BenchmarkDotNet . IntegrationTests . ManualRunning ;
11
12
using BenchmarkDotNet . Jobs ;
12
13
using BenchmarkDotNet . Portability ;
13
14
using BenchmarkDotNet . Reports ;
@@ -48,15 +49,15 @@ private static IEnumerable<Type> EmptyBenchmarkTypes() =>
48
49
typeof ( EmptyClass )
49
50
} ;
50
51
51
- public static IEnumerable < object [ ] > InProcessData ( )
52
+ public static IEnumerable < object [ ] > EmptyInProcessData ( )
52
53
{
53
54
foreach ( var type in EmptyBenchmarkTypes ( ) )
54
55
{
55
56
yield return new object [ ] { type } ;
56
57
}
57
58
}
58
59
59
- public static IEnumerable < object [ ] > CoreData ( )
60
+ public static IEnumerable < object [ ] > EmptyCoreData ( )
60
61
{
61
62
foreach ( var type in EmptyBenchmarkTypes ( ) )
62
63
{
@@ -65,7 +66,7 @@ public static IEnumerable<object[]> CoreData()
65
66
}
66
67
}
67
68
68
- public static IEnumerable < object [ ] > FrameworkData ( )
69
+ public static IEnumerable < object [ ] > EmptyFrameworkData ( )
69
70
{
70
71
foreach ( var type in EmptyBenchmarkTypes ( ) )
71
72
{
@@ -75,7 +76,7 @@ public static IEnumerable<object[]> FrameworkData()
75
76
}
76
77
77
78
[ Theory ]
78
- [ MemberData ( nameof ( InProcessData ) ) ]
79
+ [ MemberData ( nameof ( EmptyInProcessData ) ) ]
79
80
public void EmptyBenchmarksReportZeroTimeAndAllocated_InProcess ( Type benchmarkType )
80
81
{
81
82
AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
@@ -85,7 +86,7 @@ public void EmptyBenchmarksReportZeroTimeAndAllocated_InProcess(Type benchmarkTy
85
86
}
86
87
87
88
[ TheoryEnvSpecific ( "To not repeat tests in both Full .NET Framework and Core" , EnvRequirement . DotNetCoreOnly ) ]
88
- [ MemberData ( nameof ( CoreData ) ) ]
89
+ [ MemberData ( nameof ( EmptyCoreData ) ) ]
89
90
public void EmptyBenchmarksReportZeroTimeAndAllocated_Core ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
90
91
{
91
92
AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
@@ -95,7 +96,7 @@ public void EmptyBenchmarksReportZeroTimeAndAllocated_Core(Type benchmarkType, R
95
96
}
96
97
97
98
[ TheoryEnvSpecific ( "Can only run Full .NET Framework and Mono tests from Framework host" , EnvRequirement . FullFrameworkOnly ) ]
98
- [ MemberData ( nameof ( FrameworkData ) ) ]
99
+ [ MemberData ( nameof ( EmptyFrameworkData ) ) ]
99
100
public void EmptyBenchmarksReportZeroTimeAndAllocated_Framework ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
100
101
{
101
102
AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
@@ -129,57 +130,91 @@ private void AssertZeroResults(Type benchmarkType, IConfig config)
129
130
}
130
131
}
131
132
132
- [ Fact ]
133
- public void DifferentSizedStructsBenchmarksReportsNonZeroTimeAndZeroAllocated_InProcess ( )
133
+ private static IEnumerable < Type > NonEmptyBenchmarkTypes ( ) =>
134
+ new [ ]
135
+ {
136
+ typeof ( DifferentSizedStructs ) ,
137
+ typeof ( ActualWork )
138
+ } ;
139
+
140
+ public static IEnumerable < object [ ] > NonEmptyInProcessData ( )
141
+ {
142
+ foreach ( var type in NonEmptyBenchmarkTypes ( ) )
143
+ {
144
+ yield return new object [ ] { type } ;
145
+ }
146
+ }
147
+
148
+ public static IEnumerable < object [ ] > NonEmptyCoreData ( )
149
+ {
150
+ foreach ( var type in NonEmptyBenchmarkTypes ( ) )
151
+ {
152
+ yield return new object [ ] { type , RuntimeMoniker . Net70 } ;
153
+ yield return new object [ ] { type , RuntimeMoniker . Mono70 } ;
154
+ }
155
+ }
156
+
157
+ public static IEnumerable < object [ ] > NonEmptyFrameworkData ( )
158
+ {
159
+ foreach ( var type in NonEmptyBenchmarkTypes ( ) )
160
+ {
161
+ yield return new object [ ] { type , RuntimeMoniker . Net462 } ;
162
+ yield return new object [ ] { type , RuntimeMoniker . Mono } ;
163
+ }
164
+ }
165
+
166
+ [ Theory ]
167
+ [ MemberData ( nameof ( NonEmptyInProcessData ) ) ]
168
+ public void NonEmptyBenchmarksReportsNonZeroTimeAndZeroAllocated_InProcess ( Type benchmarkType )
134
169
{
135
- AssertDifferentSizedStructsResults ( ManualConfig . CreateEmpty ( )
170
+ AssertNonZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
136
171
. AddJob ( Job . Default
137
172
. WithToolchain ( InProcessEmitToolchain . Instance )
138
173
) ) ;
139
174
}
140
175
141
176
[ TheoryEnvSpecific ( "To not repeat tests in both Full .NET Framework and Core" , EnvRequirement . DotNetCoreOnly ) ]
142
- [ InlineData ( RuntimeMoniker . Net70 ) ]
143
- [ InlineData ( RuntimeMoniker . Mono70 ) ]
144
- public void DifferentSizedStructsBenchmarksReportsNonZeroTimeAndZeroAllocated_Core ( RuntimeMoniker runtimeMoniker )
177
+ [ MemberData ( nameof ( NonEmptyCoreData ) ) ]
178
+ public void NonEmptyBenchmarksReportsNonZeroTimeAndZeroAllocated_Core ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
145
179
{
146
- AssertDifferentSizedStructsResults ( ManualConfig . CreateEmpty ( )
180
+ AssertNonZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
147
181
. AddJob ( Job . Default
148
182
. WithRuntime ( runtimeMoniker . GetRuntime ( ) )
149
183
) ) ;
150
184
}
151
185
152
186
[ TheoryEnvSpecific ( "Can only run Full .NET Framework and Mono tests from Framework host" , EnvRequirement . FullFrameworkOnly ) ]
153
- [ InlineData ( RuntimeMoniker . Net462 ) ]
154
- [ InlineData ( RuntimeMoniker . Mono ) ]
155
- public void DifferentSizedStructsBenchmarksReportsNonZeroTimeAndZeroAllocated_Framework ( RuntimeMoniker runtimeMoniker )
187
+ [ MemberData ( nameof ( NonEmptyFrameworkData ) ) ]
188
+ public void NonEmptyBenchmarksReportsNonZeroTimeAndZeroAllocated_Framework ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
156
189
{
157
- AssertDifferentSizedStructsResults ( ManualConfig . CreateEmpty ( )
190
+ AssertNonZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
158
191
. AddJob ( Job . Default
159
192
. WithRuntime ( runtimeMoniker . GetRuntime ( ) )
160
193
) ) ;
161
194
}
162
195
163
- private void AssertDifferentSizedStructsResults ( IConfig config )
196
+ private void AssertNonZeroResults ( Type benchmarkType , IConfig config )
164
197
{
165
- var summary = CanExecute < DifferentSizedStructs > ( config
198
+ var summary = CanExecute ( benchmarkType , config
166
199
. WithSummaryStyle ( SummaryStyle . Default . WithTimeUnit ( TimeUnit . Nanosecond ) )
167
200
. AddDiagnoser ( new MemoryDiagnoser ( new MemoryDiagnoserConfig ( false ) ) )
168
201
) ;
169
202
170
203
var cpuResolution = RuntimeInformation . GetCpuInfo ( ) . MaxFrequency ? . ToResolution ( ) ?? FallbackCpuResolutionValue ;
171
- var threshold = Threshold . Create ( ThresholdUnit . Nanoseconds , cpuResolution . Nanoseconds ) ;
172
204
173
205
foreach ( var report in summary . Reports )
174
206
{
175
207
var workloadMeasurements = report . AllMeasurements . Where ( m => m . Is ( IterationMode . Workload , IterationStage . Actual ) ) . GetStatistics ( ) . WithoutOutliers ( ) ;
176
208
var overheadMeasurements = report . AllMeasurements . Where ( m => m . Is ( IterationMode . Overhead , IterationStage . Actual ) ) . GetStatistics ( ) . WithoutOutliers ( ) ;
177
209
178
- bool isZero = ZeroMeasurementHelper . CheckZeroMeasurementTwoSamples ( workloadMeasurements , overheadMeasurements , threshold ) ;
210
+ // We use the default threshold here rather than using cpu resolution,
211
+ // because modern cpus can execute multiple instructions per clock cycle,
212
+ // resulting in measurements greater than 0 but less than 1 clock cycle.
213
+ // (example: Intel Core i9-9880H CPU 2.30GHz reports 0.2852 ns for `_field++;`)
214
+ bool isZero = ZeroMeasurementHelper . CheckZeroMeasurementTwoSamples ( workloadMeasurements , overheadMeasurements ) ;
179
215
Assert . False ( isZero , $ "Actual time was 0.") ;
180
216
181
- isZero = ZeroMeasurementHelper . CheckZeroMeasurementTwoSamples ( overheadMeasurements , workloadMeasurements , threshold ) ;
182
- Assert . True ( isZero , "Overhead took more time than workload." ) ;
217
+ // We don't need to check if overhead was greater. It's guaranteed less if the actual time was not zero.
183
218
184
219
Assert . True ( ( report . GcStats . GetBytesAllocatedPerOperation ( report . BenchmarkCase ) ?? 0L ) == 0L , "Memory allocations measured above 0." ) ;
185
220
}
@@ -209,13 +244,25 @@ public struct Struct128
209
244
l9 , l10 , l11 , l12 ,
210
245
l13 , l14 , l15 , l16 ;
211
246
}
247
+ }
248
+
249
+ public class DifferentSizedStructs
250
+ {
251
+ [ Benchmark ] public Struct16 Struct16 ( ) => default ;
252
+ [ Benchmark ] public Struct32 Struct32 ( ) => default ;
253
+ [ Benchmark ] public Struct64 Struct64 ( ) => default ;
254
+ [ Benchmark ] public Struct128 Struct128 ( ) => default ;
255
+ }
256
+
257
+ public class ActualWork
258
+ {
259
+ public int _field ;
212
260
213
- public class DifferentSizedStructs
261
+ [ Benchmark ]
262
+ public void IncrementField ( )
214
263
{
215
- [ Benchmark ] public Struct16 Struct16 ( ) => default ;
216
- [ Benchmark ] public Struct32 Struct32 ( ) => default ;
217
- [ Benchmark ] public Struct64 Struct64 ( ) => default ;
218
- [ Benchmark ] public Struct128 Struct128 ( ) => default ;
264
+ _field ++ ;
265
+ _field ++ ;
219
266
}
220
267
}
221
268
0 commit comments