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