Skip to content
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet/Attributes/Jobs/JobConfigbaseAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public class JobConfigBaseAttribute : Attribute, IConfigSource
protected static Job GetJob(Job sourceJob, RuntimeMoniker runtimeMoniker, Jit? jit, Platform? platform)
{
var runtime = runtimeMoniker.GetRuntime();
var baseJob = sourceJob.WithRuntime(runtime).WithId($"{sourceJob.Id}-{runtime.Name}");
var id = baseJob.Id;
var baseJob = sourceJob.WithRuntime(runtime);
var id = $"{sourceJob.Id}-{runtime.Name}";

if (jit.HasValue)
{
Expand All @@ -36,7 +36,7 @@ protected static Job GetJob(Job sourceJob, RuntimeMoniker runtimeMoniker, Jit? j
id += "-" + platform.Value;
}

return baseJob.WithId(id).Freeze();
return baseJob.WithImplicitId(id).Freeze();
}
}
}
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Attributes/Jobs/SimpleJobAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private static Job CreateJob(string id, int launchCount, int warmupCount, int it
}

if (id == null && manualValuesCount == 1 && runtimeMoniker != RuntimeMoniker.HostProcess)
job = job.WithId(runtimeMoniker.GetRuntime().Name);
job = job.WithImplicitId(runtimeMoniker.GetRuntime().Name);

return job.Freeze();
}
Expand Down
30 changes: 19 additions & 11 deletions src/BenchmarkDotNet/Columns/JobCharacteristicColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using BenchmarkDotNet.Characteristics;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
Expand Down Expand Up @@ -39,19 +40,26 @@ private JobCharacteristicColumn(Characteristic characteristic)

public bool IsAvailable(Summary summary)
{
if (summary.IsMultipleRuntimes)
switch (ColumnName)
{
if (nameof(Toolchains.Toolchain).Equals(ColumnName))
{
return false;
}
if (nameof(Job).Equals(ColumnName))
{
return summary.BenchmarksCases.Any(x => x.Job.HasValue(CharacteristicObject.IdCharacteristic));
}
}
case Column.Job:
return summary.BenchmarksCases
.Any(b => b.Job.HasValue(Job.IdCharacteristic) && !b.Job.GetValue(Job.ImplicitIdCharacteristic));
case Column.Toolchain:

var toolchainsByRuntime = summary.BenchmarksCases
.GroupBy(b => b.GetRuntime().Name, b => b.Job.GetValue(InfrastructureMode.ToolchainCharacteristic))
.ToArray();

return true;
if (toolchainsByRuntime.Length <= 1)
return true;

return toolchainsByRuntime.Any(toolchains => toolchains.Where(toolchain => toolchain != null)
.DistinctBy(toolchain => toolchain.Name)
.Count() > 1);
default:
return true;
}
}

public string GetValue(Summary summary, BenchmarkCase benchmarkCase)
Expand Down
30 changes: 19 additions & 11 deletions src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private static bool Validate(CommandLineOptions options, ILogger logger)

