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/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/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/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 e922711b78..8544f81e54 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,9 +35,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 && Config.DisplayGenColumns) + yield return new Metric(GarbageCollectionsMetricDescriptor.Gen0, diagnoserResults.GcStats.Gen0Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); + 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 && Config.DisplayGenColumns) + yield return new Metric(GarbageCollectionsMetricDescriptor.Gen2, diagnoserResults.GcStats.Gen2Collections / (double)diagnoserResults.GcStats.TotalOperations * 1000); + yield return new Metric(AllocatedMemoryMetricDescriptor.Instance, diagnoserResults.GcStats.GetBytesAllocatedPerOperation(diagnoserResults.BenchmarkCase)); } @@ -50,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 @@ -63,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; } @@ -72,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/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 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 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; } } }