Skip to content

Commit

Permalink
Bump Perfolizer + Initial Phd adoption
Browse files Browse the repository at this point in the history
This commit is a first step of the grand API refactoring.

* Cpu Detection Upgrade
  * All CPU-related detection logic moved from RuntimeInformation to CpuDetector
  * BrandString formatting moved to Perfolizer (see CpuBrandHelper)
  * New serializable class for Cpu information: PhdCpu from Perfolizer
* Os Detection Upgrade
  * All OS-related detection logic moved from RuntimeInformation to OsDetector
  * BrandString formatting moved to Perfolizer (see OsBrandHelper)
  * New serializable class for Os information: PhdOs from Perfolizer
* Initial adoption of Phd (Performance History Data)
  * Phd from Perfolizer is our future way to keep and process gathered data and measurements
  * Only the initial implementation is available, it still misses a lot of features
  * Examples of Phd json and corresponding new Summary Tables are in
    BenchmarkDotNet/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest*
  * Phd can be tried via [PhdExporter]
  * Documentation is coming once we have a more complete implementation
  • Loading branch information
AndreyAkinshin committed Aug 27, 2024
1 parent a58872b commit eba5edb
Show file tree
Hide file tree
Showing 145 changed files with 8,663 additions and 1,417 deletions.
3 changes: 2 additions & 1 deletion src/BenchmarkDotNet.Diagnostics.Windows/HardwareCounters.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Detectors;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Toolchains.InProcess.Emit;
Expand Down Expand Up @@ -31,7 +32,7 @@ private static readonly Dictionary<HardwareCounter, string> EtwTranslations

public static IEnumerable<ValidationError> Validate(ValidationParameters validationParameters, bool mandatory)
{
if (!RuntimeInformation.IsWindows())
if (!OsDetector.IsWindows())
{
yield return new ValidationError(true, "Hardware Counters and EtwProfiler are supported only on Windows");
yield break;
Expand Down
3 changes: 2 additions & 1 deletion src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using BenchmarkDotNet.Detectors;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Loggers;
Expand Down Expand Up @@ -33,7 +34,7 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters)

public IEnumerable<ValidationError> Validate(ValidationParameters validationParameters)
{
if (!RuntimeInformation.IsWindows())
if (!OsDetector.IsWindows())
{
yield return new ValidationError(true, $"{GetType().Name} is supported only on Windows");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
using System.Collections.Immutable;
using System.Linq;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Detectors;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Validators;
Expand Down Expand Up @@ -124,10 +124,10 @@ internal static bool IsSupported(RuntimeMoniker runtimeMoniker)
case RuntimeMoniker.NetCoreApp20:
case RuntimeMoniker.NetCoreApp21:
case RuntimeMoniker.NetCoreApp22:
return RuntimeInformation.IsWindows();
return OsDetector.IsWindows();
case RuntimeMoniker.NetCoreApp30:
case RuntimeMoniker.NetCoreApp31:
return RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux();
return OsDetector.IsWindows() || OsDetector.IsLinux();
default:
throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported");
}
Expand Down
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
using System.Collections.Immutable;
using System.Linq;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Detectors;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains;
Expand Down Expand Up @@ -118,10 +118,10 @@ internal static bool IsSupported(RuntimeMoniker runtimeMoniker)
case RuntimeMoniker.NetCoreApp20:
case RuntimeMoniker.NetCoreApp21:
case RuntimeMoniker.NetCoreApp22:
return RuntimeInformation.IsWindows();
return OsDetector.IsWindows();
case RuntimeMoniker.NetCoreApp30:
case RuntimeMoniker.NetCoreApp31:
return RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux();
return OsDetector.IsWindows() || OsDetector.IsLinux();
default:
throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported");
}
Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Analysers/ZeroMeasurementAnalyser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private ZeroMeasurementAnalyser() { }

protected override IEnumerable<Conclusion> AnalyseReport(BenchmarkReport report, Summary summary)
{
var currentFrequency = summary.HostEnvironmentInfo.CpuInfo.Value.MaxFrequency;
var currentFrequency = summary.HostEnvironmentInfo.Cpu.Value.MaxFrequency();
if (!currentFrequency.HasValue || currentFrequency <= 0)
currentFrequency = FallbackCpuResolutionValue.ToFrequency();

Expand Down
10 changes: 10 additions & 0 deletions src/BenchmarkDotNet/Attributes/Exporters/PhdExporterAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using BenchmarkDotNet.Exporters;

namespace BenchmarkDotNet.Attributes;

/// <summary>
/// IMPORTANT: Not fully implemented yet
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)]
public class PhdExporterAttribute() : ExporterConfigBaseAttribute(new PhdJsonExporter(), new PhdMdExporter());
3 changes: 1 addition & 2 deletions src/BenchmarkDotNet/BenchmarkDotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Templates\*" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
<EmbeddedResource Include="Environments\microarchitectures.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Gee.External.Capstone" Version="2.3.0" />
<PackageReference Include="Iced" Version="1.17.0" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.2.332302" />
<PackageReference Include="Perfolizer" Version="[0.3.17]" />
<PackageReference Include="Perfolizer" Version="[0.4.0]" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.8" PrivateAssets="contentfiles;analyzers" />
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<!-- Do not update these packages, or else netcoreapp3.0 and older will no longer work -->
Expand Down
1 change: 1 addition & 0 deletions src/BenchmarkDotNet/Columns/BaselineColumn.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using JetBrains.Annotations;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet/Columns/BaselineCustomColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using JetBrains.Annotations;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down Expand Up @@ -39,6 +40,7 @@ public abstract string GetValue(Summary summary, BenchmarkCase benchmarkCase, St
public abstract bool IsNumeric { get; }
public abstract UnitType UnitType { get; }
public abstract string Legend { get; }

public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style) => GetValue(summary, benchmarkCase);
public override string ToString() => ColumnName;
public bool IsDefault(Summary summary, BenchmarkCase benchmarkCase) => false;
Expand Down
1 change: 1 addition & 0 deletions src/BenchmarkDotNet/Columns/CategoriesColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down
1 change: 1 addition & 0 deletions src/BenchmarkDotNet/Columns/IColumn.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down
1 change: 1 addition & 0 deletions src/BenchmarkDotNet/Columns/JobCharacteristicColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down
1 change: 1 addition & 0 deletions src/BenchmarkDotNet/Columns/LogicalGroupColumn.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using JetBrains.Annotations;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet/Columns/MetricColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using BenchmarkDotNet.Running;
using Perfolizer.Horology;
using Perfolizer.Metrology;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand All @@ -18,6 +19,7 @@ public class MetricColumn : IColumn
public string Id => descriptor.Id;
public string ColumnName => descriptor.DisplayName;
public string Legend => descriptor.Legend;