foreach (string runtime in options.Runtimes)
{
if (!TryParse(runtime, out RuntimeMoniker runtimeMoniker))
if (!TryParse(runtime, out RuntimeMoniker runtimeMoniker, out _))
{
logger.WriteLineError($"The provided runtime \"{runtime}\" is invalid. Available options are: {string.Join(", ", Enum.GetNames(typeof(RuntimeMoniker)).Select(name => name.ToLower()))}.");
return false;
Expand Down Expand Up @@ -365,7 +365,7 @@ private static IEnumerable<Job> Expand(Job baseJob, CommandLineOptions options,

private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, CommandLineOptions options)
{
if (!TryParse(runtimeId, out RuntimeMoniker runtimeMoniker))
if (!TryParse(runtimeId, out RuntimeMoniker runtimeMoniker, out string platformSpecificPostfix))
{
throw new InvalidOperationException("Impossible, already validated by the Validate method");
}
Expand All @@ -379,10 +379,10 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma
case RuntimeMoniker.Net472:
case RuntimeMoniker.Net48:
case RuntimeMoniker.Net481:
var clrRuntime = runtimeMoniker.GetRuntime();
return baseJob
.WithRuntime(runtimeMoniker.GetRuntime())
.WithToolchain(CsProjClassicNetToolchain.From(runtimeId, options.RestorePath?.FullName));

.WithRuntime(clrRuntime)
.WithToolchain(CsProjClassicNetToolchain.From(clrRuntime.MsBuildMoniker, clrRuntime.Name, options.RestorePath?.FullName));
case RuntimeMoniker.NetCoreApp20:
case RuntimeMoniker.NetCoreApp21:
case RuntimeMoniker.NetCoreApp22:
Expand All @@ -395,9 +395,10 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma
case RuntimeMoniker.Net60:
case RuntimeMoniker.Net70:
case RuntimeMoniker.Net80:
var coreRuntime = runtimeMoniker.GetRuntime();
return baseJob
.WithRuntime(runtimeMoniker.GetRuntime())
.WithToolchain(CsProjCoreToolchain.From(new NetCoreAppSettings(runtimeId, null, runtimeId, options.CliPath?.FullName, options.RestorePath?.FullName)));
.WithRuntime(coreRuntime)
.WithToolchain(CsProjCoreToolchain.From(new NetCoreAppSettings(coreRuntime.MsBuildMoniker + platformSpecificPostfix, null, coreRuntime.Name + platformSpecificPostfix, options.CliPath?.FullName, options.RestorePath?.FullName)));

case RuntimeMoniker.Mono:
return baseJob.WithRuntime(new MonoRuntime("Mono", options.MonoPath?.FullName));
Expand Down Expand Up @@ -613,13 +614,20 @@ private static string GetCoreRunToolchainDisplayName(IReadOnlyList<FileInfo> pat
return coreRunPath.FullName.Substring(lastCommonDirectorySeparatorIndex);
}

private static bool TryParse(string runtime, out RuntimeMoniker runtimeMoniker)
private static bool TryParse(string runtime, out RuntimeMoniker runtimeMoniker, out string platformSpecificPostfix)
{
int index = runtime.IndexOf('-');

return index < 0
? Enum.TryParse<RuntimeMoniker>(runtime.Replace(".", string.Empty), ignoreCase: true, out runtimeMoniker)
: Enum.TryParse<RuntimeMoniker>(runtime.Substring(0, index).Replace(".", string.Empty), ignoreCase: true, out runtimeMoniker);
if (index < 0)
{
platformSpecificPostfix = "";
return Enum.TryParse<RuntimeMoniker>(runtime.Replace(".", string.Empty), ignoreCase: true, out runtimeMoniker);
}
else
{
platformSpecificPostfix = runtime.Substring(index).ToLower();
return Enum.TryParse<RuntimeMoniker>(runtime.Substring(0, index).Replace(".", string.Empty), ignoreCase: true, out runtimeMoniker);
}
}
}
}
25 changes: 14 additions & 11 deletions src/BenchmarkDotNet/Jobs/Job.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,24 @@ public sealed class Job : JobMode<Job>
[PublicAPI] public static readonly Characteristic<AccuracyMode> AccuracyCharacteristic = CreateCharacteristic<AccuracyMode>(nameof(Accuracy));
[PublicAPI] public static readonly Characteristic<MetaMode> MetaCharacteristic = CreateCharacteristic<MetaMode>(nameof(Meta));

public static readonly Job LegacyJitX86 = new Job(nameof(LegacyJitX86), EnvironmentMode.LegacyJitX86).Freeze();
public static readonly Job LegacyJitX64 = new Job(nameof(LegacyJitX64), EnvironmentMode.LegacyJitX64).Freeze();
public static readonly Job RyuJitX64 = new Job(nameof(RyuJitX64), EnvironmentMode.RyuJitX64).Freeze();
public static readonly Job RyuJitX86 = new Job(nameof(RyuJitX86), EnvironmentMode.RyuJitX86).Freeze();
internal static readonly Characteristic<bool> ImplicitIdCharacteristic = CreateHiddenCharacteristic<bool>("ImplicitId");

public static readonly Job LegacyJitX86 = new Job(EnvironmentMode.LegacyJitX86).WithImplicitId(nameof(LegacyJitX86)).Freeze();
public static readonly Job LegacyJitX64 = new Job(EnvironmentMode.LegacyJitX64).WithImplicitId(nameof(LegacyJitX64)).Freeze();
public static readonly Job RyuJitX64 = new Job(EnvironmentMode.RyuJitX64).WithImplicitId(nameof(RyuJitX64)).Freeze();
public static readonly Job RyuJitX86 = new Job(EnvironmentMode.RyuJitX86).WithImplicitId(nameof(RyuJitX86)).Freeze();

// Run
public static readonly Job Dry = new Job(nameof(Dry), RunMode.Dry).Freeze();
public static readonly Job Dry = new Job(RunMode.Dry).WithImplicitId(nameof(Dry)).Freeze();

public static readonly Job ShortRun = new Job(nameof(ShortRun), RunMode.Short).Freeze();
public static readonly Job MediumRun = new Job(nameof(MediumRun), RunMode.Medium).Freeze();
public static readonly Job LongRun = new Job(nameof(LongRun), RunMode.Long).Freeze();
public static readonly Job VeryLongRun = new Job(nameof(VeryLongRun), RunMode.VeryLong).Freeze();
public static readonly Job ShortRun = new Job(RunMode.Short).WithImplicitId(nameof(ShortRun)).Freeze();
public static readonly Job MediumRun = new Job(RunMode.Medium).WithImplicitId(nameof(MediumRun)).Freeze();
public static readonly Job LongRun = new Job(RunMode.Long).WithImplicitId(nameof(LongRun)).Freeze();
public static readonly Job VeryLongRun = new Job(RunMode.VeryLong).WithImplicitId(nameof(VeryLongRun)).Freeze();

// Infrastructure
public static readonly Job InProcess = new Job(nameof(InProcess), InfrastructureMode.InProcess);
public static readonly Job InProcessDontLogOutput = new Job(nameof(InProcessDontLogOutput), InfrastructureMode.InProcessDontLogOutput);
public static readonly Job InProcess = new Job(InfrastructureMode.InProcess).WithImplicitId(nameof(InProcess));
public static readonly Job InProcessDontLogOutput = new Job(InfrastructureMode.InProcessDontLogOutput).WithImplicitId(nameof(InProcessDontLogOutput));

public Job() : this((string)null) { }

Expand All @@ -40,6 +42,7 @@ public Job(string id) : base(id)
InfrastructureCharacteristic[this] = new InfrastructureMode();
AccuracyCharacteristic[this] = new AccuracyMode();
MetaCharacteristic[this] = new MetaMode();
ImplicitIdCharacteristic[this] = false;
}

