Skip to content

Commit 917988c

Browse files
committed
Updated MemoryDiagnoserTests.
Small code cleanup.
1 parent ce134ab commit 917988c

File tree

4 files changed

+40
-24
lines changed

4 files changed

+40
-24
lines changed

src/BenchmarkDotNet/Engines/Engine.cs

+4-5
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ private ClockSpan Measure(Action<long> action, long invokeCount)
228228
if (RuntimeInformation.IsNetCore && Environment.Version.Major is >= 3 and <= 6 && Environment.GetEnvironmentVariable("COMPlus_TieredCompilation") != "0")
229229
{
230230
// #1542
231-
// We put the current thread to sleep so tiered jit can kick in, compile it's stuff
231+
// We put the current thread to sleep so tiered jit can kick in, compile its stuff,
232232
// and NOT allocate anything on the background thread when we are measuring allocations.
233233
// This is only an issue on netcoreapp3.0 to net6.0. Tiered jit allocations were fixed in net7.0,
234234
// and netcoreapp2.X uses only GetAllocatedBytesForCurrentThread which doesn't capture the tiered jit allocations.
@@ -245,10 +245,9 @@ private ClockSpan Measure(Action<long> action, long invokeCount)
245245
IterationCleanupAction(); // we run iteration cleanup after collecting GC stats
246246

247247
var totalOperationsCount = data.InvokeCount * OperationsPerInvoke;
248-
gcStats = gcStats.WithTotalOperations(totalOperationsCount);
249-
ThreadingStats threadingStats = (finalThreadingStats - initialThreadingStats).WithTotalOperations(data.InvokeCount * OperationsPerInvoke);
250-
251-
return (gcStats, threadingStats, exceptionsStats.ExceptionsCount / (double)totalOperationsCount);
248+
return (gcStats.WithTotalOperations(totalOperationsCount),
249+
(finalThreadingStats - initialThreadingStats).WithTotalOperations(totalOperationsCount),
250+
exceptionsStats.ExceptionsCount / (double)totalOperationsCount);
252251
}
253252

254253
// Isolate the allocation measurement and skip tier0 jit to make sure we don't get any unexpected allocations.

tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs

+35-16
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ public class MemoryDiagnoserTests
3535
public static IEnumerable<object[]> GetToolchains()
3636
{
3737
yield return new object[] { Job.Default.GetToolchain() };
38-
yield return new object[] { InProcessEmitToolchain.Instance };
38+
// InProcessEmit reports flaky allocations in current .Net 8.
39+
if (!RuntimeInformation.IsNetCore)
40+
{
41+
yield return new object[] { InProcessEmitToolchain.Instance };
42+
}
3943
}
4044

4145
public class AccurateAllocations
@@ -104,7 +108,7 @@ private void AllocateUntilGcWakesUp()
104108
}
105109
}
106110

107-
[Theory(Skip = "#1542 Tiered JIT Thread allocates memory in the background"), MemberData(nameof(GetToolchains))]
111+
[Theory, MemberData(nameof(GetToolchains))]
108112
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
109113
public void MemoryDiagnoserDoesNotIncludeAllocationsFromSetupAndCleanup(IToolchain toolchain)
110114
{
@@ -117,23 +121,41 @@ public void MemoryDiagnoserDoesNotIncludeAllocationsFromSetupAndCleanup(IToolcha
117121
public class NoAllocationsAtAll
118122
{
119123
[Benchmark] public void EmptyMethod() { }
124+
125+
[Benchmark]
126+
public ulong TimeConsuming()
127+
{
128+
var r = 1ul;
129+
for (var i = 0; i < 50_000_000; i++)
130+
{
131+
r /= 1;
132+
}
133+
return r;
134+
}
120135
}
121136

122137
[Theory, MemberData(nameof(GetToolchains))]
123138
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
124139
public void EngineShouldNotInterfereAllocationResults(IToolchain toolchain)
125140
{
126-
if (RuntimeInformation.IsFullFramework && toolchain.IsInProcess)
127-
{
128-
return; // this test is flaky on Full Framework
129-
}
130-
131141
AssertAllocations(toolchain, typeof(NoAllocationsAtAll), new Dictionary<string, long>
132142
{
133143
{ nameof(NoAllocationsAtAll.EmptyMethod), 0 }
134144
});
135145
}
136146

147+
// #1542
148+
[Theory, MemberData(nameof(GetToolchains))]
149+
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
150+
public void TieredJitShouldNotInterfereAllocationResults(IToolchain toolchain)
151+
{
152+
AssertAllocations(toolchain, typeof(NoAllocationsAtAll), new Dictionary<string, long>
153+
{
154+
{ nameof(NoAllocationsAtAll.TimeConsuming), 0 }
155+
},
156+
iterationCount: 10); // 1 iteration is not enough to repro the problem
157+
}
158+
137159
public class NoBoxing
138160
{
139161
[Benchmark] public ValueTuple<int> ReturnsValueType() => new ValueTuple<int>(0);
@@ -216,8 +238,7 @@ public byte[] SixtyFourBytesArray()
216238
}
217239
}
218240

219-
[Theory(Skip = "#1542 Tiered JIT Thread allocates memory in the background"), MemberData(nameof(GetToolchains))]
220-
//[TheoryNetCoreOnly("Only .NET Core 2.0+ API is bug free for this case"), MemberData(nameof(GetToolchains))]
241+
[TheoryEnvSpecific("Full Framework cannot measure precisely enough for low invocation counts.", EnvRequirement.DotNetCoreOnly), MemberData(nameof(GetToolchains))]
221242
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
222243
public void AllocationQuantumIsNotAnIssueForNetCore21Plus(IToolchain toolchain)
223244
{
@@ -256,8 +277,7 @@ public void Allocate()
256277
}
257278
}
258279