public bool AlwaysShow => true;
public ColumnCategory Category => ColumnCategory.Metric;
public int PriorityInCategory => descriptor.PriorityInCategory;
Expand Down
1 change: 1 addition & 0 deletions src/BenchmarkDotNet/Columns/ParamColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using BenchmarkDotNet.Parameters;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down
1 change: 1 addition & 0 deletions src/BenchmarkDotNet/Columns/RankColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using JetBrains.Annotations;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down
43 changes: 23 additions & 20 deletions src/BenchmarkDotNet/Columns/StatisticColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Perfolizer.Mathematics.Common;
using Perfolizer.Mathematics.Multimodality;
using Perfolizer.Metrology;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand All @@ -28,48 +29,48 @@ private enum Priority
Additional
}

public static readonly IStatisticColumn Mean = new StatisticColumn(Column.Mean, "Arithmetic mean of all measurements",
public static readonly IStatisticColumn Mean = new StatisticColumn(Column.Mean, "=mean", "Arithmetic mean of all measurements",
s => s.Mean, Priority.Main);

public static readonly IColumn StdErr = new StatisticColumn(Column.StdErr, "Standard error of all measurements",
public static readonly IColumn StdErr = new StatisticColumn(Column.StdErr, null, "Standard error of all measurements",
s => s.StandardError, Priority.Main, parentColumn: Mean);

public static readonly IColumn StdDev = new StatisticColumn(Column.StdDev, "Standard deviation of all measurements",
public static readonly IColumn StdDev = new StatisticColumn(Column.StdDev, "=stddev", "Standard deviation of all measurements",
s => s.StandardDeviation, Priority.Main, parentColumn: Mean);

public static readonly IColumn Error = new StatisticColumn(Column.Error, "Half of 99.9% confidence interval",
public static readonly IColumn Error = new StatisticColumn(Column.Error, null, "Half of 99.9% confidence interval",
s => s.GetConfidenceInterval(ConfidenceLevel.L999).Margin, Priority.Main, parentColumn: Mean);

public static readonly IColumn OperationsPerSecond = new StatisticColumn(Column.OperationPerSecond, "Operation per second",
public static readonly IColumn OperationsPerSecond = new StatisticColumn(Column.OperationPerSecond, null, "Operation per second",
s => 1.0 * 1000 * 1000 * 1000 / s.Mean, Priority.Additional, UnitType.Dimensionless);

public static readonly IColumn Min = new StatisticColumn(Column.Min, "Minimum",
public static readonly IColumn Min = new StatisticColumn(Column.Min, "=min", "Minimum",
s => s.Min, Priority.Quartile);

public static readonly IColumn Q1 = new StatisticColumn(Column.Q1, "Quartile 1 (25th percentile)",
public static readonly IColumn Q1 = new StatisticColumn(Column.Q1, "=q1", "Quartile 1 (25th percentile)",
s => s.Q1, Priority.Quartile);

public static readonly IColumn Median = new StatisticColumn(Column.Median, "Value separating the higher half of all measurements (50th percentile)",
public static readonly IColumn Median = new StatisticColumn(Column.Median, "=median", "Value separating the higher half of all measurements (50th percentile)",
s => s.Median, Priority.Quartile);

public static readonly IColumn Q3 = new StatisticColumn(Column.Q3, "Quartile 3 (75th percentile)",
public static readonly IColumn Q3 = new StatisticColumn(Column.Q3, "=q3", "Quartile 3 (75th percentile)",
s => s.Q3, Priority.Quartile);

public static readonly IColumn Max = new StatisticColumn(Column.Max, "Maximum", s => s.Max, Priority.Quartile);
public static readonly IColumn Max = new StatisticColumn(Column.Max, "=max", "Maximum", s => s.Max, Priority.Quartile);

public static readonly IColumn Skewness = new StatisticColumn(Column.Skewness, "Measure of the asymmetry (third standardized moment)",
public static readonly IColumn Skewness = new StatisticColumn(Column.Skewness, null, "Measure of the asymmetry (third standardized moment)",
s => s.Skewness, Priority.Additional, UnitType.Dimensionless);

public static readonly IColumn Kurtosis = new StatisticColumn(Column.Kurtosis, "Measure of the tailedness ( fourth standardized moment)",
public static readonly IColumn Kurtosis = new StatisticColumn(Column.Kurtosis, null, "Measure of the tailedness ( fourth standardized moment)",
s => s.Kurtosis, Priority.Additional, UnitType.Dimensionless);

/// <summary>
/// See http://www.brendangregg.com/FrequencyTrails/modes.html
/// </summary>
public static readonly IColumn MValue = new StatisticColumn(Column.MValue, "Modal value, see http://www.brendangregg.com/FrequencyTrails/modes.html",
public static readonly IColumn MValue = new StatisticColumn(Column.MValue, null, "Modal value, see http://www.brendangregg.com/FrequencyTrails/modes.html",
s => MValueCalculator.Calculate(s.Sample.Values), Priority.Additional, UnitType.Dimensionless);

public static readonly IColumn Iterations = new StatisticColumn(Column.Iterations, "Number of target iterations",
public static readonly IColumn Iterations = new StatisticColumn(Column.Iterations, null, "Number of target iterations",
s => s.N, Priority.Additional, UnitType.Dimensionless);

public static readonly IColumn P0 = CreatePercentileColumn(0, Column.P0, s => s.Percentiles.P0);
Expand All @@ -84,17 +85,17 @@ private enum Priority

[PublicAPI]
public static IColumn CiLower(ConfidenceLevel level) => new StatisticColumn(
$"CI{level} Lower", $"Lower bound of {level} confidence interval",
$"CI{level} Lower", null, $"Lower bound of {level} confidence interval",
s => new ConfidenceInterval(s.Mean, s.StandardError, s.N, level).Lower, Priority.Additional);

[PublicAPI]
public static IColumn CiUpper(ConfidenceLevel level) => new StatisticColumn(
$"CI{level} Upper", $"Upper bound of {level} confidence interval",
$"CI{level} Upper", null, $"Upper bound of {level} confidence interval",
s => new ConfidenceInterval(s.Mean, s.StandardError, s.N, level).Upper, Priority.Additional);

[PublicAPI]
public static IColumn CiError(ConfidenceLevel level) => new StatisticColumn(
$"CI{level} Margin", $"Half of {level} confidence interval",
$"CI{level} Margin", null, $"Half of {level} confidence interval",
s => new ConfidenceInterval(s.Mean, s.StandardError, s.N, level).Margin, Priority.Additional);


Expand All @@ -104,14 +105,16 @@ private enum Priority
public string Id => nameof(StatisticColumn) + "." + ColumnName;
public string ColumnName { get; }
private readonly Priority priority;
private readonly IStatisticColumn parentColumn;
private readonly IStatisticColumn? parentColumn;
private readonly string? phdSelector;

private StatisticColumn(string columnName, string legend, Func<Statistics, double> calc, Priority priority, UnitType type = UnitType.Time,
private StatisticColumn(string columnName, string? phdSelector, string legend, Func<Statistics, double> calc, Priority priority, UnitType type = UnitType.Time,
IStatisticColumn? parentColumn = null)
{
this.calc = calc;
this.priority = priority;
this.parentColumn = parentColumn;
this.phdSelector = phdSelector;
UnitType = type;
ColumnName = columnName;
Legend = legend;
Expand Down Expand Up @@ -167,6 +170,6 @@ private string Format(Summary summary, ImmutableConfig config, Statistics? stati
public bool IsDefault(Summary summary, BenchmarkCase benchmarkCase) => false;

private static IColumn CreatePercentileColumn(int percentiles, string columnName, Func<Statistics, double> calc) => new StatisticColumn(
columnName, "Percentile " + percentiles, calc, Priority.Percentiles);
columnName, "=p" + percentiles, "Percentile " + percentiles, calc, Priority.Percentiles);
}
}
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet/Columns/TagColumn.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
Expand Down Expand Up @@ -28,6 +29,7 @@ public TagColumn(string columnName, Func<string, string> getTag)
public bool IsNumeric => false;
public UnitType UnitType => UnitType.Dimensionless;
public string Legend => $"Custom '{ColumnName}' tag column";

public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style) => GetValue(summary, benchmarkCase);
public override string ToString() => ColumnName;
}
Expand Down
12 changes: 8 additions & 4 deletions src/BenchmarkDotNet/Columns/TargetMethodColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using Perfolizer.Phd.Tables;

namespace BenchmarkDotNet.Columns
{
public class TargetMethodColumn : IColumn
{
public static readonly IColumn Namespace = new TargetMethodColumn(Column.Namespace, benchmark => benchmark.Descriptor.Type.Namespace);
public static readonly IColumn Type = new TargetMethodColumn(Column.Type, benchmark => benchmark.Descriptor.Type.GetDisplayName());
public static readonly IColumn Method = new TargetMethodColumn(Column.Method, benchmark => benchmark.Descriptor.WorkloadMethodDisplayInfo, true);
public static readonly IColumn Namespace = new TargetMethodColumn(Column.Namespace, ".benchmark.namespace", benchmark => benchmark.Descriptor.Type.Namespace);
public static readonly IColumn Type = new TargetMethodColumn(Column.Type, ".benchmark.type", benchmark => benchmark.Descriptor.Type.GetDisplayName());
public static readonly IColumn Method = new TargetMethodColumn(Column.Method, ".benchmark.method", benchmark => benchmark.Descriptor.WorkloadMethodDisplayInfo, true);

private readonly Func<BenchmarkCase, string> valueProvider;
public string Id => nameof(TargetMethodColumn) + "." + ColumnName;
public string ColumnName { get; }
public string PhdSelector { get; }
public string GetValue(Summary summary, BenchmarkCase benchmarkCase) => valueProvider(benchmarkCase);
public bool IsAvailable(Summary summary) => true;
public bool AlwaysShow { get; }
Expand All @@ -22,11 +24,13 @@ public class TargetMethodColumn : IColumn
public bool IsNumeric => false;
public UnitType UnitType => UnitType.Dimensionless;
public string Legend => "";

public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style) => GetValue(summary, benchmarkCase);

private TargetMethodColumn(string columnName, Func<BenchmarkCase, string> valueProvider, bool alwaysShow = false)
private TargetMethodColumn(string columnName, string phdSelector, Func<BenchmarkCase, string> valueProvider, bool alwaysShow = false)
{
this.valueProvider = valueProvider;
PhdSelector = phdSelector;
AlwaysShow = alwaysShow;
ColumnName = columnName;
}
Expand Down
Loading

0 comments on commit eba5edb

Please sign in to comment.