Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet.Diagnostics.Windows/EtwProfilerConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class EtwProfilerConfig

public KernelTraceEventParser.Keywords KernelStackKeywords { get; }

public IReadOnlyDictionary<HardwareCounter, Func<ProfileSourceInfo, int>> IntervalSelectors { get; }
public IReadOnlyDictionary<string, Func<ProfileSourceInfo, int>> IntervalSelectors { get; }

public IReadOnlyCollection<(Guid providerGuid, TraceEventLevel providerLevel, ulong keywords, TraceEventProviderOptions options)> Providers { get; }

Expand All @@ -41,7 +41,7 @@ public EtwProfilerConfig(
float cpuSampleIntervalInMilliseconds = 1.0f,
KernelTraceEventParser.Keywords kernelKeywords = KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Profile,
KernelTraceEventParser.Keywords kernelStackKeywords = KernelTraceEventParser.Keywords.Profile,
IReadOnlyDictionary<HardwareCounter, Func<ProfileSourceInfo, int>>? intervalSelectors = null,
IReadOnlyDictionary<string, Func<ProfileSourceInfo, int>>? intervalSelectors = null,
IReadOnlyCollection<(Guid providerGuid, TraceEventLevel providerLevel, ulong keywords, TraceEventProviderOptions options)>? providers = null,
bool createHeapSession = false)
{
Expand All @@ -51,7 +51,7 @@ public EtwProfilerConfig(
PerformExtraBenchmarksRun = performExtraBenchmarksRun;
BufferSizeInMb = bufferSizeInMb;
CpuSampleIntervalInMilliseconds = cpuSampleIntervalInMilliseconds;
IntervalSelectors = intervalSelectors ?? new Dictionary<HardwareCounter, Func<ProfileSourceInfo, int>>
IntervalSelectors = intervalSelectors ?? new Dictionary<string, Func<ProfileSourceInfo, int>>
{
// following values come from xunit-performance, were selected based on a many trace files from benchmark runs
// to keep good balance between accuracy and trace file size
Expand Down
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet.Diagnostics.Windows/HardwareCounters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ namespace BenchmarkDotNet.Diagnostics.Windows
{
public static class HardwareCounters
{
private static readonly Dictionary<HardwareCounter, string> EtwTranslations
= new Dictionary<HardwareCounter, string>
private static readonly Dictionary<string, string> EtwTranslations
= new Dictionary<string, string>
{
{ HardwareCounter.Timer, "Timer" },
{ HardwareCounter.TotalIssues, "TotalIssues" },
Expand Down Expand Up @@ -88,7 +88,7 @@ public static IEnumerable<ValidationError> Validate(ValidationParameters validat
}
}

internal static PreciseMachineCounter FromCounter(HardwareCounter counter, Func<ProfileSourceInfo, int> intervalSelector)
internal static PreciseMachineCounter FromCounter(string counter, Func<ProfileSourceInfo, int> intervalSelector)
{
var profileSource = TraceEventProfileSources.GetInfo()[EtwTranslations[counter]]; // it can't fail, diagnoser validates that first

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ protected HardwareCountersAttribute()
Config = ManualConfig.CreateEmpty();
}

public HardwareCountersAttribute(params HardwareCounter[] counters)
public HardwareCountersAttribute(params string[] counters)
{
Config = ManualConfig.CreateEmpty().AddHardwareCounters(counters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private class SourceCodePresenter : CharacteristicSetPresenter
private static readonly HashSet<Type> NonExportableTypes = new HashSet<Type>
{
typeof(IToolchain), // there is no need to set toolchain in child process, it was causing parameterless ctor requirement for all IToolchain implementations
typeof(IReadOnlyCollection<HardwareCounter>), // we don't need to export this array to child process
typeof(IReadOnlyCollection<string>), // we don't need to export this array to child process
typeof(IReadOnlyList<Argument>),
typeof(IReadOnlyList<EnvironmentVariable>),
typeof(Runtime) // there is no need to set runtime in child process, it was causing parameterless ctor requirement for all Runtime implementations
Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Configs/ConfigExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static class ConfigExtensions

[PublicAPI] public static ManualConfig WithOrderer(this IConfig config, IOrderer orderer) => config.With(m => m.WithOrderer(orderer));

[PublicAPI] public static ManualConfig AddHardwareCounters(this IConfig config, params HardwareCounter[] counters) => config.With(c => c.AddHardwareCounters(counters));
[PublicAPI] public static ManualConfig AddHardwareCounters(this IConfig config, params string[] counters) => config.With(c => c.AddHardwareCounters(counters));

[PublicAPI] public static ManualConfig AddFilter(this IConfig config, params IFilter[] filters) => config.With(c => c.AddFilter(filters));

Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Configs/DebugConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public abstract class DebugConfig : IConfig
public IEnumerable<ILogger> GetLoggers() => [ConsoleLogger.Default];
public IEnumerable<IDiagnoser> GetDiagnosers() => [];
public IEnumerable<IAnalyser> GetAnalysers() => [];
public IEnumerable<HardwareCounter> GetHardwareCounters() => [];
public IEnumerable<string> GetHardwareCounters() => [];
public IEnumerable<CustomCounter> GetCustomCounters() => [];
public IEnumerable<EventProcessor> GetEventProcessors() => [];
public IEnumerable<IFilter> GetFilters() => [];
Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Configs/DefaultConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public string ArtifactsPath

public IEnumerable<IDiagnoser> GetDiagnosers() => Array.Empty<IDiagnoser>();

public IEnumerable<HardwareCounter> GetHardwareCounters() => Array.Empty<HardwareCounter>();
public IEnumerable<string> GetHardwareCounters() => Array.Empty<string>();

public IEnumerable<CustomCounter> GetCustomCounters() => Array.Empty<CustomCounter>();

Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Configs/IConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public interface IConfig
IEnumerable<IAnalyser> GetAnalysers();
IEnumerable<Job> GetJobs();
IEnumerable<IValidator> GetValidators();
IEnumerable<HardwareCounter> GetHardwareCounters();
IEnumerable<string> GetHardwareCounters();
IEnumerable<CustomCounter> GetCustomCounters();
IEnumerable<IFilter> GetFilters();
IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules();
Expand Down
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet/Configs/ImmutableConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public sealed class ImmutableConfig : IConfig
private readonly ImmutableHashSet<IAnalyser> analysers;
private readonly ImmutableHashSet<IValidator> validators;
private readonly ImmutableHashSet<Job> jobs;
private readonly ImmutableHashSet<HardwareCounter> hardwareCounters;
private readonly ImmutableHashSet<string> hardwareCounters;
private readonly ImmutableHashSet<CustomCounter> customCounters;
private readonly ImmutableHashSet<IFilter> filters;
private readonly ImmutableArray<BenchmarkLogicalGroupRule> rules;
Expand All @@ -39,7 +39,7 @@ public sealed class ImmutableConfig : IConfig
internal ImmutableConfig(
ImmutableArray<IColumnProvider> uniqueColumnProviders,
ImmutableHashSet<ILogger> uniqueLoggers,
ImmutableHashSet<HardwareCounter> uniqueHardwareCounters,
ImmutableHashSet<string> uniqueHardwareCounters,
ImmutableHashSet<CustomCounter> uniqueCustomCounters,
ImmutableHashSet<IDiagnoser> uniqueDiagnosers,
ImmutableArray<IExporter> uniqueExporters,
Expand Down Expand Up @@ -103,7 +103,7 @@ internal ImmutableConfig(
public IEnumerable<IAnalyser> GetAnalysers() => analysers;
public IEnumerable<Job> GetJobs() => jobs;
public IEnumerable<IValidator> GetValidators() => validators;
public IEnumerable<HardwareCounter> GetHardwareCounters() => hardwareCounters;
public IEnumerable<string> GetHardwareCounters() => hardwareCounters;
public IEnumerable<CustomCounter> GetCustomCounters() => customCounters;
public IEnumerable<IFilter> GetFilters() => filters;
public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => rules;
Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public static ImmutableConfig Create(IConfig source)
);
}

private static ImmutableHashSet<IDiagnoser> GetDiagnosers(IEnumerable<IDiagnoser> diagnosers, ImmutableHashSet<HardwareCounter> uniqueHardwareCounters, ImmutableHashSet<CustomCounter> uniqueCustomCounters)
private static ImmutableHashSet<IDiagnoser> GetDiagnosers(IEnumerable<IDiagnoser> diagnosers, ImmutableHashSet<string> uniqueHardwareCounters, ImmutableHashSet<CustomCounter> uniqueCustomCounters)
{
var builder = ImmutableHashSet.CreateBuilder(new TypeComparer<IDiagnoser>());

Expand Down
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet/Configs/ManualConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class ManualConfig : IConfig
private readonly List<IAnalyser> analysers = new List<IAnalyser>();
private readonly List<IValidator> validators = new List<IValidator>();
private readonly List<Job> jobs = new List<Job>();
private readonly HashSet<HardwareCounter> hardwareCounters = new HashSet<HardwareCounter>();
private readonly HashSet<string> hardwareCounters = new HashSet<string>();
private readonly HashSet<CustomCounter> customCounters = new HashSet<CustomCounter>();
private readonly List<IFilter> filters = new List<IFilter>();
private readonly List<BenchmarkLogicalGroupRule> logicalGroupRules = new List<BenchmarkLogicalGroupRule>();
Expand All @@ -45,7 +45,7 @@ public class ManualConfig : IConfig
public IEnumerable<IAnalyser> GetAnalysers() => analysers;
public IEnumerable<IValidator> GetValidators() => validators;
public IEnumerable<Job> GetJobs() => jobs;
public IEnumerable<HardwareCounter> GetHardwareCounters() => hardwareCounters;
public IEnumerable<string> GetHardwareCounters() => hardwareCounters;
public IEnumerable<CustomCounter> GetCustomCounters() => customCounters;
public IEnumerable<IFilter> GetFilters() => filters;
public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => logicalGroupRules;
Expand Down Expand Up @@ -166,7 +166,7 @@ public ManualConfig AddJob(params Job[] newJobs)
return this;
}

public ManualConfig AddHardwareCounters(params HardwareCounter[] newHardwareCounters)
public ManualConfig AddHardwareCounters(params string[] newHardwareCounters)
{
hardwareCounters.AddRange(newHardwareCounters);
return this;
Expand Down
8 changes: 5 additions & 3 deletions src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,13 @@ private static bool Validate(CommandLineOptions options, ILogger logger)
}

foreach (var counterName in options.HardwareCounters)
if (!Enum.TryParse(counterName, ignoreCase: true, out HardwareCounter _))
{
if (!HardwareCounter.All.Contains(counterName, System.StringComparer.OrdinalIgnoreCase))
{
logger.WriteLineError($"The provided hardware counter \"{counterName}\" is invalid. Available options are: {string.Join("+", Enum.GetNames(typeof(HardwareCounter)))}.");
logger.WriteLineError($"The provided hardware counter \"{counterName}\" is invalid. Available options are: {string.Join("+", HardwareCounter.All)}.");
return false;
}
}

if (options.StatisticalTestThreshold.IsNotBlank() && !Threshold.TryParse(options.StatisticalTestThreshold, out _))
{
Expand Down Expand Up @@ -340,7 +342,7 @@ private static IConfig CreateConfig(CommandLineOptions options, IConfig? globalC
config.AddExporter(options.Exporters.SelectMany(exporter => AvailableExporters[exporter]).ToArray());

config.AddHardwareCounters(options.HardwareCounters
.Select(counterName => (HardwareCounter)Enum.Parse(typeof(HardwareCounter), counterName, ignoreCase: true))
.Select(counterName => HardwareCounter.All.FirstOrDefault(c => c.Equals(counterName, System.StringComparison.OrdinalIgnoreCase)) ?? counterName)
.ToArray());

if (options.UseMemoryDiagnoser)
Expand Down
112 changes: 66 additions & 46 deletions src/BenchmarkDotNet/Diagnosers/HardwareCounter.cs
Original file line number Diff line number Diff line change
@@ -1,84 +1,104 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;

namespace BenchmarkDotNet.Diagnosers
{
// initial list is based on counters available for Windows, run `tracelog.exe -profilesources Help` to get the list
[SuppressMessage("ReSharper", "IdentifierTypo")]
public enum HardwareCounter
[PublicAPI]
public static class HardwareCounter
{
NotSet = 0,
Timer,
TotalIssues,
BranchInstructions,
CacheMisses,
BranchMispredictions,
TotalCycles,
UnhaltedCoreCycles,
InstructionRetired,
UnhaltedReferenceCycles,
LlcReference,
LlcMisses,
BranchInstructionRetired,
BranchMispredictsRetired
}
public const string NotSet = nameof(NotSet);
public const string Timer = nameof(Timer);
public const string TotalIssues = nameof(TotalIssues);
public const string BranchInstructions = nameof(BranchInstructions);
public const string CacheMisses = nameof(CacheMisses);
public const string BranchMispredictions = nameof(BranchMispredictions);
public const string TotalCycles = nameof(TotalCycles);
public const string UnhaltedCoreCycles = nameof(UnhaltedCoreCycles);
public const string InstructionRetired = nameof(InstructionRetired);
public const string UnhaltedReferenceCycles = nameof(UnhaltedReferenceCycles);
public const string LlcReference = nameof(LlcReference);
public const string LlcMisses = nameof(LlcMisses);
public const string BranchInstructionRetired = nameof(BranchInstructionRetired);
public const string BranchMispredictsRetired = nameof(BranchMispredictsRetired);

/// <summary>
/// Gets all available hardware counter names.
/// </summary>
public static IReadOnlyList<string> All { get; } = new[]
{
Timer,
TotalIssues,
BranchInstructions,
CacheMisses,
BranchMispredictions,
TotalCycles,
UnhaltedCoreCycles,
InstructionRetired,
UnhaltedReferenceCycles,
LlcReference,
LlcMisses,
BranchInstructionRetired,
BranchMispredictsRetired
};

public static class HardwareCounterExtensions
{
[SuppressMessage("ReSharper", "StringLiteralTypo")]
public static string ToShortName(this HardwareCounter hardwareCounter)
public static string ToShortName(string hardwareCounter)
{
switch (hardwareCounter)
{
case HardwareCounter.Timer:
case Timer:
return "timer";
case HardwareCounter.TotalIssues:
case TotalIssues:
return "issues";
case HardwareCounter.BranchInstructions:
case BranchInstructions:
return "branch";
case HardwareCounter.CacheMisses:
case CacheMisses:
return "miss";
case HardwareCounter.BranchMispredictions:
case BranchMispredictions:
return "mispred";
case HardwareCounter.TotalCycles:
case TotalCycles:
return "cycles";
case HardwareCounter.UnhaltedCoreCycles:
case UnhaltedCoreCycles:
return "unCoreCycles";
case HardwareCounter.InstructionRetired:
case InstructionRetired:
return "retired";
case HardwareCounter.UnhaltedReferenceCycles:
case UnhaltedReferenceCycles:
return "unRefCycles";
case HardwareCounter.LlcReference:
case LlcReference:
return "llcRef";
case HardwareCounter.LlcMisses:
case LlcMisses:
return "llcMiss";
case HardwareCounter.BranchInstructionRetired:
case BranchInstructionRetired:
return "branchInst";
case HardwareCounter.BranchMispredictsRetired:
case BranchMispredictsRetired:
return "branchMisp";
default:
throw new NotSupportedException($"{hardwareCounter} has no short name mapping");
}
}

public static bool TheGreaterTheBetter(this HardwareCounter hardwareCounter)
public static bool TheGreaterTheBetter(string hardwareCounter)
{
// this method could be just a return false as of today but we want to make sure that when we add new counter it's added here on purpose!
switch (hardwareCounter)
{
case HardwareCounter.Timer:
case HardwareCounter.TotalIssues:
case HardwareCounter.BranchInstructions:
case HardwareCounter.CacheMisses:
case HardwareCounter.BranchMispredictions:
case HardwareCounter.TotalCycles:
case HardwareCounter.UnhaltedCoreCycles:
case HardwareCounter.InstructionRetired:
case HardwareCounter.UnhaltedReferenceCycles:
case HardwareCounter.LlcReference:
case HardwareCounter.LlcMisses:
case HardwareCounter.BranchInstructionRetired:
case HardwareCounter.BranchMispredictsRetired:
case Timer:
case TotalIssues:
case BranchInstructions:
case CacheMisses:
case BranchMispredictions:
case TotalCycles:
case UnhaltedCoreCycles:
case InstructionRetired:
case UnhaltedReferenceCycles:
case LlcReference:
case LlcMisses:
case BranchInstructionRetired:
case BranchMispredictsRetired:
return false;
default:
throw new NotSupportedException($"{hardwareCounter} has no TheGreaterTheBetter mapping");
Expand Down
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet/Diagnosers/PmcStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ namespace BenchmarkDotNet.Diagnosers
public class PmcStats
{
public long TotalOperations { get; set; }
public IReadOnlyDictionary<HardwareCounter, PreciseMachineCounter> Counters { get; }
public IReadOnlyDictionary<string, PreciseMachineCounter> Counters { get; }
public IReadOnlyCollection<PreciseMachineCounter> CustomCounters { get; }
private IReadOnlyDictionary<int, PreciseMachineCounter> CountersByProfileSourceId { get; }

public PmcStats() { throw new InvalidOperationException("should never be used"); }

public PmcStats(IReadOnlyCollection<HardwareCounter> hardwareCounters, Func<HardwareCounter, PreciseMachineCounter> factory)
public PmcStats(IReadOnlyCollection<string> hardwareCounters, Func<string, PreciseMachineCounter> factory)
: this(hardwareCounters, Array.Empty<PreciseMachineCounter>(), factory)
{
}

public PmcStats(IReadOnlyCollection<HardwareCounter> hardwareCounters, IReadOnlyCollection<PreciseMachineCounter> customCounters, Func<HardwareCounter, PreciseMachineCounter> factory)
public PmcStats(IReadOnlyCollection<string> hardwareCounters, IReadOnlyCollection<PreciseMachineCounter> customCounters, Func<string, PreciseMachineCounter> factory)
{
var hwCounters = hardwareCounters
.Select(factory)
Expand Down
Loading