Skip to content

Commit

Permalink
Add Allocation Ratio column (dotnet#1859)
Browse files Browse the repository at this point in the history
Co-authored-by: Adam Sitnik <[email protected]>
  • Loading branch information
YegorStepanov and adamsitnik authored Dec 7, 2021
1 parent f00f7c7 commit 9560515
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 15 deletions.
88 changes: 88 additions & 0 deletions src/BenchmarkDotNet/Columns/BaselineAllocationRatioColumn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Mathematics;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using JetBrains.Annotations;

namespace BenchmarkDotNet.Columns
{
public class BaselineAllocationRatioColumn : BaselineCustomColumn
{
public override string Id => nameof(BaselineAllocationRatioColumn);
public override string ColumnName => "Alloc Ratio";

public override string GetValue(Summary summary, BenchmarkCase benchmarkCase, Statistics baseline, IReadOnlyDictionary<string, Metric> baselineMetrics,
Statistics current, IReadOnlyDictionary<string, Metric> currentMetrics, bool isBaseline)
{
double? ratio = GetAllocationRatio(currentMetrics, baselineMetrics);
double? invertedRatio = GetAllocationRatio(baselineMetrics, currentMetrics);

if (ratio == null)
return "NA";

var cultureInfo = summary.GetCultureInfo();
var ratioStyle = summary?.Style?.RatioStyle ?? RatioStyle.Value;

bool advancedPrecision = IsNonBaselinesPrecise(summary, baselineMetrics, benchmarkCase);
switch (ratioStyle)
{
case RatioStyle.Value:
return ratio.Value.ToString(advancedPrecision ? "N3" : "N2", cultureInfo);
case RatioStyle.Percentage:
return isBaseline
? ""
: ratio.Value >= 1.0
? "+" + ((ratio.Value - 1.0) * 100).ToString(advancedPrecision ? "N1" : "N0", cultureInfo) + "%"
: "-" + ((1.0 - ratio.Value) * 100).ToString(advancedPrecision ? "N1" : "N0", cultureInfo) + "%";
case RatioStyle.Trend:
return isBaseline
? ""
: ratio.Value >= 1.0
? ratio.Value.ToString(advancedPrecision ? "N3" : "N2", cultureInfo) + "x more"
: invertedRatio == null
? "NA"
: invertedRatio.Value.ToString(advancedPrecision ? "N3" : "N2", cultureInfo) + "x less";
default:
throw new ArgumentOutOfRangeException(nameof(summary), ratioStyle, "RatioStyle is not supported");
}
}

private static bool IsNonBaselinesPrecise(Summary summary, IReadOnlyDictionary<string, Metric> baselineMetric, BenchmarkCase benchmarkCase)
{
string logicalGroupKey = summary.GetLogicalGroupKey(benchmarkCase);
var nonBaselines = summary.GetNonBaselines(logicalGroupKey);
return nonBaselines.Any(c => GetAllocationRatio(summary[c].Metrics, baselineMetric) is > 0 and < 0.01);
}

private static double? GetAllocationRatio(
[CanBeNull] IReadOnlyDictionary<string, Metric> current,
[CanBeNull] IReadOnlyDictionary<string, Metric> baseline)
{
double? currentBytes = GetAllocatedBytes(current);
double? baselineBytes = GetAllocatedBytes(baseline);

if (currentBytes == null || baselineBytes == null)
return null;

if (baselineBytes == 0)
return null;

return currentBytes / baselineBytes;
}

private static double? GetAllocatedBytes([CanBeNull] IReadOnlyDictionary<string, Metric> metrics)
{
var metric = metrics?.Values.FirstOrDefault(m => m.Descriptor is AllocatedMemoryMetricDescriptor);
return metric?.Value;
}

public override ColumnCategory Category => ColumnCategory.Metric; //it should be displayed after Allocated column
public override int PriorityInCategory => AllocatedMemoryMetricDescriptor.Instance.PriorityInCategory + 1;
public override bool IsNumeric => true;
public override UnitType UnitType => UnitType.Dimensionless;
public override string Legend => "Allocated memory ratio distribution ([Current]/[Baseline])";
}
}
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Columns/BaselineCustomColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public abstract string GetValue(Summary summary, BenchmarkCase benchmarkCase, St

public bool IsAvailable(Summary summary) => summary.HasBaselines();
public bool AlwaysShow => true;
public ColumnCategory Category => ColumnCategory.Baseline;
public virtual ColumnCategory Category => ColumnCategory.Baseline;
public abstract int PriorityInCategory { get; }
public abstract bool IsNumeric { get; }
public abstract UnitType UnitType { get; }
Expand Down
10 changes: 10 additions & 0 deletions src/BenchmarkDotNet/Columns/DefaultColumnProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,23 @@ public IEnumerable<IColumn> GetColumns(Summary summary)
bool hide = stdDevColumnValues.All(value => value == "0.00" || value == "0.01");
if (!hide)
yield return BaselineRatioColumn.RatioStdDev;

if (HasMemoryDiagnoser(summary))
{
yield return new BaselineAllocationRatioColumn();
}
}
}

private static bool NeedToShow(Summary summary, Func<Statistics, bool> check)
{
return summary.Reports != null && summary.Reports.Any(r => r.ResultStatistics != null && check(r.ResultStatistics));
}

private static bool HasMemoryDiagnoser(Summary summary)
{
return summary.BenchmarksCases.Any(c => c.Config.HasMemoryDiagnoser());
}
}

private class ParamsColumnProvider : IColumnProvider
Expand Down
20 changes: 20 additions & 0 deletions src/BenchmarkDotNet/Diagnosers/AllocatedMemoryMetricDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Reports;

namespace BenchmarkDotNet.Diagnosers
{
internal class AllocatedMemoryMetricDescriptor : IMetricDescriptor
{
internal static readonly IMetricDescriptor Instance = new AllocatedMemoryMetricDescriptor();

public string Id => "Allocated Memory";
public string DisplayName => "Allocated";
public string Legend => "Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)";
public string NumberFormat => "0.##";
public UnitType UnitType => UnitType.Size;
public string Unit => SizeUnit.B.Name;
public bool TheGreaterTheBetter => false;
public int PriorityInCategory => GC.MaxGeneration + 1;
}
}
14 changes: 0 additions & 14 deletions src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,6 @@ public IEnumerable<Metric> ProcessResults(DiagnoserResults diagnoserResults)
yield return new Metric(AllocatedMemoryMetricDescriptor.Instance, diagnoserResults.GcStats.GetBytesAllocatedPerOperation(diagnoserResults.BenchmarkCase));
}

private class AllocatedMemoryMetricDescriptor : IMetricDescriptor
{
internal static readonly IMetricDescriptor Instance = new AllocatedMemoryMetricDescriptor();

public string Id => "Allocated Memory";
public string DisplayName => "Allocated";
public string Legend => "Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)";
public string NumberFormat => "0.##";
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
{
internal static readonly IMetricDescriptor Gen0 = new GarbageCollectionsMetricDescriptor(0);
Expand Down

0 comments on commit 9560515

Please sign in to comment.