Skip to content

Commit 5ec44f5

Browse files
committed
Added manual tests for actual work.
1 parent bb720c3 commit 5ec44f5

File tree

1 file changed

+75
-28
lines changed

1 file changed

+75
-28
lines changed

tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using BenchmarkDotNet.Diagnosers;
99
using BenchmarkDotNet.Engines;
1010
using BenchmarkDotNet.Extensions;
11+
using BenchmarkDotNet.IntegrationTests.ManualRunning;
1112
using BenchmarkDotNet.Jobs;
1213
using BenchmarkDotNet.Portability;
1314
using BenchmarkDotNet.Reports;
@@ -48,15 +49,15 @@ private static IEnumerable<Type> EmptyBenchmarkTypes() =>
4849
typeof(EmptyClass)
4950
};
5051

51-
public static IEnumerable<object[]> InProcessData()
52+
public static IEnumerable<object[]> EmptyInProcessData()
5253
{
5354
foreach (var type in EmptyBenchmarkTypes())
5455
{
5556
yield return new object[] { type };
5657
}
5758
}
5859

59-
public static IEnumerable<object[]> CoreData()
60+
public static IEnumerable<object[]> EmptyCoreData()
6061
{
6162
foreach (var type in EmptyBenchmarkTypes())
6263
{
@@ -65,7 +66,7 @@ public static IEnumerable<object[]> CoreData()
6566
}
6667
}
6768

68-
public static IEnumerable<object[]> FrameworkData()
69+
public static IEnumerable<object[]> EmptyFrameworkData()
6970
{
7071
foreach (var type in EmptyBenchmarkTypes())
7172
{
@@ -75,7 +76,7 @@ public static IEnumerable<object[]> FrameworkData()
7576
}
7677

7778
[Theory]
78-
[MemberData(nameof(InProcessData))]
79+
[MemberData(nameof(EmptyInProcessData))]
7980
public void EmptyBenchmarksReportZeroTimeAndAllocated_InProcess(Type benchmarkType)
8081
{
8182
AssertZeroResults(benchmarkType, ManualConfig.CreateEmpty()
@@ -85,7 +86,7 @@ public void EmptyBenchmarksReportZeroTimeAndAllocated_InProcess(Type benchmarkTy
8586
}
8687

8788
[TheoryEnvSpecific("To not repeat tests in both Full .NET Framework and Core", EnvRequirement.DotNetCoreOnly)]
88-
[MemberData(nameof(CoreData))]
89+
[MemberData(nameof(EmptyCoreData))]
8990
public void EmptyBenchmarksReportZeroTimeAndAllocated_Core(Type benchmarkType, RuntimeMoniker runtimeMoniker)
9091
{
9192
AssertZeroResults(benchmarkType, ManualConfig.CreateEmpty()
@@ -95,7 +96,7 @@ public void EmptyBenchmarksReportZeroTimeAndAllocated_Core(Type benchmarkType, R
9596
}
9697

9798
[TheoryEnvSpecific("Can only run Full .NET Framework and Mono tests from Framework host", EnvRequirement.FullFrameworkOnly)]
98-
[MemberData(nameof(FrameworkData))]
99+
[MemberData(nameof(EmptyFrameworkData))]
99100
public void EmptyBenchmarksReportZeroTimeAndAllocated_Framework(Type benchmarkType, RuntimeMoniker runtimeMoniker)
100101
{
101102
AssertZeroResults(benchmarkType, ManualConfig.CreateEmpty()
@@ -129,57 +130,91 @@ private void AssertZeroResults(Type benchmarkType, IConfig config)
129130
}
130131
}
131132

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)
134169
{
135-
AssertDifferentSizedStructsResults(ManualConfig.CreateEmpty()
170+
AssertNonZeroResults(benchmarkType, ManualConfig.CreateEmpty()
136171
.AddJob(Job.Default
137172
.WithToolchain(InProcessEmitToolchain.Instance)
138173
));
139174
}
140175

141176
[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)
145179
{
146-
AssertDifferentSizedStructsResults(ManualConfig.CreateEmpty()
180+
AssertNonZeroResults(benchmarkType, ManualConfig.CreateEmpty()
147181
.AddJob(Job.Default
148182
.WithRuntime(runtimeMoniker.GetRuntime())
149183
));
150184
}
151185

152186
[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)
156189
{
157-
AssertDifferentSizedStructsResults(ManualConfig.CreateEmpty()
190+
AssertNonZeroResults(benchmarkType, ManualConfig.CreateEmpty()
158191
.AddJob(Job.Default
159192
.WithRuntime(runtimeMoniker.GetRuntime())
160193
));
161194
}
162195

163-
private void AssertDifferentSizedStructsResults(IConfig config)
196+
private void AssertNonZeroResults(Type benchmarkType, IConfig config)
164197
{
165-
var summary = CanExecute<DifferentSizedStructs>(config
198+
var summary = CanExecute(benchmarkType, config
166199
.WithSummaryStyle(SummaryStyle.Default.WithTimeUnit(TimeUnit.Nanosecond))
167200
.AddDiagnoser(new MemoryDiagnoser(new MemoryDiagnoserConfig(false)))
168201
);
169202

170203
var cpuResolution = RuntimeInformation.GetCpuInfo().MaxFrequency?.ToResolution() ?? FallbackCpuResolutionValue;
171-
var threshold = Threshold.Create(ThresholdUnit.Nanoseconds, cpuResolution.Nanoseconds);
172204

173205
foreach (var report in summary.Reports)
174206
{
175207
var workloadMeasurements = report.AllMeasurements.Where(m => m.Is(IterationMode.Workload, IterationStage.Actual)).GetStatistics().WithoutOutliers();
176208
var overheadMeasurements = report.AllMeasurements.Where(m => m.Is(IterationMode.Overhead, IterationStage.Actual)).GetStatistics().WithoutOutliers();
177209

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);
179215
Assert.False(isZero, $"Actual time was 0.");
180216

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.
183218

184219
Assert.True((report.GcStats.GetBytesAllocatedPerOperation(report.BenchmarkCase) ?? 0L) == 0L, "Memory allocations measured above 0.");
185220
}
@@ -209,13 +244,25 @@ public struct Struct128
209244
l9, l10, l11, l12,
210245
l13, l14, l15, l16;
211246
}
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;
212260

213-
public class DifferentSizedStructs
261+
[Benchmark]
262+
public void IncrementField()
214263
{
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++;
219266
}
220267
}
221268

0 commit comments

Comments
 (0)