1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Diagnostics ;
4
+ using System . Linq ;
5
+ using BenchmarkDotNet . Analysers ;
6
+ using BenchmarkDotNet . Attributes ;
7
+ using BenchmarkDotNet . Configs ;
8
+ using BenchmarkDotNet . Diagnosers ;
9
+ using BenchmarkDotNet . Engines ;
10
+ using BenchmarkDotNet . Extensions ;
11
+ using BenchmarkDotNet . Jobs ;
12
+ using BenchmarkDotNet . Portability ;
13
+ using BenchmarkDotNet . Reports ;
14
+ using BenchmarkDotNet . Tests . XUnit ;
15
+ using BenchmarkDotNet . Toolchains . InProcess . Emit ;
16
+ using Perfolizer . Horology ;
17
+ using Perfolizer . Mathematics . Thresholds ;
18
+ using Xunit ;
19
+ using Xunit . Abstractions ;
20
+
21
+ namespace BenchmarkDotNet . IntegrationTests . ManualRunning
22
+ {
23
+ public class ExpectedBenchmarkResultsTests : BenchmarkTestExecutor
24
+ {
25
+ // NativeAot takes a long time to build, so not including it in these tests.
26
+ // We also don't test InProcessNoEmitToolchain because it is known to be less accurate than code-gen toolchains.
27
+
28
+ private static readonly TimeInterval FallbackCpuResolutionValue = TimeInterval . FromNanoseconds ( 0.2d ) ;
29
+
30
+ public ExpectedBenchmarkResultsTests ( ITestOutputHelper output ) : base ( output ) { }
31
+
32
+ private static IEnumerable < Type > EmptyBenchmarkTypes ( ) =>
33
+ new [ ]
34
+ {
35
+ typeof ( EmptyVoid ) ,
36
+ typeof ( EmptyByte ) ,
37
+ typeof ( EmptySByte ) ,
38
+ typeof ( EmptyShort ) ,
39
+ typeof ( EmptyUShort ) ,
40
+ typeof ( EmptyChar ) ,
41
+ typeof ( EmptyInt32 ) ,
42
+ typeof ( EmptyUInt32 ) ,
43
+ typeof ( EmptyInt64 ) ,
44
+ typeof ( EmptyUInt64 ) ,
45
+ typeof ( EmptyIntPtr ) ,
46
+ typeof ( EmptyUIntPtr ) ,
47
+ typeof ( EmptyVoidPointer ) ,
48
+ typeof ( EmptyClass )
49
+ } ;
50
+
51
+ public static IEnumerable < object [ ] > InProcessData ( )
52
+ {
53
+ foreach ( var type in EmptyBenchmarkTypes ( ) )
54
+ {
55
+ yield return new object [ ] { type } ;
56
+ }
57
+ }
58
+
59
+ public static IEnumerable < object [ ] > CoreData ( )
60
+ {
61
+ foreach ( var type in EmptyBenchmarkTypes ( ) )
62
+ {
63
+ yield return new object [ ] { type , RuntimeMoniker . Net70 } ;
64
+ yield return new object [ ] { type , RuntimeMoniker . Mono70 } ;
65
+ }
66
+ }
67
+
68
+ public static IEnumerable < object [ ] > FrameworkData ( )
69
+ {
70
+ foreach ( var type in EmptyBenchmarkTypes ( ) )
71
+ {
72
+ yield return new object [ ] { type , RuntimeMoniker . Net462 } ;
73
+ yield return new object [ ] { type , RuntimeMoniker . Mono } ;
74
+ }
75
+ }
76
+
77
+ [ Theory ]
78
+ [ MemberData ( nameof ( InProcessData ) ) ]
79
+ public void EmptyBenchmarksReportZeroTimeAndAllocated_InProcess ( Type benchmarkType )
80
+ {
81
+ AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
82
+ . AddJob ( Job . Default
83
+ . WithToolchain ( InProcessEmitToolchain . Instance )
84
+ ) ) ;
85
+ }
86
+
87
+ [ TheoryNetCoreOnly ( "To not repeat tests in both full Framework and Core" ) ]
88
+ [ MemberData ( nameof ( CoreData ) ) ]
89
+ public void EmptyBenchmarksReportZeroTimeAndAllocated_Core ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
90
+ {
91
+ AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
92
+ . AddJob ( Job . Default
93
+ . WithRuntime ( runtimeMoniker . GetRuntime ( ) )
94
+ ) ) ;
95
+ }
96
+
97
+ [ TheoryFullFrameworkOnly ( "Can only run full Framework and Mono tests from Framework host" ) ]
98
+ [ MemberData ( nameof ( FrameworkData ) ) ]
99
+ public void EmptyBenchmarksReportZeroTimeAndAllocated_Framework ( Type benchmarkType , RuntimeMoniker runtimeMoniker )
100
+ {
101
+ AssertZeroResults ( benchmarkType , ManualConfig . CreateEmpty ( )
102
+ . AddJob ( Job . Default
103
+ . WithRuntime ( runtimeMoniker . GetRuntime ( ) )
104
+ ) ) ;
105
+ }
106
+
107
+ private void AssertZeroResults ( Type benchmarkType , IConfig config )
108
+ {
109
+ var summary = CanExecute ( benchmarkType , config
110
+ . WithSummaryStyle ( SummaryStyle . Default . WithTimeUnit ( TimeUnit . Nanosecond ) )
111
+ . AddDiagnoser ( new MemoryDiagnoser ( new MemoryDiagnoserConfig ( false ) ) )
112
+ ) ;
113
+
114
+ var cpuResolution = RuntimeInformation . GetCpuInfo ( ) . MaxFrequency ? . ToResolution ( ) ?? FallbackCpuResolutionValue ;
115
+ var threshold = Threshold . Create ( ThresholdUnit . Nanoseconds , cpuResolution . Nanoseconds ) ;
116
+
117
+ foreach ( var report in summary . Reports )
118
+ {
119
+ var workloadMeasurements = report . AllMeasurements . Where ( m => m . Is ( IterationMode . Workload , IterationStage . Actual ) ) . GetStatistics ( ) . WithoutOutliers ( ) ;
120
+ var overheadMeasurements = report . AllMeasurements . Where ( m => m . Is ( IterationMode . Overhead , IterationStage . Actual ) ) . GetStatistics ( ) . WithoutOutliers ( ) ;
121
+
122
+ bool isZero = ZeroMeasurementHelper . CheckZeroMeasurementTwoSamples ( workloadMeasurements , overheadMeasurements , threshold ) ;
123
+ Assert . True ( isZero , $ "Actual time was not 0.") ;
124
+
125
+ isZero = ZeroMeasurementHelper . CheckZeroMeasurementTwoSamples ( overheadMeasurements , workloadMeasurements , threshold ) ;
126
+ Assert . True ( isZero , "Overhead took more time than workload." ) ;
127
+
128
+ Assert . True ( ( report . GcStats . GetBytesAllocatedPerOperation ( report . BenchmarkCase ) ?? 0L ) == 0L , "Memory allocations measured above 0." ) ;
129
+ }
130
+ }
131
+
132
+ [ Fact ]
133
+ public void DifferentSizedStructsBenchmarksReportsNonZeroTimeAndZeroAllocated_InProcess ( )
134
+ {
135
+ AssertDifferentSizedStructsResults ( ManualConfig . CreateEmpty ( )
136
+ . AddJob ( Job . Default
137
+ . WithToolchain ( InProcessEmitToolchain . Instance )
138
+ ) ) ;
139
+ }
140
+
141
+ [ TheoryNetCoreOnly ( "To not repeat tests in both full Framework and Core" ) ]
142
+ [ InlineData ( RuntimeMoniker . Net70 ) ]
143
+ [ InlineData ( RuntimeMoniker . Mono70 ) ]
144
+ public void DifferentSizedStructsBenchmarksReportsNonZeroTimeAndZeroAllocated_Core ( RuntimeMoniker runtimeMoniker )
145
+ {
146
+ AssertDifferentSizedStructsResults ( ManualConfig . CreateEmpty ( )
147
+ . AddJob ( Job . Default
148
+ . WithRuntime ( runtimeMoniker . GetRuntime ( ) )
149
+ ) ) ;
150
+ }
151
+
152
+ [ TheoryFullFrameworkOnly ( "Can only run full Framework and Mono tests from Framework host" ) ]
153
+ [ InlineData ( RuntimeMoniker . Net462 ) ]
154
+ [ InlineData ( RuntimeMoniker . Mono ) ]
155
+ public void DifferentSizedStructsBenchmarksReportsNonZeroTimeAndZeroAllocated_Framework ( RuntimeMoniker runtimeMoniker )
156
+ {
157
+ AssertDifferentSizedStructsResults ( ManualConfig . CreateEmpty ( )
158
+ . AddJob ( Job . Default
159
+ . WithRuntime ( runtimeMoniker . GetRuntime ( ) )
160
+ ) ) ;
161
+ }
162
+
163
+ private void AssertDifferentSizedStructsResults ( IConfig config )
164
+ {
165
+ var summary = CanExecute < DifferentSizedStructs > ( config
166
+ . WithSummaryStyle ( SummaryStyle . Default . WithTimeUnit ( TimeUnit . Nanosecond ) )
167
+ . AddDiagnoser ( new MemoryDiagnoser ( new MemoryDiagnoserConfig ( false ) ) )
168
+ ) ;
169
+
170
+ var cpuResolution = RuntimeInformation . GetCpuInfo ( ) . MaxFrequency ? . ToResolution ( ) ?? FallbackCpuResolutionValue ;
171
+ var threshold = Threshold . Create ( ThresholdUnit . Nanoseconds , cpuResolution . Nanoseconds ) ;
172
+
173
+ foreach ( var report in summary . Reports )
174
+ {
175
+ var workloadMeasurements = report . AllMeasurements . Where ( m => m . Is ( IterationMode . Workload , IterationStage . Actual ) ) . GetStatistics ( ) . WithoutOutliers ( ) ;
176
+ var overheadMeasurements = report . AllMeasurements . Where ( m => m . Is ( IterationMode . Overhead , IterationStage . Actual ) ) . GetStatistics ( ) . WithoutOutliers ( ) ;
177
+
178
+ bool isZero = ZeroMeasurementHelper . CheckZeroMeasurementTwoSamples ( workloadMeasurements , overheadMeasurements , threshold ) ;
179
+ Assert . False ( isZero , $ "Actual time was 0.") ;
180
+
181
+ isZero = ZeroMeasurementHelper . CheckZeroMeasurementTwoSamples ( overheadMeasurements , workloadMeasurements , threshold ) ;
182
+ Assert . True ( isZero , "Overhead took more time than workload." ) ;
183
+
184
+ Assert . True ( ( report . GcStats . GetBytesAllocatedPerOperation ( report . BenchmarkCase ) ?? 0L ) == 0L , "Memory allocations measured above 0." ) ;
185
+ }
186
+ }
187
+ }
188
+
189
+ public struct Struct16
190
+ {
191
+ public long l1 , l2 ;
192
+ }
193
+
194
+ public struct Struct32
195
+ {
196
+ public long l1 , l2 , l3 , l4 ;
197
+ }
198
+
199
+ public struct Struct64
200
+ {
201
+ public long l1 , l2 , l3 , l4 ,
202
+ l5 , l6 , l7 , l8 ;
203
+ }
204
+
205
+ public struct Struct128
206
+ {
207
+ public long l1 , l2 , l3 , l4 ,
208
+ l5 , l6 , l7 , l8 ,
209
+ l9 , l10 , l11 , l12 ,
210
+ l13 , l14 , l15 , l16 ;
211
+ }
212
+
213
+ public class DifferentSizedStructs
214
+ {
215
+ [ Benchmark ] public Struct16 Struct16 ( ) => default ;
216
+ [ Benchmark ] public Struct32 Struct32 ( ) => default ;
217
+ [ Benchmark ] public Struct64 Struct64 ( ) => default ;
218
+ [ Benchmark ] public Struct128 Struct128 ( ) => default ;
219
+ }
220
+ }
221
+
222
+ public class EmptyVoid { [ Benchmark ] public void Void ( ) { } }
223
+ public class EmptyByte { [ Benchmark ] public byte Byte ( ) => default ; }
224
+ public class EmptySByte { [ Benchmark ] public sbyte SByte ( ) => default ; }
225
+ public class EmptyShort { [ Benchmark ] public short Short ( ) => default ; }
226
+ public class EmptyUShort { [ Benchmark ] public ushort UShort ( ) => default ; }
227
+ public class EmptyChar { [ Benchmark ] public char Char ( ) => default ; }
228
+ public class EmptyInt32 { [ Benchmark ] public int Int32 ( ) => default ; }
229
+ public class EmptyUInt32 { [ Benchmark ] public uint UInt32 ( ) => default ; }
230
+ public class EmptyInt64 { [ Benchmark ] public long Int64 ( ) => default ; }
231
+ public class EmptyUInt64 { [ Benchmark ] public ulong UInt64 ( ) => default ; }
232
+ public class EmptyIntPtr { [ Benchmark ] public IntPtr IntPtr ( ) => default ; }
233
+ public class EmptyUIntPtr { [ Benchmark ] public UIntPtr UIntPtr ( ) => default ; }
234
+ public class EmptyVoidPointer { [ Benchmark ] public unsafe void * VoidPointer ( ) => default ; }
235
+ public class EmptyClass { [ Benchmark ] public object Class ( ) => default ; }
0 commit comments