From ba3a332936d7f9ee3d56b0f92559aae88d8aa8b6 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 3 Aug 2021 18:30:17 +0200 Subject: [PATCH 1/4] don't display Gen X collumn if there were no collections for X generation --- src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs index 078718539b..70e7c932b7 100644 --- a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs @@ -33,9 +33,13 @@ public class MemoryDiagnoser : IDiagnoser public IEnumerable ProcessResults(DiagnoserResults diagnoserResults) { - yield return new Metric(GarbageCollectionsMetricDescriptor.Gen0, diagnoserResults.GcStats.Gen0Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); - yield return new Metric(GarbageCollectionsMetricDescriptor.Gen1, diagnoserResults.GcStats.Gen1Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); - yield return new Metric(GarbageCollectionsMetricDescriptor.Gen2, diagnoserResults.GcStats.Gen2Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); + if (diagnoserResults.GcStats.Gen0Collections > 0) + yield return new Metric(GarbageCollectionsMetricDescriptor.Gen0, diagnoserResults.GcStats.Gen0Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); + if (diagnoserResults.GcStats.Gen1Collections > 0) + yield return new Metric(GarbageCollectionsMetricDescriptor.Gen1, diagnoserResults.GcStats.Gen1Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); + if (diagnoserResults.GcStats.Gen2Collections > 0) + yield return new Metric(GarbageCollectionsMetricDescriptor.Gen2, diagnoserResults.GcStats.Gen2Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); + yield return new Metric(AllocatedMemoryMetricDescriptor.Instance, diagnoserResults.GcStats.BytesAllocatedPerOperation); } From 2925b4b1bd4dc4948400492ea57f0de7ea64cb85 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 3 Aug 2021 18:56:37 +0200 Subject: [PATCH 2/4] add MemoryDiagnoserConfig, allow users to customize MemoryDiagnoser --- .../Attributes/MemoryDiagnoserAttribute.cs | 5 +++-- src/BenchmarkDotNet/Configs/ImmutableConfig.cs | 2 +- .../Diagnosers/MemoryDiagnoser.cs | 12 +++++++----- .../Diagnosers/MemoryDiagnoserConfig.cs | 16 ++++++++++++++++ 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 src/BenchmarkDotNet/Diagnosers/MemoryDiagnoserConfig.cs diff --git a/src/BenchmarkDotNet/Attributes/MemoryDiagnoserAttribute.cs b/src/BenchmarkDotNet/Attributes/MemoryDiagnoserAttribute.cs index 3cc7b4dcbd..52e2151441 100644 --- a/src/BenchmarkDotNet/Attributes/MemoryDiagnoserAttribute.cs +++ b/src/BenchmarkDotNet/Attributes/MemoryDiagnoserAttribute.cs @@ -9,9 +9,10 @@ public class MemoryDiagnoserAttribute : Attribute, IConfigSource { public IConfig Config { get; } - public MemoryDiagnoserAttribute() + /// Display Garbage Collections per Generation columns (Gen 0, Gen 1, Gen 2). True by default. + public MemoryDiagnoserAttribute(bool displayGenColumns = true) { - Config = ManualConfig.CreateEmpty().AddDiagnoser(MemoryDiagnoser.Default); + Config = ManualConfig.CreateEmpty().AddDiagnoser(new MemoryDiagnoser(new MemoryDiagnoserConfig(displayGenColumns))); } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs index cc6784c81a..f7ca6df67d 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs @@ -92,7 +92,7 @@ public sealed class ImmutableConfig : IConfig public IAnalyser GetCompositeAnalyser() => new CompositeAnalyser(analysers); public IDiagnoser GetCompositeDiagnoser() => new CompositeDiagnoser(diagnosers); - public bool HasMemoryDiagnoser() => diagnosers.Contains(MemoryDiagnoser.Default); + public bool HasMemoryDiagnoser() => diagnosers.OfType().Any(); public bool HasThreadingDiagnoser() => diagnosers.Contains(ThreadingDiagnoser.Default); diff --git a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs index 70e7c932b7..9384b27df2 100644 --- a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs @@ -15,9 +15,11 @@ public class MemoryDiagnoser : IDiagnoser { private const string DiagnoserId = nameof(MemoryDiagnoser); - public static readonly MemoryDiagnoser Default = new MemoryDiagnoser(); + public static readonly MemoryDiagnoser Default = new MemoryDiagnoser(new MemoryDiagnoserConfig(displayGenColumns: true)); - private MemoryDiagnoser() { } // we want to have only a single instance of MemoryDiagnoser + public MemoryDiagnoser(MemoryDiagnoserConfig config) => Config = config; + + public MemoryDiagnoserConfig Config { get; } public RunMode GetRunMode(BenchmarkCase benchmarkCase) => RunMode.NoOverhead; @@ -33,11 +35,11 @@ public class MemoryDiagnoser : IDiagnoser public IEnumerable ProcessResults(DiagnoserResults diagnoserResults) { - if (diagnoserResults.GcStats.Gen0Collections > 0) + if (diagnoserResults.GcStats.Gen0Collections > 0 && Config.DisplayGenColumns) yield return new Metric(GarbageCollectionsMetricDescriptor.Gen0, diagnoserResults.GcStats.Gen0Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); - if (diagnoserResults.GcStats.Gen1Collections > 0) + if (diagnoserResults.GcStats.Gen1Collections > 0 && Config.DisplayGenColumns) yield return new Metric(GarbageCollectionsMetricDescriptor.Gen1, diagnoserResults.GcStats.Gen1Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); - if (diagnoserResults.GcStats.Gen2Collections > 0) + if (diagnoserResults.GcStats.Gen2Collections > 0 && Config.DisplayGenColumns) yield return new Metric(GarbageCollectionsMetricDescriptor.Gen2, diagnoserResults.GcStats.Gen2Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); yield return new Metric(AllocatedMemoryMetricDescriptor.Instance, diagnoserResults.GcStats.BytesAllocatedPerOperation); diff --git a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoserConfig.cs b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoserConfig.cs new file mode 100644 index 0000000000..cb5eb7221e --- /dev/null +++ b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoserConfig.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +namespace BenchmarkDotNet.Diagnosers +{ + public class MemoryDiagnoserConfig + { + /// Display Garbage Collections per Generation columns (Gen 0, Gen 1, Gen 2). True by default. + [PublicAPI] + public MemoryDiagnoserConfig(bool displayGenColumns = true) + { + DisplayGenColumns = displayGenColumns; + } + + public bool DisplayGenColumns { get; } + } +} \ No newline at end of file From 51d39f063f329ae7f86dc74b59843eafc4c7d7f4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 4 Aug 2021 17:13:43 +0200 Subject: [PATCH 3/4] ensure the order of Metrics columns --- src/BenchmarkDotNet/Columns/MetricColumn.cs | 2 +- .../Diagnosers/AllocatedNativeMemoryDescriptor.cs | 2 ++ src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs | 3 +++ src/BenchmarkDotNet/Diagnosers/PmcMetricDescriptor.cs | 1 + src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs | 2 ++ src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs | 1 + src/BenchmarkDotNet/Reports/Metric.cs | 2 ++ 7 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Columns/MetricColumn.cs b/src/BenchmarkDotNet/Columns/MetricColumn.cs index 1be8241234..56acfebea1 100644 --- a/src/BenchmarkDotNet/Columns/MetricColumn.cs +++ b/src/BenchmarkDotNet/Columns/MetricColumn.cs @@ -16,7 +16,7 @@ public class MetricColumn : IColumn public string Legend => descriptor.Legend; public bool AlwaysShow => true; public ColumnCategory Category => ColumnCategory.Metric; - public int PriorityInCategory => 0; + public int PriorityInCategory => descriptor.PriorityInCategory; public bool IsNumeric => true; public UnitType UnitType => descriptor.UnitType; diff --git a/src/BenchmarkDotNet/Diagnosers/AllocatedNativeMemoryDescriptor.cs b/src/BenchmarkDotNet/Diagnosers/AllocatedNativeMemoryDescriptor.cs index c0e5ca05be..67e4e2d532 100644 --- a/src/BenchmarkDotNet/Diagnosers/AllocatedNativeMemoryDescriptor.cs +++ b/src/BenchmarkDotNet/Diagnosers/AllocatedNativeMemoryDescriptor.cs @@ -12,6 +12,7 @@ internal class AllocatedNativeMemoryDescriptor : IMetricDescriptor public UnitType UnitType => UnitType.Size; public string Unit => SizeUnit.B.Name; public bool TheGreaterTheBetter => false; + public int PriorityInCategory => 0; } internal class NativeMemoryLeakDescriptor : IMetricDescriptor @@ -23,5 +24,6 @@ internal class NativeMemoryLeakDescriptor : IMetricDescriptor public UnitType UnitType => UnitType.Size; public string Unit => SizeUnit.B.Name; public bool TheGreaterTheBetter => false; + public int PriorityInCategory => 0; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs index 9384b27df2..6432f1425c 100644 --- a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs @@ -56,6 +56,7 @@ private class AllocatedMemoryMetricDescriptor : IMetricDescriptor public UnitType UnitType => UnitType.Size; public string Unit => SizeUnit.B.Name; public bool TheGreaterTheBetter => false; + public int PriorityInCategory => GC.MaxGeneration + 1; } private class GarbageCollectionsMetricDescriptor : IMetricDescriptor @@ -69,6 +70,7 @@ private GarbageCollectionsMetricDescriptor(int generationId) Id = $"Gen{generationId}Collects"; DisplayName = $"Gen {generationId}"; Legend = $"GC Generation {generationId} collects per 1000 operations"; + PriorityInCategory = generationId; } public string Id { get; } @@ -78,6 +80,7 @@ private GarbageCollectionsMetricDescriptor(int generationId) public UnitType UnitType => UnitType.Dimensionless; public string Unit => "Count"; public bool TheGreaterTheBetter => false; + public int PriorityInCategory { get; } } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Diagnosers/PmcMetricDescriptor.cs b/src/BenchmarkDotNet/Diagnosers/PmcMetricDescriptor.cs index f459243072..3e15b5bf87 100644 --- a/src/BenchmarkDotNet/Diagnosers/PmcMetricDescriptor.cs +++ b/src/BenchmarkDotNet/Diagnosers/PmcMetricDescriptor.cs @@ -20,5 +20,6 @@ internal PmcMetricDescriptor(PreciseMachineCounter counter) public string NumberFormat => "N0"; public UnitType UnitType => UnitType.Dimensionless; public string Unit => "Count"; + public int PriorityInCategory => 0; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs index d9194dd868..017ddde4b6 100644 --- a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs @@ -61,6 +61,7 @@ private class CompletedWorkItemCountMetricDescriptor : IMetricDescriptor public UnitType UnitType => UnitType.Dimensionless; public string Unit => "Count"; public bool TheGreaterTheBetter => false; + public int PriorityInCategory => 0; } private class LockContentionCountMetricDescriptor : IMetricDescriptor @@ -74,6 +75,7 @@ private class LockContentionCountMetricDescriptor : IMetricDescriptor public UnitType UnitType => UnitType.Dimensionless; public string Unit => "Count"; public bool TheGreaterTheBetter => false; + public int PriorityInCategory => 0; } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs index 2374a7f6bf..541a7fb396 100644 --- a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs +++ b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs @@ -169,6 +169,7 @@ private class NativeCodeSizeMetricDescriptor : IMetricDescriptor public UnitType UnitType => UnitType.Size; public string Unit => SizeUnit.B.Name; public bool TheGreaterTheBetter => false; + public int PriorityInCategory => 0; } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Reports/Metric.cs b/src/BenchmarkDotNet/Reports/Metric.cs index ad69edd13e..688e421571 100644 --- a/src/BenchmarkDotNet/Reports/Metric.cs +++ b/src/BenchmarkDotNet/Reports/Metric.cs @@ -32,6 +32,8 @@ public interface IMetricDescriptor [PublicAPI] string Unit { get; } [PublicAPI] bool TheGreaterTheBetter { get; } + + [PublicAPI] int PriorityInCategory { get; } } public class MetricDescriptorEqualityComparer : EqualityComparer From 396446adc714dd3fe563227dba8bf3b56d4ce0ee Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 4 Aug 2021 17:23:12 +0200 Subject: [PATCH 4/4] fix the build --- tests/BenchmarkDotNet.Tests/Reports/FakeMetricDescriptor.cs | 1 + tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/BenchmarkDotNet.Tests/Reports/FakeMetricDescriptor.cs b/tests/BenchmarkDotNet.Tests/Reports/FakeMetricDescriptor.cs index aef9500879..c005345aba 100644 --- a/tests/BenchmarkDotNet.Tests/Reports/FakeMetricDescriptor.cs +++ b/tests/BenchmarkDotNet.Tests/Reports/FakeMetricDescriptor.cs @@ -19,5 +19,6 @@ public FakeMetricDescriptor(string id, string legend, string numberFormat = null public UnitType UnitType { get; } public string Unit { get; } public bool TheGreaterTheBetter { get; } + public int PriorityInCategory => 0; } } diff --git a/tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs b/tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs index 8b44c4c264..8497f83bd4 100644 --- a/tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs +++ b/tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs @@ -92,6 +92,7 @@ private sealed class FakeMetricDescriptor : IMetricDescriptor public UnitType UnitType { get; } public string Unit { get; } = nameof(Unit); public bool TheGreaterTheBetter { get; } + public int PriorityInCategory => 0; } } }