259-
[TheoryEnvSpecific(".NET Core 3.0 preview6+ exposes a GC.GetTotalAllocatedBytes method which makes it possible to work",
260-
EnvRequirement.DotNetCore30Only)]
280+
[Theory(Skip = "Test is currently failing on all toolchains.")]
261281
[MemberData(nameof(GetToolchains))]
262282
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
263283
public void MemoryDiagnoserIsAccurateForMultiThreadedBenchmarks(IToolchain toolchain)
@@ -274,9 +294,9 @@ public void MemoryDiagnoserIsAccurateForMultiThreadedBenchmarks(IToolchain toolc
274294
});
275295
}
276296

277-
private void AssertAllocations(IToolchain toolchain, Type benchmarkType, Dictionary<string, long> benchmarksAllocationsValidators)
297+
private void AssertAllocations(IToolchain toolchain, Type benchmarkType, Dictionary<string, long> benchmarksAllocationsValidators, int iterationCount = 1)
278298
{
279-
var config = CreateConfig(toolchain);
299+
var config = CreateConfig(toolchain, iterationCount);
280300
var benchmarks = BenchmarkConverter.TypeToBenchmarks(benchmarkType, config);
281301

282302
var summary = BenchmarkRunner.Run(benchmarks);
@@ -312,16 +332,15 @@ private void AssertAllocations(IToolchain toolchain, Type benchmarkType, Diction
312332
}
313333
}
314334

315-
private IConfig CreateConfig(IToolchain toolchain)
335+
private IConfig CreateConfig(IToolchain toolchain, int iterationCount = 1) // Single iteration is enough for most of the tests.
316336
=> ManualConfig.CreateEmpty()
317337
.AddJob(Job.ShortRun
318338
.WithEvaluateOverhead(false) // no need to run idle for this test
319339
.WithWarmupCount(0) // don't run warmup to save some time for our CI runs
320-
.WithIterationCount(1) // single iteration is enough for us
340+
.WithIterationCount(iterationCount)
321341
.WithGcForce(false)
322342
.WithGcServer(false)
323343
.WithGcConcurrent(false)
324-
.WithEnvironmentVariable("COMPlus_TieredCompilation", "0") // Tiered JIT can allocate some memory on a background thread, let's disable it to make our tests less flaky (#1542)
325344
.WithToolchain(toolchain))
326345
.AddColumnProvider(DefaultColumnProviders.Instance)
327346
.AddDiagnoser(MemoryDiagnoser.Default)

tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ public enum EnvRequirement
77
NonLinux,
88
FullFrameworkOnly,
99
NonFullFramework,
10-
DotNetCoreOnly,
11-
DotNetCore30Only
10+
DotNetCoreOnly
1211
}

tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ public static class EnvRequirementChecker
1919
EnvRequirement.FullFrameworkOnly => BdnRuntimeInformation.IsFullFramework ? null : "Full .NET Framework-only test",
2020
EnvRequirement.NonFullFramework => !BdnRuntimeInformation.IsFullFramework ? null : "Non-Full .NET Framework test",
2121
EnvRequirement.DotNetCoreOnly => BdnRuntimeInformation.IsNetCore ? null : ".NET/.NET Core-only test",
22-
EnvRequirement.DotNetCore30Only => IsRuntime(RuntimeMoniker.NetCoreApp30) ? null : ".NET Core 3.0-only test",
2322
_ => throw new ArgumentOutOfRangeException(nameof(requirement), requirement, "Unknown value")
2423
};
2524

0 commit comments

Comments
 (0)