public Job(CharacteristicObject other) : this((string)null, other)
Expand Down
5 changes: 4 additions & 1 deletion src/BenchmarkDotNet/Jobs/JobExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ public static class JobExtensions
public static Job With(this Job job, Platform platform) => job.WithPlatform(platform);
public static Job WithPlatform(this Job job, Platform platform) => job.WithCore(j => j.Environment.Platform = platform);

public static Job WithId(this Job job, string id) => new Job(id, job);
public static Job WithId(this Job job, string id) => new Job(id, job).WithImplicitId(false);

internal static Job WithImplicitId(this Job job, string id) => new Job(id, job).WithImplicitId(true);
private static Job WithImplicitId(this Job job, bool isImplicit) => job.WithCore(j => Job.ImplicitIdCharacteristic[j] = isImplicit);

// Env
[EditorBrowsable(EditorBrowsableState.Never)]
Expand Down
4 changes: 0 additions & 4 deletions src/BenchmarkDotNet/Reports/Summary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ public class Summary

private ImmutableDictionary<BenchmarkCase, BenchmarkReport> ReportMap { get; }
private BaseliningStrategy BaseliningStrategy { get; }
private bool? isMultipleRuntimes;

public Summary(
string title,
Expand Down Expand Up @@ -80,9 +79,6 @@ public Summary(

public int GetNumberOfExecutedBenchmarks() => Reports.Count(report => report.ExecuteResults.Any(result => result.FoundExecutable));

public bool IsMultipleRuntimes
=> isMultipleRuntimes ??= BenchmarksCases.Length > 1 ? BenchmarksCases.Select(benchmark => benchmark.GetRuntime()).Distinct().Count() > 1 : false;

internal static Summary ValidationFailed(string title, string resultsDirectoryPath, string logFilePath, ImmutableArray<ValidationError>? validationErrors = null)
=> new Summary(title, ImmutableArray<BenchmarkReport>.Empty, HostEnvironmentInfo.GetCurrent(), resultsDirectoryPath, logFilePath, TimeSpan.Zero, DefaultCultureInfo.Instance, validationErrors ?? ImmutableArray<ValidationError>.Empty, ImmutableArray<IColumnHidingRule>.Empty);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ private CsProjClassicNetToolchain(string targetFrameworkMoniker, string name, st
{
}

public static IToolchain From(string targetFrameworkMoniker, string packagesPath = null)
=> new CsProjClassicNetToolchain(targetFrameworkMoniker, targetFrameworkMoniker, packagesPath);
public static IToolchain From(string targetFrameworkMoniker, string name, string packagesPath = null)
=> new CsProjClassicNetToolchain(targetFrameworkMoniker, name, packagesPath);

public override IEnumerable<ValidationError> Validate(BenchmarkCase benchmarkCase, IResolver resolver)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public InProcessEmitToolchain(bool logOutput) :
/// <param name="logOutput"><c>true</c> if the output should be logged.</param>
public InProcessEmitToolchain(TimeSpan timeout, bool logOutput) :
base(
nameof(InProcessEmitToolchain),
"InProcessEmit",
new InProcessEmitGenerator(),
new InProcessEmitBuilder(),
new InProcessEmitExecutor(timeout, logOutput))
Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal static IToolchain GetToolchain(this Runtime runtime, Descriptor descrip
if (RuntimeInformation.IsNetCore || preferMsBuildToolchains)
return clrRuntime.RuntimeMoniker != RuntimeMoniker.NotRecognized
? GetToolchain(clrRuntime.RuntimeMoniker)
: CsProjClassicNetToolchain.From(clrRuntime.MsBuildMoniker);
: CsProjClassicNetToolchain.From(clrRuntime.MsBuildMoniker, clrRuntime.Name);

return RoslynToolchain.Instance;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

BenchmarkDotNet=v0.10.x-mock, OS=Microsoft Windows NT 10.0.x.mock, VM=Hyper-V
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
Dry : extra output line

Job=Dry IterationCount=1 LaunchCount=1
RunStrategy=ColdStart UnrollFactor=1 WarmupCount=1

Method | Mean | Error |
------- |---------:|------:|
Method | 1.000 ns | NA |
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

BenchmarkDotNet=v0.10.x-mock, OS=Microsoft Windows NT 10.0.x.mock, VM=Hyper-V
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
Dry : extra output line

IterationCount=1 LaunchCount=1 RunStrategy=ColdStart
UnrollFactor=1 WarmupCount=1

Method | Mean | Error |
------- |---------:|------:|
Method | 1.000 ns | NA |
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

BenchmarkDotNet=v0.10.x-mock, OS=Microsoft Windows NT 10.0.x.mock, VM=Hyper-V
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
Job-rndId0 : extra output line
Job-rndId1 : extra output line
Job-rndId2 : extra output line
Dry : extra output line
Dry-.NET 6.0 : extra output line
Dry-.NET Framework 4.8.1 : extra output line
Dry-NativeAOT 6.0 : extra output line


Method | Runtime | IterationCount | LaunchCount | RunStrategy | UnrollFactor | WarmupCount | Mean | Error | Ratio |
------- |--------------------- |--------------- |------------ |------------ |------------- |------------ |---------:|------:|------:|
Method | .NET 7.0 | Default | Default | Default | 16 | Default | 1.000 ns | NA | 1.00 |
Method | .NET Framework 4.8.1 | Default | Default | Default | 16 | Default | 1.000 ns | NA | 1.00 |
Method | NativeAOT 7.0 | Default | Default | Default | 16 | Default | 1.000 ns | NA | 1.00 |
Method | .NET 7.0 | 1 | 1 | ColdStart | 1 | 1 | 1.000 ns | NA | 1.00 |
Method | .NET Framework 4.8.1 | 1 | 1 | ColdStart | 1 | 1 | 1.000 ns | NA | 1.00 |
Method | NativeAOT 7.0 | 1 | 1 | ColdStart | 1 | 1 | 1.000 ns | NA | 1.00 |
Method | .NET 6.0 | 1 | 1 | ColdStart | 1 | 1 | 1.000 ns | NA | 1.00 |
Method | .NET Framework 4.8.1 | 1 | 1 | ColdStart | 1 | 1 | 1.000 ns | NA | 1.00 |
Method | NativeAOT 6.0 | 1 | 1 | ColdStart | 1 | 1 | 1.000 ns | NA | 1.00 |
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

BenchmarkDotNet=v0.10.x-mock, OS=Microsoft Windows NT 10.0.x.mock, VM=Hyper-V
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
Job-rndId0 : extra output line

Runtime=.NET 7.0

Method | Mean | Error |
------- |---------:|------:|
Method | 1.000 ns | NA |
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

BenchmarkDotNet=v0.10.x-mock, OS=Microsoft Windows NT 10.0.x.mock, VM=Hyper-V
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
Job-rndId0 : extra output line
Job-rndId1 : extra output line


Method | Runtime | Mean | Error |
------- |--------- |---------:|------:|
Method | .NET 6.0 | 1.000 ns | NA |
Method | .NET 7.0 | 1.000 ns | NA |
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

BenchmarkDotNet=v0.10.x-mock, OS=Microsoft Windows NT 10.0.x.mock, VM=Hyper-V
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
Job-rndId0 : extra output line
Job-rndId1 : extra output line


Method | Runtime | Mean | Error |
------- |--------- |---------:|------:|
Method | .NET 6.0 | 1.000 ns | NA |
Method | .NET 7.0 | 1.000 ns | NA |
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

BenchmarkDotNet=v0.10.x-mock, OS=Microsoft Windows NT 10.0.x.mock, VM=Hyper-V
MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores
Frequency=2531248 Hz, Resolution=395.0620 ns, Timer=TSC
[Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION
InProcess : extra output line

Toolchain=InProcessEmit

Method | Mean | Error |
------- |---------:|------:|
Method | 1.000 ns | NA |
Loading