From c7b7abf0ded44458ad022eacad2fd9990546cd14 Mon Sep 17 00:00:00 2001 From: Martin Othamar Date: Mon, 1 Apr 2024 17:18:21 +0200 Subject: [PATCH 01/57] DotMemoryDiagnoser implementation (#2549) * DotMemoryDiagnoser implementation * Typo fix, less alloc in test * attempt to fix reinit logic for tool * Simplify implementation --- BenchmarkDotNet.sln | 7 + docs/articles/guides/nuget.md | 1 + .../samples/IntroDotMemoryDiagnoser.md | 22 +++ docs/articles/samples/toc.yml | 2 + .../BenchmarkDotNet.Samples.csproj | 1 + .../IntroDotMemoryDiagnoser.cs | 52 ++++++ ...nchmarkDotNet.Diagnostics.dotMemory.csproj | 20 +++ .../DotMemoryDiagnoser.cs | 156 ++++++++++++++++++ .../DotMemoryDiagnoserAttribute.cs | 21 +++ .../DotMemoryTool.cs | 140 ++++++++++++++++ .../Progress.cs | 38 +++++ .../Properties/AssemblyInfo.cs | 11 ++ .../ExternalDotTraceTool.cs | 2 +- .../Properties/AssemblyInfo.cs | 2 + .../BenchmarkDotNet.IntegrationTests.csproj | 1 + .../DotMemoryTests.cs | 74 +++++++++ .../BenchmarkDotNet.Tests.csproj | 1 + .../dotMemory/DotMemoryTests.cs | 17 ++ 18 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 docs/articles/samples/IntroDotMemoryDiagnoser.md create mode 100644 samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs create mode 100644 src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj create mode 100644 src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs create mode 100644 src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs create mode 100644 src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs create mode 100644 src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs create mode 100644 src/BenchmarkDotNet.Diagnostics.dotMemory/Properties/AssemblyInfo.cs create mode 100644 tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs create mode 100644 tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs diff --git a/BenchmarkDotNet.sln b/BenchmarkDotNet.sln index eed29c80af..d51cb4e029 100644 --- a/BenchmarkDotNet.sln +++ b/BenchmarkDotNet.sln @@ -53,6 +53,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet.Integration EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.TestAdapter", "src\BenchmarkDotNet.TestAdapter\BenchmarkDotNet.TestAdapter.csproj", "{4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Diagnostics.dotMemory", "src\BenchmarkDotNet.Diagnostics.dotMemory\BenchmarkDotNet.Diagnostics.dotMemory.csproj", "{2E2283A3-6DA6-4482-8518-99D6D9F689AB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -143,6 +145,10 @@ Global {4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC}.Release|Any CPU.Build.0 = Release|Any CPU + {2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -169,6 +175,7 @@ Global {C5BDA61F-3A56-4B59-901D-0A17E78F4076} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} {AACA2C63-A85B-47AB-99FC-72C3FF408B14} = {14195214-591A-45B7-851A-19D3BA2413F9} {4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} + {2E2283A3-6DA6-4482-8518-99D6D9F689AB} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4D9AF12B-1F7F-45A7-9E8C-E4E46ADCBD1F} diff --git a/docs/articles/guides/nuget.md b/docs/articles/guides/nuget.md index 0e2de62a79..d1359c03d9 100644 --- a/docs/articles/guides/nuget.md +++ b/docs/articles/guides/nuget.md @@ -13,6 +13,7 @@ We have the following set of NuGet packages (you can install it directly from `n * `BenchmarkDotNet.Annotations`: Basic BenchmarkDotNet annotations for your benchmarks. * `BenchmarkDotNet.Diagnostics.Windows`: an additional optional package that provides a set of Windows diagnosers. * `BenchmarkDotNet.Diagnostics.dotTrace`: an additional optional package that provides DotTraceDiagnoser. +* `BenchmarkDotNet.Diagnostics.dotMemory`: an additional optional package that provides DotMemoryDiagnoser. * `BenchmarkDotNet.Templates`: Templates for BenchmarkDotNet. You might find other NuGet packages that start with `BenchmarkDotNet` name, but they are internal BDN packages that should not be installed manually. All that matters are the three packages mentioned above. diff --git a/docs/articles/samples/IntroDotMemoryDiagnoser.md b/docs/articles/samples/IntroDotMemoryDiagnoser.md new file mode 100644 index 0000000000..e16e96a83c --- /dev/null +++ b/docs/articles/samples/IntroDotMemoryDiagnoser.md @@ -0,0 +1,22 @@ +--- +uid: BenchmarkDotNet.Samples.IntroDotMemoryDiagnoser +--- + +## Sample: IntroDotMemoryDiagnoser + +If you want to get a memory allocation profile of your benchmarks, just add the `[DotMemoryDiagnoser]` attribute, as shown below. +As a result, BenchmarkDotNet performs bonus benchmark runs using attached + [dotMemory Command-Line Profiler](https://www.jetbrains.com/help/dotmemory/Working_with_dotMemory_Command-Line_Profiler.html). +The obtained dotMemory workspaces are saved to the `artifacts` folder. +These dotMemory workspaces can be opened using the [standalone dotMemory](https://www.jetbrains.com/dotmemory/), + or [dotMemory in Rider](https://www.jetbrains.com/help/rider/Memory_profiling_of_.NET_code.html). + +### Source code + +[!code-csharp[IntroDotMemoryDiagnoser.cs](../../../samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs)] + +### Links + +* The permanent link to this sample: @BenchmarkDotNet.Samples.IntroDotMemoryDiagnoser + +--- \ No newline at end of file diff --git a/docs/articles/samples/toc.yml b/docs/articles/samples/toc.yml index 8098a637c3..7e4c7671e1 100644 --- a/docs/articles/samples/toc.yml +++ b/docs/articles/samples/toc.yml @@ -38,6 +38,8 @@ href: IntroDisassemblyRyuJit.md - name: IntroDotTraceDiagnoser href: IntroDotTraceDiagnoser.md +- name: IntroDotMemoryDiagnoser + href: IntroDotMemoryDiagnoser.md - name: IntroEnvVars href: IntroEnvVars.md - name: IntroEventPipeProfiler diff --git a/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj b/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj index 1af13f61c3..36c3f60b32 100644 --- a/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj +++ b/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj @@ -26,6 +26,7 @@ + diff --git a/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs b/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs new file mode 100644 index 0000000000..846e2178f4 --- /dev/null +++ b/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs @@ -0,0 +1,52 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnostics.dotMemory; +using System.Collections.Generic; + +namespace BenchmarkDotNet.Samples +{ + // Enables dotMemory profiling for all jobs + [DotMemoryDiagnoser] + // Adds the default "external-process" job + // Profiling is performed using dotMemory Command-Line Profiler + // See: https://www.jetbrains.com/help/dotmemory/Working_with_dotMemory_Command-Line_Profiler.html + [SimpleJob] + // Adds an "in-process" job + // Profiling is performed using dotMemory SelfApi + // NuGet reference: https://www.nuget.org/packages/JetBrains.Profiler.SelfApi + [InProcess] + public class IntroDotMemoryDiagnoser + { + [Params(1024)] + public int Size; + + private byte[] dataArray; + private IEnumerable dataEnumerable; + + [GlobalSetup] + public void Setup() + { + dataArray = new byte[Size]; + dataEnumerable = dataArray; + } + + [Benchmark] + public int IterateArray() + { + var count = 0; + foreach (var _ in dataArray) + count++; + + return count; + } + + [Benchmark] + public int IterateEnumerable() + { + var count = 0; + foreach (var _ in dataEnumerable) + count++; + + return count; + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj new file mode 100644 index 0000000000..9ee6b4be18 --- /dev/null +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj @@ -0,0 +1,20 @@ + + + + net6.0;net462;netcoreapp3.1 + $(NoWarn);1591 + BenchmarkDotNet.Diagnostics.dotMemory + BenchmarkDotNet.Diagnostics.dotMemory + BenchmarkDotNet.Diagnostics.dotMemory + enable + + + + + + + + + + + diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs new file mode 100644 index 0000000000..cbba52fb56 --- /dev/null +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using BenchmarkDotNet.Analysers; +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; +using RunMode = BenchmarkDotNet.Diagnosers.RunMode; + +namespace BenchmarkDotNet.Diagnostics.dotMemory +{ + public class DotMemoryDiagnoser : IProfiler + { + private readonly Uri? nugetUrl; + private readonly string? toolsDownloadFolder; + + private DotMemoryTool? tool; + + public DotMemoryDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null) + { + this.nugetUrl = nugetUrl; + this.toolsDownloadFolder = toolsDownloadFolder; + } + + public IEnumerable Ids => new[] { "DotMemory" }; + public string ShortName => "dotMemory"; + + public RunMode GetRunMode(BenchmarkCase benchmarkCase) + { + return IsSupported(benchmarkCase.Job.Environment.GetRuntime().RuntimeMoniker) ? RunMode.ExtraRun : RunMode.None; + } + + private readonly List snapshotFilePaths = new (); + + public void Handle(HostSignal signal, DiagnoserActionParameters parameters) + { + var logger = parameters.Config.GetCompositeLogger(); + var job = parameters.BenchmarkCase.Job; + + var runtimeMoniker = job.Environment.GetRuntime().RuntimeMoniker; + if (!IsSupported(runtimeMoniker)) + { + logger.WriteLineError($"Runtime '{runtimeMoniker}' is not supported by dotMemory"); + return; + } + + switch (signal) + { + case HostSignal.BeforeAnythingElse: + if (tool is not null) + throw new InvalidOperationException("DotMemory tool is already initialized"); + tool = new DotMemoryTool(logger, nugetUrl, downloadTo: toolsDownloadFolder); + tool.Init(); + break; + case HostSignal.BeforeActualRun: + if (tool is null) + throw new InvalidOperationException("DotMemory tool is not initialized"); + snapshotFilePaths.Add(tool.Start(parameters)); + break; + case HostSignal.AfterActualRun: + if (tool is null) + throw new InvalidOperationException("DotMemory tool is not initialized"); + tool.Stop(); + tool = null; + break; + } + } + + public IEnumerable Exporters => Enumerable.Empty(); + public IEnumerable Analysers => Enumerable.Empty(); + + public IEnumerable Validate(ValidationParameters validationParameters) + { + var runtimeMonikers = validationParameters.Benchmarks.Select(b => b.Job.Environment.GetRuntime().RuntimeMoniker).Distinct(); + foreach (var runtimeMoniker in runtimeMonikers) + { + if (!IsSupported(runtimeMoniker)) + yield return new ValidationError(true, $"Runtime '{runtimeMoniker}' is not supported by dotMemory"); + } + } + + internal static bool IsSupported(RuntimeMoniker runtimeMoniker) + { + switch (runtimeMoniker) + { + case RuntimeMoniker.HostProcess: + case RuntimeMoniker.Net461: + case RuntimeMoniker.Net462: + case RuntimeMoniker.Net47: + case RuntimeMoniker.Net471: + case RuntimeMoniker.Net472: + case RuntimeMoniker.Net48: + case RuntimeMoniker.Net481: + case RuntimeMoniker.Net50: + case RuntimeMoniker.Net60: + case RuntimeMoniker.Net70: + case RuntimeMoniker.Net80: + case RuntimeMoniker.Net90: + return true; + case RuntimeMoniker.NotRecognized: + case RuntimeMoniker.Mono: + case RuntimeMoniker.NativeAot60: + case RuntimeMoniker.NativeAot70: + case RuntimeMoniker.NativeAot80: + case RuntimeMoniker.NativeAot90: + case RuntimeMoniker.Wasm: + case RuntimeMoniker.WasmNet50: + case RuntimeMoniker.WasmNet60: + case RuntimeMoniker.WasmNet70: + case RuntimeMoniker.WasmNet80: + case RuntimeMoniker.WasmNet90: + case RuntimeMoniker.MonoAOTLLVM: + case RuntimeMoniker.MonoAOTLLVMNet60: + case RuntimeMoniker.MonoAOTLLVMNet70: + case RuntimeMoniker.MonoAOTLLVMNet80: + case RuntimeMoniker.MonoAOTLLVMNet90: + case RuntimeMoniker.Mono60: + case RuntimeMoniker.Mono70: + case RuntimeMoniker.Mono80: + case RuntimeMoniker.Mono90: +#pragma warning disable CS0618 // Type or member is obsolete + case RuntimeMoniker.NetCoreApp50: +#pragma warning restore CS0618 // Type or member is obsolete + return false; + case RuntimeMoniker.NetCoreApp20: + case RuntimeMoniker.NetCoreApp21: + case RuntimeMoniker.NetCoreApp22: + return RuntimeInformation.IsWindows(); + case RuntimeMoniker.NetCoreApp30: + case RuntimeMoniker.NetCoreApp31: + return RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux(); + default: + throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported"); + } + } + + public IEnumerable ProcessResults(DiagnoserResults results) => ImmutableArray.Empty; + + public void DisplayResults(ILogger logger) + { + if (snapshotFilePaths.Any()) + { + logger.WriteLineInfo("The following dotMemory snapshots were generated:"); + foreach (string snapshotFilePath in snapshotFilePaths) + logger.WriteLineInfo($"* {snapshotFilePath}"); + } + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs new file mode 100644 index 0000000000..a474312978 --- /dev/null +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs @@ -0,0 +1,21 @@ +using System; +using BenchmarkDotNet.Configs; + +namespace BenchmarkDotNet.Diagnostics.dotMemory +{ + [AttributeUsage(AttributeTargets.Class)] + public class DotMemoryDiagnoserAttribute : Attribute, IConfigSource + { + public IConfig Config { get; } + + public DotMemoryDiagnoserAttribute() + { + Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser()); + } + + public DotMemoryDiagnoserAttribute(Uri? nugetUrl = null, string? toolsDownloadFolder = null) + { + Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser(nugetUrl, toolsDownloadFolder)); + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs new file mode 100644 index 0000000000..1e640d6708 --- /dev/null +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs @@ -0,0 +1,140 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Helpers; +using BenchmarkDotNet.Loggers; +using JetBrains.Profiler.SelfApi; + +namespace BenchmarkDotNet.Diagnostics.dotMemory +{ + internal sealed class DotMemoryTool + { + private readonly ILogger logger; + private readonly Uri? nugetUrl; + private readonly NuGetApi nugetApi; + private readonly string? downloadTo; + + public DotMemoryTool(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null) + { + this.logger = logger; + this.nugetUrl = nugetUrl; + this.nugetApi = nugetApi; + this.downloadTo = downloadTo; + } + + public void Init() + { + try + { + logger.WriteLineInfo("Ensuring that dotMemory prerequisite is installed..."); + var progress = new Progress(logger, "Installing DotMemory"); + DotMemory.EnsurePrerequisiteAsync(progress, nugetUrl, nugetApi, downloadTo).Wait(); + logger.WriteLineInfo("dotMemory prerequisite is installed"); + logger.WriteLineInfo($"dotMemory runner path: {GetRunnerPath()}"); + } + catch (Exception e) + { + logger.WriteLineError(e.ToString()); + } + } + + public string Start(DiagnoserActionParameters parameters) + { + string snapshotFile = ArtifactFileNameHelper.GetFilePath(parameters, "snapshots", DateTime.Now, "dmw", ".0000".Length); + string? snapshotDirectory = Path.GetDirectoryName(snapshotFile); + logger.WriteLineInfo($"Target snapshot file: {snapshotFile}"); + if (!Directory.Exists(snapshotDirectory) && snapshotDirectory != null) + { + try + { + Directory.CreateDirectory(snapshotDirectory); + } + catch (Exception e) + { + logger.WriteLineError($"Failed to create directory: {snapshotDirectory}"); + logger.WriteLineError(e.ToString()); + } + } + + try + { + logger.WriteLineInfo("Attaching dotMemory to the process..."); + Attach(parameters, snapshotFile); + logger.WriteLineInfo("dotMemory is successfully attached"); + } + catch (Exception e) + { + logger.WriteLineError(e.ToString()); + return snapshotFile; + } + + return snapshotFile; + } + + public void Stop() + { + try + { + logger.WriteLineInfo("Taking dotMemory snapshot..."); + Snapshot(); + logger.WriteLineInfo("dotMemory snapshot is successfully taken"); + } + catch (Exception e) + { + logger.WriteLineError(e.ToString()); + } + + try + { + logger.WriteLineInfo("Detaching dotMemory from the process..."); + Detach(); + logger.WriteLineInfo("dotMemory is successfully detached"); + } + catch (Exception e) + { + logger.WriteLineError(e.ToString()); + } + } + + private void Attach(DiagnoserActionParameters parameters, string snapshotFile) + { + var config = new DotMemory.Config(); + + var pid = parameters.Process.Id; + var currentPid = Process.GetCurrentProcess().Id; + if (pid != currentPid) + config = config.ProfileExternalProcess(pid); + + config = config.SaveToFile(snapshotFile); + DotMemory.Attach(config); + } + + private void Snapshot() => DotMemory.GetSnapshot(); + + private void Detach() => DotMemory.Detach(); + + private string GetRunnerPath() + { + var consoleRunnerPackageField = typeof(DotMemory).GetField("ConsoleRunnerPackage", BindingFlags.NonPublic | BindingFlags.Static); + if (consoleRunnerPackageField == null) + throw new InvalidOperationException("Field 'ConsoleRunnerPackage' not found."); + + object? consoleRunnerPackage = consoleRunnerPackageField.GetValue(null); + if (consoleRunnerPackage == null) + throw new InvalidOperationException("Unable to get value of 'ConsoleRunnerPackage'."); + + var consoleRunnerPackageType = consoleRunnerPackage.GetType(); + var getRunnerPathMethod = consoleRunnerPackageType.GetMethod("GetRunnerPath"); + if (getRunnerPathMethod == null) + throw new InvalidOperationException("Method 'GetRunnerPath' not found."); + + string? runnerPath = getRunnerPathMethod.Invoke(consoleRunnerPackage, null) as string; + if (runnerPath == null) + throw new InvalidOperationException("Unable to invoke 'GetRunnerPath'."); + + return runnerPath; + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs new file mode 100644 index 0000000000..738997bb6d --- /dev/null +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs @@ -0,0 +1,38 @@ +using System; +using System.Diagnostics; +using BenchmarkDotNet.Loggers; + +namespace BenchmarkDotNet.Diagnostics.dotMemory +{ + public class Progress : IProgress + { + private static readonly TimeSpan ReportInterval = TimeSpan.FromSeconds(0.1); + + private readonly ILogger logger; + private readonly string title; + + public Progress(ILogger logger, string title) + { + this.logger = logger; + this.title = title; + } + + private int lastProgress; + private Stopwatch? stopwatch; + + public void Report(double value) + { + int progress = (int)Math.Floor(value); + bool needToReport = stopwatch == null || + (stopwatch != null && stopwatch?.Elapsed > ReportInterval) || + progress == 100; + + if (lastProgress != progress && needToReport) + { + logger.WriteLineInfo($"{title}: {progress}%"); + lastProgress = progress; + stopwatch = Stopwatch.StartNew(); + } + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/Properties/AssemblyInfo.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..270fdc2c9c --- /dev/null +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Properties; + +[assembly: CLSCompliant(true)] + +#if RELEASE +[assembly: InternalsVisibleTo("BenchmarkDotNet.Tests,PublicKey=" + BenchmarkDotNetInfo.PublicKey)] +#else +[assembly: InternalsVisibleTo("BenchmarkDotNet.Tests")] +#endif \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs index dfc9903b82..c7f1cf18c8 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs @@ -70,7 +70,7 @@ protected override void Attach(DiagnoserActionParameters parameters, string snap } if (!attachWaitingTask.Task.Wait(AttachTimeout)) - throw new Exception($"Failed to attach dotTrace to the target process (timeout: {AttachTimeout.TotalSeconds} sec"); + throw new Exception($"Failed to attach dotTrace to the target process (timeout: {AttachTimeout.TotalSeconds} sec)"); if (!attachWaitingTask.Task.Result) throw new Exception($"Failed to attach dotTrace to the target process (ExitCode={process.ExitCode})"); } diff --git a/src/BenchmarkDotNet/Properties/AssemblyInfo.cs b/src/BenchmarkDotNet/Properties/AssemblyInfo.cs index cec4ef220c..ee7077a55e 100644 --- a/src/BenchmarkDotNet/Properties/AssemblyInfo.cs +++ b/src/BenchmarkDotNet/Properties/AssemblyInfo.cs @@ -14,6 +14,7 @@ [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests,PublicKey=" + BenchmarkDotNetInfo.PublicKey)] [assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.Windows,PublicKey=" + BenchmarkDotNetInfo.PublicKey)] [assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.dotTrace,PublicKey=" + BenchmarkDotNetInfo.PublicKey)] +[assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.dotMemory,PublicKey=" + BenchmarkDotNetInfo.PublicKey)] [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests.ManualRunning,PublicKey=" + BenchmarkDotNetInfo.PublicKey)] [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks,PublicKey=" + BenchmarkDotNetInfo.PublicKey)] [assembly: InternalsVisibleTo("BenchmarkDotNet.TestAdapter,PublicKey=" + BenchmarkDotNetInfo.PublicKey)] @@ -22,6 +23,7 @@ [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests")] [assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.Windows")] [assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.dotTrace")] +[assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.dotMemory")] [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests.ManualRunning")] [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks")] [assembly: InternalsVisibleTo("BenchmarkDotNet.TestAdapter")] diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj index 8949cce2c0..a928af1424 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj @@ -29,6 +29,7 @@ + diff --git a/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs b/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs new file mode 100644 index 0000000000..1e8dc79d00 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnostics.dotMemory; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Portability; +using BenchmarkDotNet.Toolchains.InProcess.Emit; +using Xunit; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.IntegrationTests +{ + public class DotMemoryTests : BenchmarkTestExecutor + { + public DotMemoryTests(ITestOutputHelper output) : base(output) { } + + [Fact] + public void DotMemorySmokeTest() + { + if (!RuntimeInformation.IsWindows() && RuntimeInformation.IsMono) + { + Output.WriteLine("Skip Mono on non-Windows"); + return; + } + + var config = new ManualConfig().AddJob( + Job.Dry.WithId("ExternalProcess"), + Job.Dry.WithToolchain(InProcessEmitToolchain.Instance).WithId("InProcess") + ); + string snapshotDirectory = Path.Combine(Directory.GetCurrentDirectory(), "BenchmarkDotNet.Artifacts", "snapshots"); + if (Directory.Exists(snapshotDirectory)) + Directory.Delete(snapshotDirectory, true); + + CanExecute(config); + + Output.WriteLine("---------------------------------------------"); + Output.WriteLine("SnapshotDirectory:" + snapshotDirectory); + var snapshots = Directory.EnumerateFiles(snapshotDirectory) + .Where(filePath => Path.GetExtension(filePath).Equals(".dmw", StringComparison.OrdinalIgnoreCase)) + .Select(Path.GetFileName) + .OrderBy(fileName => fileName) + .ToList(); + Output.WriteLine("Snapshots:"); + foreach (string snapshot in snapshots) + Output.WriteLine("* " + snapshot); + Assert.Equal(4, snapshots.Count); + } + + [DotMemoryDiagnoser] + public class Benchmarks + { + [Benchmark] + public int Foo0() + { + var list = new List(); + for (int i = 0; i < 1000; i++) + list.Add(new object()); + return list.Count; + } + + [Benchmark] + public int Foo1() + { + var list = new List(); + for (int i = 0; i < 1000; i++) + list.Add(new object()); + return list.Count; + } + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj b/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj index c85c101890..fcdc416bbf 100755 --- a/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj +++ b/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj @@ -36,6 +36,7 @@ + diff --git a/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs b/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs new file mode 100644 index 0000000000..7142486280 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs @@ -0,0 +1,17 @@ +using System; +using BenchmarkDotNet.Diagnostics.dotMemory; +using BenchmarkDotNet.Jobs; +using Xunit; + +namespace BenchmarkDotNet.Tests.dotMemory +{ + public class DotMemoryTests + { + [Fact] + public void AllRuntimeMonikerAreKnown() + { + foreach (RuntimeMoniker moniker in Enum.GetValues(typeof(RuntimeMoniker))) + DotMemoryDiagnoser.IsSupported(moniker); // Just check that it doesn't throw exceptions + } + } +} \ No newline at end of file From 55ce92d7fa2aa26e463350e65b7cf6c0c2c78d97 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Mon, 1 Apr 2024 17:20:14 +0200 Subject: [PATCH 02/57] Bump JetBrains.Profiler.SelfApi: 2.5.0->2.5.2 --- .../BenchmarkDotNet.Diagnostics.dotMemory.csproj | 2 +- .../BenchmarkDotNet.Diagnostics.dotTrace.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj index 9ee6b4be18..23261abb24 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj b/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj index e7852f1575..aa8c7af1e4 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj @@ -14,7 +14,7 @@ - + From a24d689361c44356b12892424d25bdf27d13d8da Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Mon, 8 Apr 2024 11:18:47 +0200 Subject: [PATCH 03/57] Fix DotTrace/DotMemory attribute ctors, fix #2554 --- .../DotMemoryDiagnoser.cs | 11 +---------- .../DotMemoryDiagnoserAttribute.cs | 5 +++-- .../DotTraceDiagnoser.cs | 11 +---------- .../DotTraceDiagnoserAttribute.cs | 5 +++-- 4 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs index cbba52fb56..46789b518c 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs @@ -16,19 +16,10 @@ namespace BenchmarkDotNet.Diagnostics.dotMemory { - public class DotMemoryDiagnoser : IProfiler + public class DotMemoryDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null) : IProfiler { - private readonly Uri? nugetUrl; - private readonly string? toolsDownloadFolder; - private DotMemoryTool? tool; - public DotMemoryDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null) - { - this.nugetUrl = nugetUrl; - this.toolsDownloadFolder = toolsDownloadFolder; - } - public IEnumerable Ids => new[] { "DotMemory" }; public string ShortName => "dotMemory"; diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs index a474312978..f9a3612471 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs @@ -13,9 +13,10 @@ public DotMemoryDiagnoserAttribute() Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser()); } - public DotMemoryDiagnoserAttribute(Uri? nugetUrl = null, string? toolsDownloadFolder = null) + public DotMemoryDiagnoserAttribute(string? nugetUrl = null, string? toolsDownloadFolder = null) { - Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser(nugetUrl, toolsDownloadFolder)); + var nugetUri = nugetUrl == null ? null : new Uri(nugetUrl); + Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser(nugetUri, toolsDownloadFolder)); } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs index fe9e77005a..2aada424b7 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs @@ -17,17 +17,8 @@ namespace BenchmarkDotNet.Diagnostics.dotTrace { - public class DotTraceDiagnoser : IProfiler + public class DotTraceDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null) : IProfiler { - private readonly Uri? nugetUrl; - private readonly string? toolsDownloadFolder; - - public DotTraceDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null) - { - this.nugetUrl = nugetUrl; - this.toolsDownloadFolder = toolsDownloadFolder; - } - public IEnumerable Ids => new[] { "DotTrace" }; public string ShortName => "dotTrace"; diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs index 19e88a6de1..90d4173016 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs @@ -13,9 +13,10 @@ public DotTraceDiagnoserAttribute() Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser()); } - public DotTraceDiagnoserAttribute(Uri? nugetUrl = null, string? toolsDownloadFolder = null) + public DotTraceDiagnoserAttribute(string? nugetUrl = null, string? toolsDownloadFolder = null) { - Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser(nugetUrl, toolsDownloadFolder)); + var nugetUri = nugetUrl == null ? null : new Uri(nugetUrl); + Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser(nugetUri, toolsDownloadFolder)); } } } \ No newline at end of file From d98a1d221d8a1968d72be071193e36fad7d99eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 22 Apr 2024 19:34:55 +0900 Subject: [PATCH 04/57] Fix names of instructions sets for PublishAot scenarios (#2566) --- src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs index bbeb0113d8..2f2a6d80f3 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs @@ -248,11 +248,11 @@ private IEnumerable GetCurrentProcessInstructionSets(Platform platform) if (HardwareIntrinsics.IsX86Sse42Supported) yield return "sse4.2"; if (HardwareIntrinsics.IsX86AvxSupported) yield return "avx"; if (HardwareIntrinsics.IsX86Avx2Supported) yield return "avx2"; - if (HardwareIntrinsics.IsX86Avx512FSupported) yield return "avx-512f"; - if (HardwareIntrinsics.IsX86Avx512BWSupported) yield return "avx-512bw"; - if (HardwareIntrinsics.IsX86Avx512CDSupported) yield return "avx-512cd"; - if (HardwareIntrinsics.IsX86Avx512DQSupported) yield return "avx-512dq"; - if (HardwareIntrinsics.IsX86Avx512VbmiSupported) yield return "avx-512vbmi"; + if (HardwareIntrinsics.IsX86Avx512FSupported) yield return "avx512f"; + if (HardwareIntrinsics.IsX86Avx512BWSupported) yield return "avx512bw"; + if (HardwareIntrinsics.IsX86Avx512CDSupported) yield return "avx512cd"; + if (HardwareIntrinsics.IsX86Avx512DQSupported) yield return "avx512dq"; + if (HardwareIntrinsics.IsX86Avx512VbmiSupported) yield return "avx512vbmi"; if (HardwareIntrinsics.IsX86AesSupported) yield return "aes"; if (HardwareIntrinsics.IsX86Bmi1Supported) yield return "bmi"; if (HardwareIntrinsics.IsX86Bmi2Supported) yield return "bmi2"; From 205ce61313e7f1b1410d72b20951736b54c70cfd Mon Sep 17 00:00:00 2001 From: Omkar Gorde <72370559+AumkarGorde@users.noreply.github.com> Date: Sun, 28 Apr 2024 06:20:42 +0530 Subject: [PATCH 05/57] Fix - incorrect table markdown #2545 (#2565) * Fix - incorrect table markdown #2545 * Hide Column Table Markdown test case * columHidingRules support for CreateSummary in MockFactory * made the test case more generic to support hide columns attribute * Update tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs --------- Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> --- .../Exporters/MarkdownExporter.cs | 4 +-- .../Exporters/MarkdownExporterVerifyTests.cs | 28 ++++++++++++------- ...est_HideColumns_TableMarkDown.verified.txt | 15 ++++++++++ .../Mocks/MockFactory.cs | 4 +-- 4 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_HideColumns_TableMarkDown.verified.txt diff --git a/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs b/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs index 668396baa8..2a106e0132 100644 --- a/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs +++ b/src/BenchmarkDotNet/Exporters/MarkdownExporter.cs @@ -164,9 +164,9 @@ private void PrintTable(SummaryTable table, ILogger logger) logger.WriteStatistic(ColumnsStartWithSeparator ? TableHeaderSeparator.TrimStart().TrimEnd() + "-" : "-"); logger.WriteLineStatistic(string.Join("", - table.Columns.Where(c => c.NeedToShow).Select(column => + table.Columns.Where(c => c.NeedToShow).Select((column, index) => new string('-', column.Width - 1) + GetHeaderSeparatorIndicator(column.OriginalColumn.IsNumeric) + - GetHeaderSeparatorColumnDivider(column.Index, table.ColumnCount)))); + GetHeaderSeparatorColumnDivider(index, table.Columns.Where(c => c.NeedToShow).Count())))); } int rowCounter = 0; diff --git a/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs b/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs index 9d957f4f61..fca08d0c0e 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs +++ b/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs @@ -14,6 +14,8 @@ using JetBrains.Annotations; using VerifyXunit; using Xunit; +using BenchmarkDotNet.Columns; +using System.Reflection; namespace BenchmarkDotNet.Tests.Exporters { @@ -40,9 +42,8 @@ public Task GroupExporterTest(Type benchmarkType) var logger = new AccumulationLogger(); logger.WriteLine("=== " + benchmarkType.Name + " ==="); - var exporter = MarkdownExporter.Mock; - var summary = MockFactory.CreateSummary(benchmarkType); + var summary = MockFactory.CreateSummary(benchmarkType, benchmarkType.GetCustomAttribute()?.Config.GetColumnHidingRules().ToArray() ?? []); exporter.ExportToLog(summary, logger); var validator = BaselineValidator.FailOnError; @@ -219,8 +220,8 @@ [Benchmark] public void Bar() { } [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2")] public class MethodJobBaseline_MethodsJobs { - [Benchmark(Baseline = true)] public void Foo() {} - [Benchmark] public void Bar() {} + [Benchmark(Baseline = true)] public void Foo() { } + [Benchmark] public void Bar() { } } [RankColumn, LogicalGroupColumn, BaselineColumn] @@ -229,8 +230,8 @@ public class MethodJobBaseline_MethodsJobsParams { [Params(2, 10), UsedImplicitly] public int Param; - [Benchmark(Baseline = true)] public void Foo() {} - [Benchmark] public void Bar() {} + [Benchmark(Baseline = true)] public void Foo() { } + [Benchmark] public void Bar() { } } /* Invalid */ @@ -238,16 +239,16 @@ [Benchmark] public void Bar() {} [RankColumn, LogicalGroupColumn, BaselineColumn] public class Invalid_TwoMethodBaselines { - [Benchmark(Baseline = true)] public void Foo() {} - [Benchmark(Baseline = true)] public void Bar() {} + [Benchmark(Baseline = true)] public void Foo() { } + [Benchmark(Baseline = true)] public void Bar() { } } [RankColumn, LogicalGroupColumn, BaselineColumn] [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2", baseline: true)] public class Invalid_TwoJobBaselines { - [Benchmark] public void Foo() {} - [Benchmark] public void Bar() {} + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } } /* Escape Params */ @@ -260,6 +261,13 @@ public class Escape_ParamsAndArguments [Benchmark] public void Foo(char charArg) {} [Benchmark] public void Bar() {} } + + /* Hide Column */ + [HideColumns(Column.StdDev)] + public class HideColumns_TableMarkDown + { + [Benchmark] public void Foo() { } + } } } } diff --git a/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_HideColumns_TableMarkDown.verified.txt b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_HideColumns_TableMarkDown.verified.txt new file mode 100644 index 0000000000..5b932e42da --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Exporters/VerifiedFiles/MarkdownExporterVerifyTests.GroupExporterTest_HideColumns_TableMarkDown.verified.txt @@ -0,0 +1,15 @@ +=== HideColumns_TableMarkDown === + +BenchmarkDotNet v0.10.x-mock, Microsoft Windows NT 10.0.x.mock (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.062 ns, Timer: TSC + [Host] : Clr 4.0.x.mock, 64mock RyuJIT-v4.6.x.mock CONFIGURATION + DefaultJob : extra output line + +StdDev=8.80 ns + + Method | Mean | Error | +------- |---------:|--------:| + Foo | 114.5 ns | 5.88 ns | + +Errors: 0 diff --git a/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs b/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs index eb8402f5db..8c0ce0019e 100644 --- a/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs +++ b/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs @@ -24,7 +24,7 @@ namespace BenchmarkDotNet.Tests.Mocks { public static class MockFactory { - public static Summary CreateSummary(Type benchmarkType) + public static Summary CreateSummary(Type benchmarkType, params IColumnHidingRule[] columHidingRules) { var runInfo = BenchmarkConverter.TypeToBenchmarks(benchmarkType); return new Summary( @@ -36,7 +36,7 @@ public static Summary CreateSummary(Type benchmarkType) TimeSpan.FromMinutes(1), TestCultureInfo.Instance, ImmutableArray.Empty, - ImmutableArray.Empty); + ImmutableArray.Create(columHidingRules)); } public static Summary CreateSummary(IConfig config) => new Summary( From 063d2f0dda0e3a1e0655de666d988d1b76304be4 Mon Sep 17 00:00:00 2001 From: Alon Sheffer <38475934+sheffer21@users.noreply.github.com> Date: Sun, 19 May 2024 15:54:48 +0300 Subject: [PATCH 06/57] Fix BenchmarkDotNet test adapter not running tests with dotnet test (#2571) * fix VsTestAdapter test filtering * CR fixes --- src/BenchmarkDotNet.TestAdapter/BenchmarkExecutor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet.TestAdapter/BenchmarkExecutor.cs b/src/BenchmarkDotNet.TestAdapter/BenchmarkExecutor.cs index b13a4eaf4a..f1cd64f34e 100644 --- a/src/BenchmarkDotNet.TestAdapter/BenchmarkExecutor.cs +++ b/src/BenchmarkDotNet.TestAdapter/BenchmarkExecutor.cs @@ -38,7 +38,7 @@ public void RunBenchmarks(string assemblyPath, TestExecutionRecorderWrapper reco foreach (var benchmarkCase in benchmark.BenchmarksCases) { var testId = benchmarkCase.GetTestCaseId(); - if (benchmarkIds != null && benchmarkIds.Contains(testId)) + if (benchmarkIds == null || benchmarkIds.Contains(testId)) { filteredCases.Add(benchmarkCase); testCases.Add(benchmarkCase.ToVsTestCase(assemblyPath, needsJobInfo)); From 6a7244d76082f098a19785e4e3b0e0f269fed004 Mon Sep 17 00:00:00 2001 From: Alon Sheffer <38475934+sheffer21@users.noreply.github.com> Date: Sat, 1 Jun 2024 10:23:23 +0300 Subject: [PATCH 07/57] Add warning support (#2572) --- src/BenchmarkDotNet.TestAdapter/VSTestLogger.cs | 8 ++++++-- src/BenchmarkDotNet/Analysers/ConclusionHelper.cs | 2 +- src/BenchmarkDotNet/Loggers/ConsoleLogger.cs | 1 + src/BenchmarkDotNet/Loggers/LinqPadLogger.cs | 2 ++ src/BenchmarkDotNet/Loggers/LogKind.cs | 2 +- src/BenchmarkDotNet/Loggers/LoggerExtensions.cs | 4 ++++ 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/BenchmarkDotNet.TestAdapter/VSTestLogger.cs b/src/BenchmarkDotNet.TestAdapter/VSTestLogger.cs index 28fc54995c..c9a8de620f 100644 --- a/src/BenchmarkDotNet.TestAdapter/VSTestLogger.cs +++ b/src/BenchmarkDotNet.TestAdapter/VSTestLogger.cs @@ -33,8 +33,12 @@ public void Write(LogKind logKind, string text) // Assume that if the log kind is an error, that the whole line is treated as an error // The level will be reset to Informational when WriteLine() is called. - if (logKind == LogKind.Error) - currentLevel = TestMessageLevel.Error; + currentLevel = logKind switch + { + LogKind.Error => TestMessageLevel.Error, + LogKind.Warning => TestMessageLevel.Warning, + _ => currentLevel + }; } public void WriteLine() diff --git a/src/BenchmarkDotNet/Analysers/ConclusionHelper.cs b/src/BenchmarkDotNet/Analysers/ConclusionHelper.cs index 9542230606..af872414f2 100644 --- a/src/BenchmarkDotNet/Analysers/ConclusionHelper.cs +++ b/src/BenchmarkDotNet/Analysers/ConclusionHelper.cs @@ -10,7 +10,7 @@ public static class ConclusionHelper public static void Print(ILogger logger, IEnumerable conclusions) { PrintFiltered(conclusions, ConclusionKind.Error, "Errors", logger.WriteLineError); - PrintFiltered(conclusions, ConclusionKind.Warning, "Warnings", logger.WriteLineError); + PrintFiltered(conclusions, ConclusionKind.Warning, "Warnings", logger.WriteLineWarning); PrintFiltered(conclusions, ConclusionKind.Hint, "Hints", logger.WriteLineHint); } diff --git a/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs b/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs index a1c7795d53..20aa2f7a54 100644 --- a/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs +++ b/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs @@ -79,6 +79,7 @@ private static Dictionary CreateColorfulScheme() => { LogKind.Statistic, ConsoleColor.Cyan }, { LogKind.Info, ConsoleColor.DarkYellow }, { LogKind.Error, ConsoleColor.Red }, + { LogKind.Warning, ConsoleColor.Yellow }, { LogKind.Hint, ConsoleColor.DarkCyan } }; diff --git a/src/BenchmarkDotNet/Loggers/LinqPadLogger.cs b/src/BenchmarkDotNet/Loggers/LinqPadLogger.cs index 01fa682c99..cbb023848f 100644 --- a/src/BenchmarkDotNet/Loggers/LinqPadLogger.cs +++ b/src/BenchmarkDotNet/Loggers/LinqPadLogger.cs @@ -82,6 +82,7 @@ private static IReadOnlyDictionary CreateDarkScheme() => { LogKind.Statistic, "#00FFFF" }, { LogKind.Info, "#808000" }, { LogKind.Error, "#FF0000" }, + { LogKind.Warning, "#FFFF00" }, { LogKind.Hint, "#008080" } }; @@ -95,6 +96,7 @@ private static IReadOnlyDictionary CreateLightScheme() => { LogKind.Statistic, "#008080" }, { LogKind.Info, "#808000" }, { LogKind.Error, "#FF0000" }, + { LogKind.Warning, "#FFFF00" }, { LogKind.Hint, "#008080" } }; } diff --git a/src/BenchmarkDotNet/Loggers/LogKind.cs b/src/BenchmarkDotNet/Loggers/LogKind.cs index 8f3dff72a8..8fa4660aa8 100644 --- a/src/BenchmarkDotNet/Loggers/LogKind.cs +++ b/src/BenchmarkDotNet/Loggers/LogKind.cs @@ -2,6 +2,6 @@ { public enum LogKind { - Default, Help, Header, Result, Statistic, Info, Error, Hint + Default, Help, Header, Result, Statistic, Info, Error, Hint, Warning } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Loggers/LoggerExtensions.cs b/src/BenchmarkDotNet/Loggers/LoggerExtensions.cs index 866620c3fb..1d75341bb8 100644 --- a/src/BenchmarkDotNet/Loggers/LoggerExtensions.cs +++ b/src/BenchmarkDotNet/Loggers/LoggerExtensions.cs @@ -18,6 +18,8 @@ public static class LoggerExtensions public static void WriteLineError(this ILogger logger, string text) => logger.WriteLine(LogKind.Error, text); + public static void WriteLineWarning(this ILogger logger, string text) => logger.WriteLine(LogKind.Warning, text); + public static void WriteLineHint(this ILogger logger, string text) => logger.WriteLine(LogKind.Hint, text); public static void Write(this ILogger logger, string text) => logger.Write(LogKind.Default, text); @@ -36,6 +38,8 @@ public static class LoggerExtensions public static void WriteError(this ILogger logger, string text) => logger.Write(LogKind.Error, text); + public static void WriteWarning(this ILogger logger, string text) => logger.Write(LogKind.Warning, text); + [PublicAPI] public static void WriteHint(this ILogger logger, string text) => logger.Write(LogKind.Hint, text); } From 01d9b7889e13cf756068985275a3997217e4a102 Mon Sep 17 00:00:00 2001 From: Kenneth Cochran Date: Thu, 11 Jul 2024 14:56:34 -0500 Subject: [PATCH 08/57] Update README.md (#2583) Made simplicity verbiage more inclusive --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f788590301..c18916d2d2 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Four aspects define the design of these features: ### Simplicity -You shouldn't be an experienced performance engineer if you want to write benchmarks. +You shouldn't have to be an experienced performance engineer if you want to write benchmarks. You can design very complicated performance experiments in the declarative style using simple APIs. For example, if you want to [parameterize](https://benchmarkdotnet.org/articles/features/parameterization.html) your benchmark, @@ -277,4 +277,4 @@ BenchmarkDotNet is supported by the [AWS Open Source Software Fund](https://gith [![](https://gist.githubusercontent.com/AndreyAkinshin/f08c77960c064233348157215781b13b/raw/9d00f0ba4acb8861f9287de5d43b0ec60d7a55ac/aws-logo-small.png)](https://github.com/aws/dotnet-foss) - \ No newline at end of file + From 20e2ee7ded7881659fd29143167b93925f16543d Mon Sep 17 00:00:00 2001 From: Matt Chaulklin <85208563+MattFromRVA@users.noreply.github.com> Date: Sat, 20 Jul 2024 03:40:01 -0400 Subject: [PATCH 09/57] Docs for SummaryStyle (#2510) * Added SummaryStyle doc and sample * Fixed link to source code * correct spelling --- docs/articles/samples/IntroSummaryStyle.md | 51 +++++++++++++++++++ .../IntroSummaryStyle.cs | 39 ++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 docs/articles/samples/IntroSummaryStyle.md create mode 100644 samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs diff --git a/docs/articles/samples/IntroSummaryStyle.md b/docs/articles/samples/IntroSummaryStyle.md new file mode 100644 index 0000000000..470bb901a1 --- /dev/null +++ b/docs/articles/samples/IntroSummaryStyle.md @@ -0,0 +1,51 @@ +--- +uid: BenchmarkDotNet.SummaryStyle +--- + +## SummaryStyle in BenchmarkDotNet + +`SummaryStyle` is a class in BenchmarkDotNet that allows customization of the summary reports of benchmark results. It offers several properties to fine-tune how the results are displayed. + +### Usage + +You can customize the summary report by specifying various properties of `SummaryStyle`. These properties include formatting options like whether to print units in the header or content, setting the maximum width for parameter columns, and choosing units for size and time measurements. + +### Source Code + +[!code-csharp[IntroSummaryStyle.cs](../../../samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs)] + +### Properties + +- `PrintUnitsInHeader`: Boolean to indicate if units should be printed in the header. +- `PrintUnitsInContent`: Boolean to control unit printing in the content. +- `PrintZeroValuesInContent`: Determines if zero values should be printed. +- `MaxParameterColumnWidth`: Integer defining the max width for parameter columns. +- `SizeUnit`: Optional `SizeUnit` to specify the unit for size measurements. +- `TimeUnit`: Optional `TimeUnit` for time measurement units. +- `CultureInfo`: `CultureInfo` to define culture-specific formatting. + +### Example Output + +Using SummaryStyle options: + +```markdown +| Method | N | Mean [ns] | Error [ns] | StdDev [ns] | +|------- |---- |--------------:|-----------:|------------:| +| Sleep | 10 | 15,644,973.1 | 32,808.7 | 30,689.3 | +| Sleep | 100 | 109,440,686.7 | 236,673.8 | 221,384.8 | +``` + +Default: + +```markdown +| Method | N | Mean | Error | StdDev | +|------- |---- |----------:|---------:|---------:| +| Sleep | 10 | 15.65 ms | 0.039 ms | 0.034 ms | +| Sleep | 100 | 109.20 ms | 0.442 ms | 0.392 ms | +``` + +### Links + +* @docs.SummaryStyle +* The permanent link to this sample: @BenchmarkDotNet.Samples.IntroSummaryStyle + diff --git a/samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs b/samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs new file mode 100644 index 0000000000..e24fda60e4 --- /dev/null +++ b/samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs @@ -0,0 +1,39 @@ +using System.Globalization; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Columns; +using Perfolizer.Horology; + +namespace BenchmarkDotNet.Samples +{ + [Config(typeof(Config))] + public class IntroSummaryStyle + { + private class Config : ManualConfig + { + public Config() + { + // Configure the summary style here + var summaryStyle = new SummaryStyle + ( + cultureInfo: CultureInfo.InvariantCulture, + printUnitsInHeader: true, + printUnitsInContent: false, + sizeUnit: SizeUnit.KB, + timeUnit: TimeUnit.Nanosecond, + maxParameterColumnWidth: 20 + + ); + + WithSummaryStyle(summaryStyle); + } + } + + [Params(10, 100)] + public int N; + + [Benchmark] + public void Sleep() => System.Threading.Thread.Sleep(N); + } +} \ No newline at end of file From e933bb00774617d51f0bee8508937b7d945599b5 Mon Sep 17 00:00:00 2001 From: Andrew Chisholm Date: Sat, 20 Jul 2024 17:57:01 +1000 Subject: [PATCH 10/57] Initial ScottPlotExporter with just Bar Plot and Unit Tests (#2560) * Initial ScottPlotExporter with just Bar Plot and Unit Tests * Simplifying project settings, added missing common.props, adde some documentation for config settings. * Removed redundant warning suppressions * Fix missing public documentation * Removed redundant condition * Update tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj --------- Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> --- BenchmarkDotNet.sln | 14 + .../BenchmarkDotNet.Exporters.Plotting.csproj | 19 ++ .../ScottPlotExporter.cs | 256 ++++++++++++++++++ ...markDotNet.Exporters.Plotting.Tests.csproj | 26 ++ .../ScottPlotExporterTests.cs | 240 ++++++++++++++++ 5 files changed, 555 insertions(+) create mode 100644 src/BenchmarkDotNet.Exporters.Plotting/BenchmarkDotNet.Exporters.Plotting.csproj create mode 100644 src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs create mode 100644 tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj create mode 100644 tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs diff --git a/BenchmarkDotNet.sln b/BenchmarkDotNet.sln index d51cb4e029..1df6c0aabd 100644 --- a/BenchmarkDotNet.sln +++ b/BenchmarkDotNet.sln @@ -55,6 +55,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.TestAdapter EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Diagnostics.dotMemory", "src\BenchmarkDotNet.Diagnostics.dotMemory\BenchmarkDotNet.Diagnostics.dotMemory.csproj", "{2E2283A3-6DA6-4482-8518-99D6D9F689AB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.Plotting", "src\BenchmarkDotNet.Exporters.Plotting\BenchmarkDotNet.Exporters.Plotting.csproj", "{B92ECCEF-7C27-4012-9E19-679F3C40A6A6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.Plotting.Tests", "tests\BenchmarkDotNet.Exporters.Plotting.Tests\BenchmarkDotNet.Exporters.Plotting.Tests.csproj", "{199AC83E-30BD-40CD-87CE-0C838AC0320D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -149,6 +153,14 @@ Global {2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Release|Any CPU.Build.0 = Release|Any CPU + {B92ECCEF-7C27-4012-9E19-679F3C40A6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B92ECCEF-7C27-4012-9E19-679F3C40A6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B92ECCEF-7C27-4012-9E19-679F3C40A6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B92ECCEF-7C27-4012-9E19-679F3C40A6A6}.Release|Any CPU.Build.0 = Release|Any CPU + {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -176,6 +188,8 @@ Global {AACA2C63-A85B-47AB-99FC-72C3FF408B14} = {14195214-591A-45B7-851A-19D3BA2413F9} {4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} {2E2283A3-6DA6-4482-8518-99D6D9F689AB} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} + {B92ECCEF-7C27-4012-9E19-679F3C40A6A6} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} + {199AC83E-30BD-40CD-87CE-0C838AC0320D} = {14195214-591A-45B7-851A-19D3BA2413F9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4D9AF12B-1F7F-45A7-9E8C-E4E46ADCBD1F} diff --git a/src/BenchmarkDotNet.Exporters.Plotting/BenchmarkDotNet.Exporters.Plotting.csproj b/src/BenchmarkDotNet.Exporters.Plotting/BenchmarkDotNet.Exporters.Plotting.csproj new file mode 100644 index 0000000000..799a055885 --- /dev/null +++ b/src/BenchmarkDotNet.Exporters.Plotting/BenchmarkDotNet.Exporters.Plotting.csproj @@ -0,0 +1,19 @@ + + + + BenchmarkDotNet plotting export support. + netstandard2.0 + BenchmarkDotNet.Exporters.Plotting + BenchmarkDotNet.Exporters.Plotting + BenchmarkDotNet.Exporters.Plotting + + True + enable + + + + + + + + diff --git a/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs b/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs new file mode 100644 index 0000000000..cfb7a735b7 --- /dev/null +++ b/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Properties; +using BenchmarkDotNet.Reports; +using ScottPlot; +using ScottPlot.Plottables; + +namespace BenchmarkDotNet.Exporters.Plotting +{ + /// + /// Provides plot exports as .png files. + /// + public class ScottPlotExporter : IExporter + { + /// + /// Default instance of the exporter with default configuration. + /// + public static readonly IExporter Default = new ScottPlotExporter(); + + /// + /// Gets the name of the Exporter type. + /// + public string Name => nameof(ScottPlotExporter); + + /// + /// Initializes a new instance of ScottPlotExporter. + /// + /// The width of all plots in pixels (optional). Defaults to 1920. + /// The height of all plots in pixels (optional). Defaults to 1080. + public ScottPlotExporter(int width = 1920, int height = 1080) + { + this.Width = width; + this.Height = height; + this.IncludeBarPlot = true; + this.RotateLabels = true; + } + + /// + /// Gets or sets the width of all plots in pixels. + /// + public int Width { get; set; } + + /// + /// Gets or sets the height of all plots in pixels. + /// + public int Height { get; set; } + + /// + /// Gets or sets a value indicating whether labels for Plot X-axis should be rotated. + /// This allows for longer labels at the expense of chart height. + /// + public bool RotateLabels { get; set; } + + /// + /// Gets or sets a value indicating whether a bar plot for time-per-op + /// measurement values should be exported. + /// + public bool IncludeBarPlot { get; set; } + + /// + /// Not supported. + /// + /// This parameter is not used. + /// This parameter is not used. + /// + public void ExportToLog(Summary summary, ILogger logger) + { + throw new NotSupportedException(); + } + + /// + /// Exports plots to .png file. + /// + /// The summary to be exported. + /// Logger to output to. + /// The file paths of every plot exported. + public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) + { + var title = summary.Title; + var version = BenchmarkDotNetInfo.Instance.BrandTitle; + var annotations = GetAnnotations(version); + + var (timeUnit, timeScale) = GetTimeUnit(summary.Reports.SelectMany(m => m.AllMeasurements)); + + foreach (var benchmark in summary.Reports.GroupBy(r => r.BenchmarkCase.Descriptor.Type.Name)) + { + var benchmarkName = benchmark.Key; + + // Get the measurement nanoseconds per op, divided by time scale, grouped by target and Job [param]. + var timeStats = from report in benchmark + let jobId = report.BenchmarkCase.DisplayInfo.Replace(report.BenchmarkCase.Descriptor.DisplayInfo + ": ", string.Empty) + from measurement in report.AllMeasurements + let measurementValue = measurement.Nanoseconds / measurement.Operations + group measurementValue / timeScale by (Target: report.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo, JobId: jobId) into g + select (g.Key.Target, g.Key.JobId, Mean: g.Average(), StdError: StandardError(g.ToList())); + + if (this.IncludeBarPlot) + { + // -barplot.png + yield return CreateBarPlot( + $"{title} - {benchmarkName}", + Path.Combine(summary.ResultsDirectoryPath, $"{title}-{benchmarkName}-barplot.png"), + $"Time ({timeUnit})", + "Target", + timeStats, + annotations); + } + + /* TODO: Rest of the RPlotExporter plots. + -boxplot.png + --density.png + --facetTimeline.png + --facetTimelineSmooth.png + ---timelineSmooth.png + ---timelineSmooth.png*/ + } + } + + /// + /// Calculate Standard Deviation. + /// + /// Values to calculate from. + /// Standard deviation of values. + private static double StandardError(IReadOnlyList values) + { + double average = values.Average(); + double sumOfSquaresOfDifferences = values.Select(val => (val - average) * (val - average)).Sum(); + double standardDeviation = Math.Sqrt(sumOfSquaresOfDifferences / values.Count); + return standardDeviation / Math.Sqrt(values.Count); + } + + /// + /// Gets the lowest appropriate time scale across all measurements. + /// + /// All measurements + /// A unit and scaling factor to convert from nanoseconds. + private (string Unit, double ScaleFactor) GetTimeUnit(IEnumerable values) + { + var minValue = values.Select(m => m.Nanoseconds / m.Operations).DefaultIfEmpty(0d).Min(); + if (minValue > 1000000000d) + { + return ("sec", 1000000000d); + } + + if (minValue > 1000000d) + { + return ("ms", 1000000d); + } + + if (minValue > 1000d) + { + return ("us", 1000d); + } + + return ("ns", 1d); + } + + private string CreateBarPlot(string title, string fileName, string yLabel, string xLabel, IEnumerable<(string Target, string JobId, double Mean, double StdError)> data, IReadOnlyList annotations) + { + Plot plt = new Plot(); + plt.Title(title, 28); + plt.YLabel(yLabel); + plt.XLabel(xLabel); + + var palette = new ScottPlot.Palettes.Category10(); + + var legendPalette = data.Select(d => d.JobId) + .Distinct() + .Select((jobId, index) => (jobId, index)) + .ToDictionary(t => t.jobId, t => palette.GetColor(t.index)); + + plt.Legend.IsVisible = true; + plt.Legend.Location = Alignment.UpperRight; + var legend = data.Select(d => d.JobId) + .Distinct() + .Select((label, index) => new LegendItem() + { + Label = label, + FillColor = legendPalette[label] + }) + .ToList(); + + plt.Legend.ManualItems.AddRange(legend); + + var jobCount = plt.Legend.ManualItems.Count; + var ticks = data + .Select((d, index) => new Tick(index, d.Target)) + .ToArray(); + plt.Axes.Bottom.TickGenerator = new ScottPlot.TickGenerators.NumericManual(ticks); + plt.Axes.Bottom.MajorTickStyle.Length = 0; + + if (this.RotateLabels) + { + plt.Axes.Bottom.TickLabelStyle.Rotation = 45; + plt.Axes.Bottom.TickLabelStyle.Alignment = Alignment.MiddleLeft; + + // determine the width of the largest tick label + float largestLabelWidth = 0; + foreach (Tick tick in ticks) + { + PixelSize size = plt.Axes.Bottom.TickLabelStyle.Measure(tick.Label); + largestLabelWidth = Math.Max(largestLabelWidth, size.Width); + } + + // ensure axis panels do not get smaller than the largest label + plt.Axes.Bottom.MinimumSize = largestLabelWidth; + plt.Axes.Right.MinimumSize = largestLabelWidth; + } + + var bars = data + .Select((d, index) => new Bar() + { + Position = ticks[index].Position, + Value = d.Mean, + Error = d.StdError, + FillColor = legendPalette[d.JobId] + }); + plt.Add.Bars(bars); + + // Tell the plot to autoscale with no padding beneath the bars + plt.Axes.Margins(bottom: 0, right: .2); + + plt.PlottableList.AddRange(annotations); + + plt.SavePng(fileName, this.Width, this.Height); + return Path.GetFullPath(fileName); + } + + /// + /// Provides a list of annotations to put over the data area. + /// + /// The version to be displayed. + /// A list of annotations for every plot. + private IReadOnlyList GetAnnotations(string version) + { + var versionAnnotation = new Annotation() + { + Label = + { + Text = version, + FontSize = 14, + ForeColor = new Color(0, 0, 0, 100) + }, + OffsetY = 10, + OffsetX = 20, + Alignment = Alignment.LowerRight + }; + + + return new[] { versionAnnotation }; + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj new file mode 100644 index 0000000000..0c61fd72af --- /dev/null +++ b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj @@ -0,0 +1,26 @@ + + + + net8.0;net462 + false + true + true + false + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs new file mode 100644 index 0000000000..a585b9df86 --- /dev/null +++ b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs @@ -0,0 +1,240 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Tests.Mocks; +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Xunit; + +namespace BenchmarkDotNet.Exporters.Plotting.Tests +{ + public class ScottPlotExporterTests + { + public static TheoryData GetGroupBenchmarkTypes() + { + var data = new TheoryData(); + foreach (var type in typeof(BaselinesBenchmarks).GetNestedTypes()) + data.Add(type); + return data; + } + + [Theory] + [MemberData(nameof(GetGroupBenchmarkTypes))] + public void BarPlots(Type benchmarkType) + { + var logger = new AccumulationLogger(); + logger.WriteLine("=== " + benchmarkType.Name + " ==="); + + var exporter = new ScottPlotExporter(); + var summary = MockFactory.CreateSummary(benchmarkType); + var filePaths = exporter.ExportToFiles(summary, logger).ToList(); + Assert.NotEmpty(filePaths); + Assert.All(filePaths, f => File.Exists(f)); + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public static class BaselinesBenchmarks + { + /* NoBaseline */ + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1"), SimpleJob(id: "Job2")] + public class NoBaseline_MethodsParamsJobs + { + [Params(2, 10)] public int Param; + + [Benchmark] public void Base() { } + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1"), SimpleJob(id: "Job2")] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByMethod)] + public class NoBaseline_MethodsParamsJobs_GroupByMethod + { + [Params(2, 10)] public int Param; + + [Benchmark, BenchmarkCategory("CatA")] public void Base() { } + [Benchmark, BenchmarkCategory("CatB")] public void Foo() { } + [Benchmark, BenchmarkCategory("CatB")] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1"), SimpleJob(id: "Job2")] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByJob)] + public class NoBaseline_MethodsParamsJobs_GroupByJob + { + [Params(2, 10)] public int Param; + + [Benchmark, BenchmarkCategory("CatA")] public void Base() { } + [Benchmark, BenchmarkCategory("CatB")] public void Foo() { } + [Benchmark, BenchmarkCategory("CatB")] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1"), SimpleJob(id: "Job2")] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByParams)] + public class NoBaseline_MethodsParamsJobs_GroupByParams + { + [Params(2, 10)] public int Param; + + [Benchmark, BenchmarkCategory("CatA")] public void Base() { } + [Benchmark, BenchmarkCategory("CatB")] public void Foo() { } + [Benchmark, BenchmarkCategory("CatB")] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1"), SimpleJob(id: "Job2")] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + public class NoBaseline_MethodsParamsJobs_GroupByCategory + { + [Params(2, 10)] public int Param; + + [Benchmark(Baseline = true), BenchmarkCategory("CatA")] + public void A1() { } + + [Benchmark, BenchmarkCategory("CatA")] public void A2() { } + + [Benchmark(Baseline = true), BenchmarkCategory("CatB")] + public void B1() { } + + [Benchmark, BenchmarkCategory("CatB")] public void B2() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1"), SimpleJob(id: "Job2")] + [GroupBenchmarksBy( + BenchmarkLogicalGroupRule.ByMethod, + BenchmarkLogicalGroupRule.ByJob, + BenchmarkLogicalGroupRule.ByParams, + BenchmarkLogicalGroupRule.ByCategory)] + public class NoBaseline_MethodsParamsJobs_GroupByAll + { + [Params(2, 10)] public int Param; + + [Benchmark(Baseline = true), BenchmarkCategory("CatA")] + public void A1() { } + + [Benchmark, BenchmarkCategory("CatA")] public void A2() { } + + [Benchmark(Baseline = true), BenchmarkCategory("CatB")] + public void B1() { } + + [Benchmark, BenchmarkCategory("CatB")] public void B2() { } + } + + /* MethodBaseline */ + + [RankColumn, LogicalGroupColumn, BaselineColumn] + public class MethodBaseline_Methods + { + [Benchmark(Baseline = true)] public void Base() { } + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + public class MethodBaseline_MethodsParams + { + [Params(2, 10)] public int Param; + + [Benchmark(Baseline = true)] public void Base() { } + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1"), SimpleJob(id: "Job2")] + public class MethodBaseline_MethodsJobs + { + [Benchmark(Baseline = true)] public void Base() { } + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1"), SimpleJob(id: "Job2")] + public class MethodBaseline_MethodsParamsJobs + { + [Params(2, 10)] public int Param; + + [Benchmark(Baseline = true)] public void Base() { } + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } + } + + /* JobBaseline */ + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2")] + public class JobBaseline_MethodsJobs + { + [Benchmark] public void Base() { } + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2")] + public class JobBaseline_MethodsParamsJobs + { + [Params(2, 10)] public int Param; + + [Benchmark] public void Base() { } + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } + } + + /* MethodJobBaseline */ + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2")] + public class MethodJobBaseline_MethodsJobs + { + [Benchmark(Baseline = true)] public void Foo() { } + [Benchmark] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2")] + public class MethodJobBaseline_MethodsJobsParams + { + [Params(2, 10)] public int Param; + + [Benchmark(Baseline = true)] public void Foo() { } + [Benchmark] public void Bar() { } + } + + /* Invalid */ + + [RankColumn, LogicalGroupColumn, BaselineColumn] + public class Invalid_TwoMethodBaselines + { + [Benchmark(Baseline = true)] public void Foo() { } + [Benchmark(Baseline = true)] public void Bar() { } + } + + [RankColumn, LogicalGroupColumn, BaselineColumn] + [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2", baseline: true)] + public class Invalid_TwoJobBaselines + { + [Benchmark] public void Foo() { } + [Benchmark] public void Bar() { } + } + + /* Escape Params */ + + public class Escape_ParamsAndArguments + { + [Params("\t", "\n")] public string StringParam; + + [Arguments('\t')] + [Arguments('\n')] + [Benchmark] public void Foo(char charArg) { } + [Benchmark] public void Bar() { } + } + } + } +} \ No newline at end of file From f8082a2138b7cf1bda1eab8dca98d7d3c43b9946 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Sat, 20 Jul 2024 10:01:19 +0200 Subject: [PATCH 11/57] Fix IntroSummaryStyle compilation --- samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs b/samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs index e24fda60e4..6a9830be18 100644 --- a/samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs +++ b/samples/BenchmarkDotNet.Samples/IntroSummaryStyle.cs @@ -2,8 +2,8 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Reports; -using BenchmarkDotNet.Columns; using Perfolizer.Horology; +using Perfolizer.Metrology; namespace BenchmarkDotNet.Samples { From 834417a7dbec1dbb22a99cbb5f45c9cd474e483e Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Sat, 20 Jul 2024 10:03:05 +0200 Subject: [PATCH 12/57] Improve logging in ScottPlotExporterTests --- .../ScottPlotExporterTests.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs index a585b9df86..32f69061f6 100644 --- a/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs +++ b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs @@ -7,10 +7,11 @@ using System.IO; using System.Linq; using Xunit; +using Xunit.Abstractions; namespace BenchmarkDotNet.Exporters.Plotting.Tests { - public class ScottPlotExporterTests + public class ScottPlotExporterTests(ITestOutputHelper output) { public static TheoryData GetGroupBenchmarkTypes() { @@ -32,6 +33,10 @@ public void BarPlots(Type benchmarkType) var filePaths = exporter.ExportToFiles(summary, logger).ToList(); Assert.NotEmpty(filePaths); Assert.All(filePaths, f => File.Exists(f)); + + foreach (string filePath in filePaths) + logger.WriteLine($"* {filePath}"); + output.WriteLine(logger.GetLog()); } [SuppressMessage("ReSharper", "InconsistentNaming")] @@ -233,6 +238,7 @@ public class Escape_ParamsAndArguments [Arguments('\t')] [Arguments('\n')] [Benchmark] public void Foo(char charArg) { } + [Benchmark] public void Bar() { } } } From 15200d46a1395ef6e69c39c6f3371ab0e0d96e5c Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Sat, 20 Jul 2024 10:13:52 +0200 Subject: [PATCH 13/57] [build] Add BenchmarkDotNet.Exporters.Plotting.Tests to unit-tests --- .../BenchmarkDotNet.Build/Folder.DotSettings | 3 ++ .../Runners/UnitTestRunner.cs | 46 +++++++++---------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/build/BenchmarkDotNet.Build/Folder.DotSettings b/build/BenchmarkDotNet.Build/Folder.DotSettings index 53109cf04e..539b6fe39e 100644 --- a/build/BenchmarkDotNet.Build/Folder.DotSettings +++ b/build/BenchmarkDotNet.Build/Folder.DotSettings @@ -1,4 +1,7 @@  <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + True True \ No newline at end of file diff --git a/build/BenchmarkDotNet.Build/Runners/UnitTestRunner.cs b/build/BenchmarkDotNet.Build/Runners/UnitTestRunner.cs index c759ff1f9f..9b10d54ea4 100644 --- a/build/BenchmarkDotNet.Build/Runners/UnitTestRunner.cs +++ b/build/BenchmarkDotNet.Build/Runners/UnitTestRunner.cs @@ -7,28 +7,25 @@ namespace BenchmarkDotNet.Build.Runners; -public class UnitTestRunner +public class UnitTestRunner(BuildContext context) { - private readonly BuildContext context; + private FilePath UnitTestsProjectFile { get; } = context.RootDirectory + .Combine("tests") + .Combine("BenchmarkDotNet.Tests") + .CombineWithFilePath("BenchmarkDotNet.Tests.csproj"); - private FilePath UnitTestsProjectFile { get; } - private FilePath IntegrationTestsProjectFile { get; } - private DirectoryPath TestOutputDirectory { get; } + private FilePath ExporterTestsProjectFile { get; } = context.RootDirectory + .Combine("tests") + .Combine("BenchmarkDotNet.Exporters.Plotting.Tests") + .CombineWithFilePath("BenchmarkDotNet.Exporters.Plotting.Tests.csproj"); - public UnitTestRunner(BuildContext context) - { - this.context = context; - UnitTestsProjectFile = context.RootDirectory - .Combine("tests") - .Combine("BenchmarkDotNet.Tests") - .CombineWithFilePath("BenchmarkDotNet.Tests.csproj"); - IntegrationTestsProjectFile = context.RootDirectory - .Combine("tests") - .Combine("BenchmarkDotNet.IntegrationTests") - .CombineWithFilePath("BenchmarkDotNet.IntegrationTests.csproj"); - TestOutputDirectory = context.RootDirectory - .Combine("TestResults"); - } + private FilePath IntegrationTestsProjectFile { get; } = context.RootDirectory + .Combine("tests") + .Combine("BenchmarkDotNet.IntegrationTests") + .CombineWithFilePath("BenchmarkDotNet.IntegrationTests.csproj"); + + private DirectoryPath TestOutputDirectory { get; } = context.RootDirectory + .Combine("TestResults"); private DotNetTestSettings GetTestSettingsParameters(FilePath logFile, string tfm) { @@ -58,14 +55,15 @@ private void RunTests(FilePath projectFile, string alias, string tfm) context.DotNetTest(projectFile.FullPath, settings); } - private void RunUnitTests(string tfm) => RunTests(UnitTestsProjectFile, "unit", tfm); + private void RunUnitTests(string tfm) + { + RunTests(UnitTestsProjectFile, "unit", tfm); + RunTests(ExporterTestsProjectFile, "exporters", tfm); + } public void RunUnitTests() { - var targetFrameworks = context.IsRunningOnWindows() - ? new[] { "net462", "net8.0" } - : new[] { "net8.0" }; - + string[] targetFrameworks = context.IsRunningOnWindows() ? ["net462", "net8.0"] : ["net8.0"]; foreach (var targetFramework in targetFrameworks) RunUnitTests(targetFramework); } From 0275649d350bcdc6953215598eca775b4882ece5 Mon Sep 17 00:00:00 2001 From: Tim Cassell <35501420+timcassell@users.noreply.github.com> Date: Sat, 20 Jul 2024 04:16:11 -0400 Subject: [PATCH 14/57] =?UTF-8?q?=EF=BB=BFFixed=20crash=20from=20TaskbarPr?= =?UTF-8?q?ogress=20when=20BuiltInComInteropSupport=20is=20disabled.=20(#2?= =?UTF-8?q?255)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed terminal sequence printed to console in legacy console. Include progress in error and warning states. --- .../Helpers/Taskbar/TaskbarProgress.cs | 197 ------------- .../Helpers/TaskbarProgress.cs | 258 ++++++++++++++++++ .../Running/BenchmarkRunnerClean.cs | 3 +- 3 files changed, 259 insertions(+), 199 deletions(-) delete mode 100644 src/BenchmarkDotNet/Helpers/Taskbar/TaskbarProgress.cs create mode 100644 src/BenchmarkDotNet/Helpers/TaskbarProgress.cs diff --git a/src/BenchmarkDotNet/Helpers/Taskbar/TaskbarProgress.cs b/src/BenchmarkDotNet/Helpers/Taskbar/TaskbarProgress.cs deleted file mode 100644 index 19ad5f6ffe..0000000000 --- a/src/BenchmarkDotNet/Helpers/Taskbar/TaskbarProgress.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace BenchmarkDotNet.Helpers -{ - internal class TaskbarProgress : IDisposable - { - private static readonly bool OsVersionIsSupported = Portability.RuntimeInformation.IsWindows() - // Must be windows 7 or greater - && Environment.OSVersion.Version >= new Version(6, 1); - - private IntPtr consoleWindowHandle = IntPtr.Zero; - private IntPtr consoleHandle = IntPtr.Zero; - - [DllImport("kernel32.dll")] - private static extern IntPtr GetConsoleWindow(); - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr GetStdHandle(int nStdHandle); - - private const int STD_OUTPUT_HANDLE = -11; - - internal TaskbarProgress() - { - if (OsVersionIsSupported) - { - consoleWindowHandle = GetConsoleWindow(); - consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); - Console.CancelKeyPress += OnConsoleCancelEvent; - } - } - - internal void SetState(TaskbarProgressState state) - { - if (OsVersionIsSupported) - { - TaskbarProgressCom.SetState(consoleWindowHandle, consoleHandle, state); - } - } - - internal void SetProgress(float progressValue) - { - if (OsVersionIsSupported) - { - TaskbarProgressCom.SetValue(consoleWindowHandle, consoleHandle, progressValue); - } - } - - private void OnConsoleCancelEvent(object sender, ConsoleCancelEventArgs e) - { - Dispose(); - } - - public void Dispose() - { - if (OsVersionIsSupported) - { - TaskbarProgressCom.SetState(consoleWindowHandle, consoleHandle, TaskbarProgressState.NoProgress); - consoleWindowHandle = IntPtr.Zero; - consoleHandle = IntPtr.Zero; - Console.CancelKeyPress -= OnConsoleCancelEvent; - } - } - } - - internal enum TaskbarProgressState - { - NoProgress = 0, - Indeterminate = 0x1, - Normal = 0x2, - Error = 0x4, - Paused = 0x8, - Warning = Paused - } - - internal static class TaskbarProgressCom - { - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModes lpMode); - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool SetConsoleMode(IntPtr hConsoleHandle, ConsoleModes dwMode); - - [Flags] - private enum ConsoleModes : uint - { - ENABLE_PROCESSED_INPUT = 0x0001, - ENABLE_LINE_INPUT = 0x0002, - ENABLE_ECHO_INPUT = 0x0004, - ENABLE_WINDOW_INPUT = 0x0008, - ENABLE_MOUSE_INPUT = 0x0010, - ENABLE_INSERT_MODE = 0x0020, - ENABLE_QUICK_EDIT_MODE = 0x0040, - ENABLE_EXTENDED_FLAGS = 0x0080, - ENABLE_AUTO_POSITION = 0x0100, - - ENABLE_PROCESSED_OUTPUT = 0x0001, - ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002, - ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004, - DISABLE_NEWLINE_AUTO_RETURN = 0x0008, - ENABLE_LVB_GRID_WORLDWIDE = 0x0010 - } - - [ComImport] - [Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - private interface ITaskbarList3 - { - // ITaskbarList - [PreserveSig] - void HrInit(); - [PreserveSig] - void AddTab(IntPtr hwnd); - [PreserveSig] - void DeleteTab(IntPtr hwnd); - [PreserveSig] - void ActivateTab(IntPtr hwnd); - [PreserveSig] - void SetActiveAlt(IntPtr hwnd); - - // ITaskbarList2 - [PreserveSig] - void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); - - // ITaskbarList3 - [PreserveSig] - void SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal); - [PreserveSig] - void SetProgressState(IntPtr hwnd, TaskbarProgressState state); - } - - [Guid("56FDF344-FD6D-11d0-958A-006097C9A090")] - [ClassInterface(ClassInterfaceType.None)] - [ComImport] - private class TaskbarInstance - { - } - - private static readonly ITaskbarList3 s_taskbarInstance = (ITaskbarList3) new TaskbarInstance(); - - internal static void SetState(IntPtr consoleWindowHandle, IntPtr consoleHandle, TaskbarProgressState taskbarState) - { - if (consoleWindowHandle != IntPtr.Zero) - { - s_taskbarInstance.SetProgressState(consoleWindowHandle, taskbarState); - } - - if (consoleHandle != IntPtr.Zero) - { - // Write progress state to console for Windows Terminal (https://github.com/microsoft/terminal/issues/6700). - GetConsoleMode(consoleHandle, out ConsoleModes previousConsoleMode); - SetConsoleMode(consoleHandle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT); - switch (taskbarState) - { - case TaskbarProgressState.NoProgress: - Console.Write("\x1b]9;4;0;0\x1b\\"); - break; - case TaskbarProgressState.Indeterminate: - Console.Write("\x1b]9;4;3;0\x1b\\"); - break; - case TaskbarProgressState.Normal: - // Do nothing, this is set automatically when SetValue is called (and WT has no documented way to set this). - break; - case TaskbarProgressState.Error: - Console.Write("\x1b]9;4;2;0\x1b\\"); - break; - case TaskbarProgressState.Warning: - Console.Write("\x1b]9;4;4;0\x1b\\"); - break; - } - SetConsoleMode(consoleHandle, previousConsoleMode); - } - } - - internal static void SetValue(IntPtr consoleWindowHandle, IntPtr consoleHandle, float progressValue) - { - bool isValidRange = progressValue >= 0 & progressValue <= 1; - if (!isValidRange) - { - throw new ArgumentOutOfRangeException(nameof(progressValue), "progressValue must be between 0 and 1 inclusive."); - } - uint value = (uint) (progressValue * 100); - - if (consoleWindowHandle != IntPtr.Zero) - { - s_taskbarInstance.SetProgressValue(consoleWindowHandle, value, 100); - } - - if (consoleHandle != IntPtr.Zero) - { - // Write progress sequence to console for Windows Terminal (https://github.com/microsoft/terminal/discussions/14268). - GetConsoleMode(consoleHandle, out ConsoleModes previousConsoleMode); - SetConsoleMode(consoleHandle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT); - Console.Write($"\x1b]9;4;1;{value}\x1b\\"); - SetConsoleMode(consoleHandle, previousConsoleMode); - } - } - } -} diff --git a/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs b/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs new file mode 100644 index 0000000000..f66e21cdf6 --- /dev/null +++ b/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs @@ -0,0 +1,258 @@ +using System; +using System.Runtime.InteropServices; + +namespace BenchmarkDotNet.Helpers +{ + internal enum TaskbarProgressState + { + NoProgress = 0, + Indeterminate = 0x1, + Normal = 0x2, + Error = 0x4, + Paused = 0x8, + Warning = Paused + } + + internal class TaskbarProgress : IDisposable + { + private static readonly bool OsVersionIsSupported = Portability.RuntimeInformation.IsWindows() + // Must be windows 7 or greater + && Environment.OSVersion.Version >= new Version(6, 1); + + private Com? com; + private Terminal? terminal; + + private bool IsEnabled => com != null || terminal != null; + + internal TaskbarProgress(TaskbarProgressState initialTaskbarState) + { + if (OsVersionIsSupported) + { + com = Com.MaybeCreateInstanceAndSetInitialState(initialTaskbarState); + terminal = Terminal.MaybeCreateInstanceAndSetInitialState(initialTaskbarState); + if (IsEnabled) + { + Console.CancelKeyPress += OnConsoleCancelEvent; + } + } + } + + internal void SetState(TaskbarProgressState state) + { + com?.SetState(state); + terminal?.SetState(state); + } + + internal void SetProgress(float progressValue) + { + bool isValidRange = progressValue >= 0 & progressValue <= 1; + if (!isValidRange) + { + throw new ArgumentOutOfRangeException(nameof(progressValue), "progressValue must be between 0 and 1 inclusive."); + } + uint value = (uint) (progressValue * 100); + com?.SetValue(value); + terminal?.SetValue(value); + } + + private void OnConsoleCancelEvent(object sender, ConsoleCancelEventArgs e) + => Dispose(); + + public void Dispose() + { + if (IsEnabled) + { + SetState(TaskbarProgressState.NoProgress); + com = null; + terminal = null; + Console.CancelKeyPress -= OnConsoleCancelEvent; + } + } + + private sealed class Com + { + [ComImport] + [Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface ITaskbarList3 + { + // ITaskbarList + [PreserveSig] + void HrInit(); + [PreserveSig] + void AddTab(IntPtr hwnd); + [PreserveSig] + void DeleteTab(IntPtr hwnd); + [PreserveSig] + void ActivateTab(IntPtr hwnd); + [PreserveSig] + void SetActiveAlt(IntPtr hwnd); + + // ITaskbarList2 + [PreserveSig] + void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); + + // ITaskbarList3 + [PreserveSig] + void SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal); + [PreserveSig] + void SetProgressState(IntPtr hwnd, TaskbarProgressState state); + } + + [Guid("56FDF344-FD6D-11d0-958A-006097C9A090")] + [ClassInterface(ClassInterfaceType.None)] + [ComImport] + private class TaskbarInstance { } + + [DllImport("kernel32.dll")] + private static extern IntPtr GetConsoleWindow(); + + private readonly ITaskbarList3 taskbarInstance; + private readonly IntPtr consoleWindowHandle; + + private Com(IntPtr handle) + { + taskbarInstance = (ITaskbarList3) new TaskbarInstance(); + consoleWindowHandle = handle; + } + + internal static Com? MaybeCreateInstanceAndSetInitialState(TaskbarProgressState initialTaskbarState) + { + try + { + IntPtr handle = GetConsoleWindow(); + if (handle == IntPtr.Zero) + { + return null; + } + var com = new Com(handle); + com.SetState(initialTaskbarState); + return com; + } + // COM may be disabled, in which case this will throw (#2253). + // It could be NotSupportedException or COMException, we just catch all. + catch + { + return null; + } + } + + internal void SetState(TaskbarProgressState taskbarState) + => taskbarInstance.SetProgressState(consoleWindowHandle, taskbarState); + + /// + /// Sets the progress value out of 100. + /// + internal void SetValue(uint progressValue) + => taskbarInstance.SetProgressValue(consoleWindowHandle, progressValue, 100); + } + + private sealed class Terminal + { + [Flags] + private enum ConsoleModes : uint + { + ENABLE_PROCESSED_INPUT = 0x0001, + ENABLE_LINE_INPUT = 0x0002, + ENABLE_ECHO_INPUT = 0x0004, + ENABLE_WINDOW_INPUT = 0x0008, + ENABLE_MOUSE_INPUT = 0x0010, + ENABLE_INSERT_MODE = 0x0020, + ENABLE_QUICK_EDIT_MODE = 0x0040, + ENABLE_EXTENDED_FLAGS = 0x0080, + ENABLE_AUTO_POSITION = 0x0100, + + ENABLE_PROCESSED_OUTPUT = 0x0001, + ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002, + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004, + DISABLE_NEWLINE_AUTO_RETURN = 0x0008, + ENABLE_LVB_GRID_WORLDWIDE = 0x0010 + } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModes lpMode); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetConsoleMode(IntPtr hConsoleHandle, ConsoleModes dwMode); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr GetStdHandle(int nStdHandle); + private const int STD_OUTPUT_HANDLE = -11; + + private readonly IntPtr consoleHandle; + private uint currentProgress; + + private Terminal(IntPtr handle) + => consoleHandle = handle; + + internal static Terminal? MaybeCreateInstanceAndSetInitialState(TaskbarProgressState initialTaskbarState) + { + IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE); + if (handle == IntPtr.Zero) + { + return null; + } + if (!GetConsoleMode(handle, out ConsoleModes previousConsoleMode) + || !SetConsoleMode(handle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT)) + { + // If we failed to set virtual terminal processing mode, it is likely due to an older Windows version that does not support it, + // or legacy console. In either case the TaskbarProgressCom will take care of the progress. See https://stackoverflow.com/a/44574463/5703407. + // If we try to write without VT mode, the sequence will be printed for the user to see, which clutters the output. + return null; + } + var terminal = new Terminal(handle); + terminal.WriteStateSequence(initialTaskbarState); + SetConsoleMode(handle, previousConsoleMode); + return terminal; + } + + internal void SetState(TaskbarProgressState taskbarState) + { + GetConsoleMode(consoleHandle, out ConsoleModes previousConsoleMode); + SetConsoleMode(consoleHandle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT); + WriteStateSequence(taskbarState); + SetConsoleMode(consoleHandle, previousConsoleMode); + } + + private void WriteStateSequence(TaskbarProgressState taskbarState) + { + // Write progress state to console for Windows Terminal (https://github.com/microsoft/terminal/issues/6700). + switch (taskbarState) + { + case TaskbarProgressState.NoProgress: + currentProgress = 100; + Console.Write("\x1b]9;4;0;0\x1b\\"); + break; + case TaskbarProgressState.Indeterminate: + currentProgress = 100; + Console.Write("\x1b]9;4;3;0\x1b\\"); + break; + case TaskbarProgressState.Normal: + // Normal state is set when progress is set. + WriteProgressSequence(currentProgress); + break; + case TaskbarProgressState.Error: + Console.Write($"\x1b]9;4;2;{currentProgress}\x1b\\"); + break; + case TaskbarProgressState.Warning: + Console.Write($"\x1b]9;4;4;{currentProgress}\x1b\\"); + break; + } + } + + /// + /// Sets the progress value out of 100. + /// + internal void SetValue(uint progressValue) + { + currentProgress = progressValue; + // Write progress sequence to console for Windows Terminal (https://github.com/microsoft/terminal/discussions/14268). + GetConsoleMode(consoleHandle, out ConsoleModes previousConsoleMode); + SetConsoleMode(consoleHandle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT); + WriteProgressSequence(progressValue); + SetConsoleMode(consoleHandle, previousConsoleMode); + } + + private static void WriteProgressSequence(uint progressValue) + => Console.Write($"\x1b]9;4;1;{progressValue}\x1b\\"); + } + } +} diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs index 7ea1932794..4a1e85eee9 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs @@ -37,8 +37,7 @@ internal static class BenchmarkRunnerClean internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos) { - using var taskbarProgress = new TaskbarProgress(); - taskbarProgress.SetState(TaskbarProgressState.Indeterminate); + using var taskbarProgress = new TaskbarProgress(TaskbarProgressState.Indeterminate); var resolver = DefaultResolver; var artifactsToCleanup = new List(); From bf0a49d1f5756cd2f7cb1da56974a7ee6a5a6fdf Mon Sep 17 00:00:00 2001 From: workgroupengineering Date: Mon, 5 Aug 2024 19:25:19 +0200 Subject: [PATCH 15/57] fix(CI): Deprecation issues (#2605) * fix(CI): Deprecation issues * fix: missing --- .github/workflows/run-tests.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 5243ee47be..698b429514 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -17,7 +17,7 @@ jobs: - name: Disable Windows Defender run: Set-MpPreference -DisableRealtimeMonitoring $true shell: powershell - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run task 'build' shell: cmd run: ./build.cmd build @@ -37,7 +37,7 @@ jobs: - name: Disable Windows Defender run: Set-MpPreference -DisableRealtimeMonitoring $true shell: powershell - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run task 'build' shell: cmd run: ./build.cmd build @@ -45,7 +45,7 @@ jobs: shell: cmd run: ./build.cmd in-tests-full -e - name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: test-windows-full-trx @@ -54,7 +54,7 @@ jobs: test-linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Clang uses: egor-tensin/setup-clang@v1 with: @@ -77,7 +77,7 @@ jobs: - name: Run task 'in-tests-core' run: ./build.cmd in-tests-core -e - name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: test-linux-trx @@ -86,7 +86,7 @@ jobs: test-macos: runs-on: macos-13 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up node uses: actions/setup-node@v4 with: @@ -102,7 +102,7 @@ jobs: - name: Run task 'in-tests-core' run: ./build.cmd in-tests-core -e - name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: test-macos-trx @@ -111,7 +111,7 @@ jobs: test-pack: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Clang uses: egor-tensin/setup-clang@v1 with: @@ -125,8 +125,8 @@ jobs: spellcheck-docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 name: Setup node with: node-version: "18" From 3d34edb219b84a68a377cb38b833dd30241fd5c8 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Tue, 6 Aug 2024 14:52:51 +0200 Subject: [PATCH 16/57] Bump JetBrains.Profiler.SelfApi: 2.5.2->2.5.9 Support latest dotTrace and dotMemory --- .../BenchmarkDotNet.Diagnostics.dotMemory.csproj | 2 +- src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs | 2 +- .../BenchmarkDotNet.Diagnostics.dotTrace.csproj | 2 +- src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj index 23261abb24..414d927c0d 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs index 1e640d6708..c28e08fdb2 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs @@ -30,7 +30,7 @@ public void Init() { logger.WriteLineInfo("Ensuring that dotMemory prerequisite is installed..."); var progress = new Progress(logger, "Installing DotMemory"); - DotMemory.EnsurePrerequisiteAsync(progress, nugetUrl, nugetApi, downloadTo).Wait(); + DotMemory.InitAsync(progress, nugetUrl, nugetApi, downloadTo).Wait(); logger.WriteLineInfo("dotMemory prerequisite is installed"); logger.WriteLineInfo($"dotMemory runner path: {GetRunnerPath()}"); } diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj b/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj index aa8c7af1e4..4ea24eeb04 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs index f2f07625fa..2fb36e9c66 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs @@ -29,7 +29,7 @@ public void Init(DiagnoserActionParameters parameters) { logger.WriteLineInfo("Ensuring that dotTrace prerequisite is installed..."); var progress = new Progress(logger, "Installing DotTrace"); - DotTrace.EnsurePrerequisiteAsync(progress, nugetUrl, nugetApi, downloadTo).Wait(); + DotTrace.InitAsync(progress, nugetUrl, nugetApi, downloadTo).Wait(); logger.WriteLineInfo("dotTrace prerequisite is installed"); logger.WriteLineInfo($"dotTrace runner path: {GetRunnerPath()}"); } From 23e6c523cfe638d53508d6ca8212ca23501049ce Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Tue, 6 Aug 2024 15:38:44 +0200 Subject: [PATCH 17/57] Fix InvalidOperationException in DotMemoryDiagnoser If a DotMemoryDiagnoser instance is reused and previous AfterActualRun failed, we shouldn't throw on the second attempt to init the tool --- .../DotMemoryDiagnoser.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs index 46789b518c..24e62feb31 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs @@ -45,10 +45,11 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) switch (signal) { case HostSignal.BeforeAnythingElse: - if (tool is not null) - throw new InvalidOperationException("DotMemory tool is already initialized"); - tool = new DotMemoryTool(logger, nugetUrl, downloadTo: toolsDownloadFolder); - tool.Init(); + if (tool is null) + { + tool = new DotMemoryTool(logger, nugetUrl, downloadTo: toolsDownloadFolder); + tool.Init(); + } break; case HostSignal.BeforeActualRun: if (tool is null) From b3fbe7c489c2b6e354f736ba4c0854e4f1daacfb Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Tue, 6 Aug 2024 11:59:35 +0200 Subject: [PATCH 18/57] Set next BenchmarkDotNet version: 0.14.0 --- build/common.props | 2 +- build/versions.txt | 2 +- .../.template.config/template.json | 2 +- .../.template.config/template.json | 2 +- .../.template.config/template.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/common.props b/build/common.props index 23e4dfd8e8..e74a0b8680 100644 --- a/build/common.props +++ b/build/common.props @@ -40,7 +40,7 @@ - 0.13.13 + 0.14.0 diff --git a/build/versions.txt b/build/versions.txt index 6b64259d94..584dcda7c7 100644 --- a/build/versions.txt +++ b/build/versions.txt @@ -57,4 +57,4 @@ 0.13.10 0.13.11 0.13.12 -0.13.13 \ No newline at end of file +0.14.0 \ No newline at end of file diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json index 3bd358cbaf..05090c29d1 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json @@ -131,7 +131,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.13.13", + "defaultValue": "0.14.0", "replaces": "$(BenchmarkDotNetVersion)" } }, diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json index ab866f143e..b0731534ea 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json @@ -131,7 +131,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.13.13", + "defaultValue": "0.14.0", "replaces": "$(BenchmarkDotNetVersion)" } }, diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json index aeb8928bd7..bf4fc78b1a 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json @@ -131,7 +131,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.13.13", + "defaultValue": "0.14.0", "replaces": "$(BenchmarkDotNetVersion)" } }, From 17cf3b0a71b7fa41e83e2db16307219420f4a4f8 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Tue, 6 Aug 2024 12:23:34 +0200 Subject: [PATCH 19/57] [docs] Prepare v0.14.0 changelog --- README.md | 2 +- docs/_changelog/footer/v0.14.0.md | 14 ++++++++++++++ docs/_changelog/header/v0.14.0.md | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 docs/_changelog/footer/v0.14.0.md diff --git a/README.md b/README.md index c18916d2d2..3022e0ebb3 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ It's no harder than writing unit tests! Under the hood, it performs a lot of [magic](#automation) that guarantees [reliable and precise](#reliability) results thanks to the [perfolizer](https://github.com/AndreyAkinshin/perfolizer) statistical engine. BenchmarkDotNet protects you from popular benchmarking mistakes and warns you if something is wrong with your benchmark design or obtained measurements. The results are presented in a [user-friendly](#friendliness) form that highlights all the important facts about your experiment. -BenchmarkDotNet is already adopted by [19100+ GitHub projects](https://github.com/dotnet/BenchmarkDotNet/network/dependents) including +BenchmarkDotNet is already adopted by [22000+ GitHub projects](https://github.com/dotnet/BenchmarkDotNet/network/dependents) including [.NET Runtime](https://github.com/dotnet/runtime), [.NET Compiler](https://github.com/dotnet/roslyn), [.NET Performance](https://github.com/dotnet/performance), diff --git a/docs/_changelog/footer/v0.14.0.md b/docs/_changelog/footer/v0.14.0.md new file mode 100644 index 0000000000..cf7b8d6984 --- /dev/null +++ b/docs/_changelog/footer/v0.14.0.md @@ -0,0 +1,14 @@ +_Date: TBA_ + +_Milestone: [v0.14.0](https://github.com/dotnet/BenchmarkDotNet/issues?q=milestone%3Av0.14.0)_ +([List of commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.12...v0.14.0)) + +_NuGet Packages:_ +* https://www.nuget.org/packages/BenchmarkDotNet/0.14.0 +* https://www.nuget.org/packages/BenchmarkDotNet.Annotations/0.14.0 +* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.dotMemory/0.14.0 +* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.dotTrace/0.14.0 +* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.Windows/0.14.0 +* https://www.nuget.org/packages/BenchmarkDotNet.Exporters.Plotting/0.14.0 +* https://www.nuget.org/packages/BenchmarkDotNet.Templates/0.14.0 +* https://www.nuget.org/packages/BenchmarkDotNet.TestAdapter/0.14.0 diff --git a/docs/_changelog/header/v0.14.0.md b/docs/_changelog/header/v0.14.0.md index 260ba4d47f..1c2f6693ce 100644 --- a/docs/_changelog/header/v0.14.0.md +++ b/docs/_changelog/header/v0.14.0.md @@ -1,5 +1,8 @@ ## Highlights +* Introduce `BenchmarkDotNet.Diagnostics.dotMemory` [#2549](https://github.com/dotnet/BenchmarkDotNet/pull/2549): memory allocation profile of your benchmarks using [dotMemory](https://www.jetbrains.com/dotmemory/), see @BenchmarkDotNet.Samples.IntroDotMemoryDiagnoser +* Introduce `BenchmarkDotNet.Exporters.Plotting` [#2560](https://github.com/dotnet/BenchmarkDotNet/pull/2560): plotting via [ScottPlot](https://scottplot.net/) (initial version) +* Multiple bugfixes * The default build toolchains have been updated to pass `IntermediateOutputPath`, `OutputPath`, and `OutDir` properties to the `dotnet build` command. This change forces all build outputs to be placed in a new directory generated by BenchmarkDotNet, and fixes many issues that have been reported with builds. You can also access these paths in your own `.csproj` and `.props` from those properties if you need to copy custom files to the output. ## Bug fixes From cf882d378d51a6998aad43ca9caa29c19d122b87 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Tue, 6 Aug 2024 14:14:45 +0200 Subject: [PATCH 20/57] Add macOS Sequoia in OsBrandStringHelper --- src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs b/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs index d4369120eb..045c1f7543 100644 --- a/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs +++ b/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs @@ -236,6 +236,7 @@ private MacOSXVersion(int darwinVersion, string codeName) new MacOSXVersion(21, "Monterey"), new MacOSXVersion(22, "Ventura"), new MacOSXVersion(23, "Sonoma"), + new MacOSXVersion(24, "Sequoia"), }; public static string? ResolveCodeName(string kernelVersion) From a739e2cc9a9626b17a85e0fbe7d016282948cad1 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Tue, 6 Aug 2024 16:44:48 +0200 Subject: [PATCH 21/57] Set next BenchmarkDotNet version: 0.14.1 --- build/common.props | 2 +- build/versions.txt | 3 ++- docs/_changelog/footer/v0.14.0.md | 2 +- .../.template.config/template.json | 2 +- .../.template.config/template.json | 2 +- .../.template.config/template.json | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build/common.props b/build/common.props index e74a0b8680..d59d37e8a1 100644 --- a/build/common.props +++ b/build/common.props @@ -40,7 +40,7 @@ - 0.14.0 + 0.14.1 diff --git a/build/versions.txt b/build/versions.txt index 584dcda7c7..934332a0df 100644 --- a/build/versions.txt +++ b/build/versions.txt @@ -57,4 +57,5 @@ 0.13.10 0.13.11 0.13.12 -0.14.0 \ No newline at end of file +0.14.0 +0.14.1 \ No newline at end of file diff --git a/docs/_changelog/footer/v0.14.0.md b/docs/_changelog/footer/v0.14.0.md index cf7b8d6984..47d421f49d 100644 --- a/docs/_changelog/footer/v0.14.0.md +++ b/docs/_changelog/footer/v0.14.0.md @@ -1,4 +1,4 @@ -_Date: TBA_ +_Date: August 06, 2024_ _Milestone: [v0.14.0](https://github.com/dotnet/BenchmarkDotNet/issues?q=milestone%3Av0.14.0)_ ([List of commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.12...v0.14.0)) diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json index 05090c29d1..12a8c08785 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json @@ -131,7 +131,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.14.0", + "defaultValue": "0.14.1", "replaces": "$(BenchmarkDotNetVersion)" } }, diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json index b0731534ea..ebaaaa1073 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json @@ -131,7 +131,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.14.0", + "defaultValue": "0.14.1", "replaces": "$(BenchmarkDotNetVersion)" } }, diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json index bf4fc78b1a..bc3989e97d 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json @@ -131,7 +131,7 @@ "type": "parameter", "datatype": "string", "description": "Version of BenchmarkDotNet that will be referenced.", - "defaultValue": "0.14.0", + "defaultValue": "0.14.1", "replaces": "$(BenchmarkDotNetVersion)" } }, From c8105aab564dac2b5c132b6f1d4bf956ee52198c Mon Sep 17 00:00:00 2001 From: Korexio Date: Thu, 22 Aug 2024 20:09:16 +0200 Subject: [PATCH 22/57] Fix deadlock in GetDotNetSdkVersion (#2622) * Fix deadlock in GetDotNetSdkVersion * Remove newline at the end of file --- .../Toolchains/DotNetCli/DotNetCliCommandExecutor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs index 4348685369..10ce4ef15d 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs @@ -56,7 +56,7 @@ public static DotNetCliCommandResult Execute(DotNetCliCommand parameters) internal static string? GetDotNetSdkVersion() { - using (var process = new Process { StartInfo = BuildStartInfo(customDotNetCliPath: null, workingDirectory: string.Empty, arguments: "--version") }) + using (var process = new Process { StartInfo = BuildStartInfo(customDotNetCliPath: null, workingDirectory: string.Empty, arguments: "--version", redirectStandardError: false) }) using (new ConsoleExitHandler(process, NullLogger.Instance)) { try From 29a0ebb25865fbe8ad19923545a409e4bc552e66 Mon Sep 17 00:00:00 2001 From: Steve Dunn Date: Sun, 25 Aug 2024 10:43:25 +0100 Subject: [PATCH 23/57] Update good-practices.md (#2618) --- docs/articles/guides/good-practices.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/guides/good-practices.md b/docs/articles/guides/good-practices.md index 854ead5679..da4329de2b 100644 --- a/docs/articles/guides/good-practices.md +++ b/docs/articles/guides/good-practices.md @@ -4,8 +4,8 @@ Never use the Debug build for benchmarking. *Never*. The debug version of the target method can run 10–100 times slower. The release mode means that you should have `true` in your csproj file -or use [/optimize](https://learn.microsoft.com/dotnet/csharp/language-reference/compiler-options/) for `csc`. Also your never -should use an attached debugger (e.g. Visual Studio or WinDbg) during the benchmarking. The best way is +or use [/optimize](https://learn.microsoft.com/dotnet/csharp/language-reference/compiler-options/) for `csc`. Also, never +use an attached debugger (e.g. Visual Studio or WinDbg) during the benchmarking. The best way is build our benchmark in the Release mode and run it from the command line. ## Try different environments From 3927ce7b5b77f76fb940fb7e801f189d280c768c Mon Sep 17 00:00:00 2001 From: Andrew Chisholm Date: Sun, 25 Aug 2024 20:51:46 +1000 Subject: [PATCH 24/57] Fix #2611 - Add Iteration mode and stage filtering to ScottPlotExporter (#2612) * Fix #2611 - Add Iteration mode and stage filtering * Fix #2611 - Also choose time scale based on results --- src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs b/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs index cfb7a735b7..33752cce9b 100644 --- a/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs +++ b/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using BenchmarkDotNet.Engines; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Properties; using BenchmarkDotNet.Reports; @@ -83,7 +84,8 @@ public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) var version = BenchmarkDotNetInfo.Instance.BrandTitle; var annotations = GetAnnotations(version); - var (timeUnit, timeScale) = GetTimeUnit(summary.Reports.SelectMany(m => m.AllMeasurements)); + var (timeUnit, timeScale) = GetTimeUnit(summary.Reports + .SelectMany(m => m.AllMeasurements.Where(m => m.Is(IterationMode.Workload, IterationStage.Result)))); foreach (var benchmark in summary.Reports.GroupBy(r => r.BenchmarkCase.Descriptor.Type.Name)) { @@ -93,6 +95,7 @@ public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) var timeStats = from report in benchmark let jobId = report.BenchmarkCase.DisplayInfo.Replace(report.BenchmarkCase.Descriptor.DisplayInfo + ": ", string.Empty) from measurement in report.AllMeasurements + where measurement.Is(IterationMode.Workload, IterationStage.Result) let measurementValue = measurement.Nanoseconds / measurement.Operations group measurementValue / timeScale by (Target: report.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo, JobId: jobId) into g select (g.Key.Target, g.Key.JobId, Mean: g.Average(), StdError: StandardError(g.ToList())); From cd9e4e93c3945cffcd9aeb59c01b2dfaee072292 Mon Sep 17 00:00:00 2001 From: Andrew Chisholm Date: Tue, 13 Aug 2024 00:20:24 +1000 Subject: [PATCH 25/57] Feature - Added Box plots Also improved readability by setting most font sizes to 14 by default (customisable) --- .../ScottPlotExporter.cs | 193 +++++++++++++++++- .../ScottPlotExporterTests.cs | 53 ++++- .../Mocks/MockFactory.cs | 55 +++++ 3 files changed, 293 insertions(+), 8 deletions(-) diff --git a/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs b/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs index 33752cce9b..dba73d5f8d 100644 --- a/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs +++ b/src/BenchmarkDotNet.Exporters.Plotting/ScottPlotExporter.cs @@ -36,6 +36,7 @@ public ScottPlotExporter(int width = 1920, int height = 1080) this.Width = width; this.Height = height; this.IncludeBarPlot = true; + this.IncludeBoxPlot = true; this.RotateLabels = true; } @@ -49,6 +50,16 @@ public ScottPlotExporter(int width = 1920, int height = 1080) /// public int Height { get; set; } + /// + /// Gets or sets the common font size for ticks, labels etc. (defaults to 14). + /// + public int FontSize { get; set; } = 14; + + /// + /// Gets or sets the font size for the chart title. (defaults to 28). + /// + public int TitleFontSize { get; set; } = 28; + /// /// Gets or sets a value indicating whether labels for Plot X-axis should be rotated. /// This allows for longer labels at the expense of chart height. @@ -61,6 +72,12 @@ public ScottPlotExporter(int width = 1920, int height = 1080) /// public bool IncludeBarPlot { get; set; } + /// + /// Gets or sets a value indicating whether a box plot or whisker plot for time-per-op + /// measurement values should be exported. + /// + public bool IncludeBoxPlot { get; set; } + /// /// Not supported. /// @@ -98,7 +115,7 @@ from measurement in report.AllMeasurements where measurement.Is(IterationMode.Workload, IterationStage.Result) let measurementValue = measurement.Nanoseconds / measurement.Operations group measurementValue / timeScale by (Target: report.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo, JobId: jobId) into g - select (g.Key.Target, g.Key.JobId, Mean: g.Average(), StdError: StandardError(g.ToList())); + select new ChartStats(g.Key.Target, g.Key.JobId, g.ToList()); if (this.IncludeBarPlot) { @@ -112,8 +129,19 @@ where measurement.Is(IterationMode.Workload, IterationStage.Result) annotations); } + if (this.IncludeBoxPlot) + { + // -boxplot.png + yield return CreateBoxPlot( + $"{title} - {benchmarkName}", + Path.Combine(summary.ResultsDirectoryPath, $"{title}-{benchmarkName}-boxplot.png"), + $"Time ({timeUnit})", + "Target", + timeStats, + annotations); + } + /* TODO: Rest of the RPlotExporter plots. - -boxplot.png --density.png --facetTimeline.png --facetTimelineSmooth.png @@ -161,12 +189,12 @@ private static double StandardError(IReadOnlyList values) return ("ns", 1d); } - private string CreateBarPlot(string title, string fileName, string yLabel, string xLabel, IEnumerable<(string Target, string JobId, double Mean, double StdError)> data, IReadOnlyList annotations) + private string CreateBarPlot(string title, string fileName, string yLabel, string xLabel, IEnumerable data, IReadOnlyList annotations) { Plot plt = new Plot(); - plt.Title(title, 28); - plt.YLabel(yLabel); - plt.XLabel(xLabel); + plt.Title(title, this.TitleFontSize); + plt.YLabel(yLabel, this.FontSize); + plt.XLabel(xLabel, this.FontSize); var palette = new ScottPlot.Palettes.Category10(); @@ -177,6 +205,7 @@ private string CreateBarPlot(string title, string fileName, string yLabel, strin plt.Legend.IsVisible = true; plt.Legend.Location = Alignment.UpperRight; + plt.Legend.Font.Size = this.FontSize; var legend = data.Select(d => d.JobId) .Distinct() .Select((label, index) => new LegendItem() @@ -192,8 +221,11 @@ private string CreateBarPlot(string title, string fileName, string yLabel, strin var ticks = data .Select((d, index) => new Tick(index, d.Target)) .ToArray(); + + plt.Axes.Left.TickLabelStyle.FontSize = this.FontSize; plt.Axes.Bottom.TickGenerator = new ScottPlot.TickGenerators.NumericManual(ticks); plt.Axes.Bottom.MajorTickStyle.Length = 0; + plt.Axes.Bottom.TickLabelStyle.FontSize = this.FontSize; if (this.RotateLabels) { @@ -209,7 +241,7 @@ private string CreateBarPlot(string title, string fileName, string yLabel, strin } // ensure axis panels do not get smaller than the largest label - plt.Axes.Bottom.MinimumSize = largestLabelWidth; + plt.Axes.Bottom.MinimumSize = largestLabelWidth * 2; plt.Axes.Right.MinimumSize = largestLabelWidth; } @@ -232,6 +264,89 @@ private string CreateBarPlot(string title, string fileName, string yLabel, strin return Path.GetFullPath(fileName); } + private string CreateBoxPlot(string title, string fileName, string yLabel, string xLabel, IEnumerable data, IReadOnlyList annotations) + { + Plot plt = new Plot(); + plt.Title(title, this.TitleFontSize); + plt.YLabel(yLabel, this.FontSize); + plt.XLabel(xLabel, this.FontSize); + + var palette = new ScottPlot.Palettes.Category10(); + + var legendPalette = data.Select(d => d.JobId) + .Distinct() + .Select((jobId, index) => (jobId, index)) + .ToDictionary(t => t.jobId, t => palette.GetColor(t.index)); + + plt.Legend.IsVisible = true; + plt.Legend.Location = Alignment.UpperRight; + plt.Legend.Font.Size = this.FontSize; + var legend = data.Select(d => d.JobId) + .Distinct() + .Select((label, index) => new LegendItem() + { + Label = label, + FillColor = legendPalette[label] + }) + .ToList(); + + plt.Legend.ManualItems.AddRange(legend); + + var jobCount = plt.Legend.ManualItems.Count; + var ticks = data + .Select((d, index) => new Tick(index, d.Target)) + .ToArray(); + + plt.Axes.Left.TickLabelStyle.FontSize = this.FontSize; + plt.Axes.Bottom.TickGenerator = new ScottPlot.TickGenerators.NumericManual(ticks); + plt.Axes.Bottom.MajorTickStyle.Length = 0; + plt.Axes.Bottom.TickLabelStyle.FontSize = this.FontSize; + + if (this.RotateLabels) + { + plt.Axes.Bottom.TickLabelStyle.Rotation = 45; + plt.Axes.Bottom.TickLabelStyle.Alignment = Alignment.MiddleLeft; + + // determine the width of the largest tick label + float largestLabelWidth = 0; + foreach (Tick tick in ticks) + { + PixelSize size = plt.Axes.Bottom.TickLabelStyle.Measure(tick.Label); + largestLabelWidth = Math.Max(largestLabelWidth, size.Width); + } + + // ensure axis panels do not get smaller than the largest label + plt.Axes.Bottom.MinimumSize = largestLabelWidth * 2; + plt.Axes.Right.MinimumSize = largestLabelWidth; + } + + int globalIndex = 0; + foreach (var (targetGroup, targetGroupIndex) in data.GroupBy(s => s.Target).Select((targetGroup, index) => (targetGroup, index))) + { + var boxes = targetGroup.Select(job => (job.JobId, Stats: job.CalculateBoxPlotStatistics())).Select((j, jobIndex) => new Box() + { + Position = ticks[globalIndex++].Position, + Fill = new FillStyle() { Color = legendPalette[j.JobId] }, + Stroke = new LineStyle() { Color = Colors.Black }, + BoxMin = j.Stats.Q1, + BoxMax = j.Stats.Q3, + WhiskerMin = j.Stats.Min, + WhiskerMax = j.Stats.Max, + BoxMiddle = j.Stats.Median + }) + .ToList(); + plt.Add.Boxes(boxes); + } + + // Tell the plot to autoscale with a small padding below the boxes. + plt.Axes.Margins(bottom: 0.05, right: .2); + + plt.PlottableList.AddRange(annotations); + + plt.SavePng(fileName, this.Width, this.Height); + return Path.GetFullPath(fileName); + } + /// /// Provides a list of annotations to put over the data area. /// @@ -255,5 +370,69 @@ private IReadOnlyList GetAnnotations(string version) return new[] { versionAnnotation }; } + + private class ChartStats + { + public ChartStats(string Target, string JobId, IReadOnlyList Values) + { + this.Target = Target; + this.JobId = JobId; + this.Values = Values; + } + + public string Target { get; } + + public string JobId { get; } + + public IReadOnlyList Values { get; } + + public double Min => this.Values.DefaultIfEmpty(0d).Min(); + + public double Max => this.Values.DefaultIfEmpty(0d).Max(); + + public double Mean => this.Values.DefaultIfEmpty(0d).Average(); + + public double StdError => StandardError(this.Values); + + + private static (int MidPoint, double Median) CalculateMedian(ReadOnlySpan values) + { + int n = values.Length; + var midPoint = n / 2; + + // Check if count is even, if so use average of the two middle values, + // otherwise take the middle value. + var median = n % 2 == 0 ? (values[midPoint - 1] + values[midPoint]) / 2d : values[midPoint]; + return (midPoint, median); + } + + /// + /// Calculate the mid points. + /// + /// + public (double Min, double Q1, double Median, double Q3, double Max, double[] Outliers) CalculateBoxPlotStatistics() + { + var values = this.Values.ToArray(); + Array.Sort(values); + var s = values.AsSpan(); + var (midPoint, median) = CalculateMedian(s); + + var (q1Index, q1) = midPoint > 0 ? CalculateMedian(s.Slice(0, midPoint)) : (midPoint, median); + var (q3Index, q3) = midPoint + 1 < s.Length ? CalculateMedian(s.Slice(midPoint + 1)) : (midPoint, median); + var iqr = q3 - q1; + var lowerFence = q1 - 1.5d * iqr; + var upperFence = q3 + 1.5d * iqr; + var outliers = values.Where(v => v < lowerFence || v > upperFence).ToArray(); + var nonOutliers = values.Where(v => v >= lowerFence && v <= upperFence).ToArray(); + return ( + nonOutliers.FirstOrDefault(), + q1, + median, + q3, + nonOutliers.LastOrDefault(), + outliers + ); + } + } } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs index 32f69061f6..e717b6b84d 100644 --- a/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs +++ b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/ScottPlotExporterTests.cs @@ -1,6 +1,9 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Tests.Builders; using BenchmarkDotNet.Tests.Mocks; using System; using System.Diagnostics.CodeAnalysis; @@ -28,7 +31,11 @@ public void BarPlots(Type benchmarkType) var logger = new AccumulationLogger(); logger.WriteLine("=== " + benchmarkType.Name + " ==="); - var exporter = new ScottPlotExporter(); + var exporter = new ScottPlotExporter() + { + IncludeBarPlot = true, + IncludeBoxPlot = false, + }; var summary = MockFactory.CreateSummary(benchmarkType); var filePaths = exporter.ExportToFiles(summary, logger).ToList(); Assert.NotEmpty(filePaths); @@ -39,6 +46,50 @@ public void BarPlots(Type benchmarkType) output.WriteLine(logger.GetLog()); } + [Theory] + [MemberData(nameof(GetGroupBenchmarkTypes))] + public void BoxPlots(Type benchmarkType) + { + var logger = new AccumulationLogger(); + logger.WriteLine("=== " + benchmarkType.Name + " ==="); + + var exporter = new ScottPlotExporter() + { + IncludeBarPlot = false, + IncludeBoxPlot = true, + }; + var summary = MockFactory.CreateSummaryWithBiasedDistribution(benchmarkType, 1, 4, 10, 9); + var filePaths = exporter.ExportToFiles(summary, logger).ToList(); + Assert.NotEmpty(filePaths); + Assert.All(filePaths, f => File.Exists(f)); + + foreach (string filePath in filePaths) + logger.WriteLine($"* {filePath}"); + output.WriteLine(logger.GetLog()); + } + + [Theory] + [MemberData(nameof(GetGroupBenchmarkTypes))] + public void BoxPlotsWithOneMeasurement(Type benchmarkType) + { + var logger = new AccumulationLogger(); + logger.WriteLine("=== " + benchmarkType.Name + " ==="); + + var exporter = new ScottPlotExporter() + { + IncludeBarPlot = false, + IncludeBoxPlot = true, + }; + var summary = MockFactory.CreateSummaryWithBiasedDistribution(benchmarkType, 1, 4, 10, 1); + var filePaths = exporter.ExportToFiles(summary, logger).ToList(); + Assert.NotEmpty(filePaths); + Assert.All(filePaths, f => File.Exists(f)); + + foreach (string filePath in filePaths) + logger.WriteLine($"* {filePath}"); + output.WriteLine(logger.GetLog()); + } + [SuppressMessage("ReSharper", "InconsistentNaming")] public static class BaselinesBenchmarks { diff --git a/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs b/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs index 8c0ce0019e..edbaba0e3a 100644 --- a/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs +++ b/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs @@ -37,6 +37,27 @@ public static Summary CreateSummary(Type benchmarkType, params IColumnHidingRule TestCultureInfo.Instance, ImmutableArray.Empty, ImmutableArray.Create(columHidingRules)); + } + + public static Summary CreateSummaryWithBiasedDistribution(Type benchmarkType, int min, int median, int max, int n) + { + var runInfo = BenchmarkConverter.TypeToBenchmarks(benchmarkType); + return new Summary( + $"MockSummary-N{n}", + runInfo.BenchmarksCases.Select((benchmark, index) => CreateReportWithBiasedDistribution( + benchmark, + (index + 1) * min, + (index + 1) * median, + (index + 1) * max, + n, + Array.Empty())).ToImmutableArray(), + new HostEnvironmentInfoBuilder().WithoutDotNetSdkVersion().Build(), + string.Empty, + string.Empty, + TimeSpan.FromMinutes(1), + TestCultureInfo.Instance, + ImmutableArray.Empty, + ImmutableArray.Empty); } public static Summary CreateSummary(IConfig config) => new Summary( @@ -120,6 +141,40 @@ private static BenchmarkReport CreateReport(BenchmarkCase benchmarkCase, bool hu return new BenchmarkReport(true, benchmarkCase, buildResult, buildResult, new List { executeResult }, metrics); } + private static BenchmarkReport CreateReportWithBiasedDistribution(BenchmarkCase benchmarkCase, int min, int median, int max, int n, Metric[] metrics) + { + var buildResult = BuildResult.Success(GenerateResult.Success(ArtifactsPaths.Empty, Array.Empty())); + bool isFoo = benchmarkCase.Descriptor.WorkloadMethodDisplayInfo == "Foo"; + bool isBar = benchmarkCase.Descriptor.WorkloadMethodDisplayInfo == "Bar"; + var measurements = from i in Enumerable.Range(0, Math.Max(1, n / 9)) + from m in isFoo ? new[] + { + new Measurement(1, IterationMode.Workload, IterationStage.Result, 1, 1, min), // 1 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 2, 1, min + ((median - min) / 2) + 1), // 3 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 4, 1, median), // 4 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 5, 1, median), // 4 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 5, 1, median), // 4 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 7, 1, median + ((max - median) / 2)), // 7 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 8, 1, median + ((max - median) / 2)), // 7 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 9, 1, max), // 10 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 9, 1, max), // 10 + } : new[] + { + new Measurement(1, IterationMode.Workload, IterationStage.Result, 1, 1, min), // 1 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 1, 1, min), // 1 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 2, 1, min + ((median - min) / 2) + 1), // 3 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 2, 1, min + ((median - min) / 2) + 1), // 3 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 4, 1, median), // 4 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 5, 1, median), // 4 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 5, 1, median), // 4 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 7, 1, median + ((max - median) / 2)), // 7 + new Measurement(1, IterationMode.Workload, IterationStage.Result, 9, 1, max), // 10 + } + select m; + var executeResult = new ExecuteResult(measurements.Take(n).ToList(), default, default, 0); + return new BenchmarkReport(true, benchmarkCase, buildResult, buildResult, new List { executeResult }, metrics); + } + [LongRunJob] public class MockBenchmarkClass { From d2f73e8a6556c8f07b13e3682dc39911b90f6be0 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Sun, 25 Aug 2024 11:52:57 +0200 Subject: [PATCH 26/57] Bump .NET SDK: 8.0.101->8.0.401 --- build/sdk/global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/sdk/global.json b/build/sdk/global.json index 94c8cfbc04..c55fa7dfa6 100644 --- a/build/sdk/global.json +++ b/build/sdk/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.101", + "version": "8.0.401", "rollForward": "disable" } } From 64b3d85222f6f7b5b8eccf81c6629e7f48a9b7f5 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Sun, 25 Aug 2024 16:16:34 +0200 Subject: [PATCH 27/57] Refactor CpuInfo detection, fix #2577 --- .../ProcessorBrandStringHelper.cs | 239 +++++++++--------- .../Cpu/CompositeCpuInfoDetector.cs | 18 ++ .../Portability/Cpu/CpuInfo.cs | 60 ++--- .../Portability/Cpu/CpuInfoFormatter.cs | 70 ++--- .../Portability/Cpu/ICpuInfoDetector.cs | 10 + .../Cpu/Linux/LinuxCpuInfoDetector.cs | 21 ++ .../Cpu/Linux/LinuxCpuInfoParser.cs | 111 ++++++++ .../Portability/Cpu/MosCpuInfoProvider.cs | 56 ---- .../Portability/Cpu/ProcCpuInfoKeyNames.cs | 11 - .../Portability/Cpu/ProcCpuInfoParser.cs | 73 ------ .../Portability/Cpu/ProcCpuInfoProvider.cs | 75 ------ .../Portability/Cpu/SysctlCpuInfoParser.cs | 42 --- .../Portability/Cpu/SysctlCpuInfoProvider.cs | 24 -- .../Cpu/Windows/MosCpuInfoDetector.cs | 59 +++++ .../Cpu/Windows/WindowsCpuInfoDetector.cs | 3 + .../Cpu/Windows/WmicCpuInfoDetector.cs | 28 ++ .../Cpu/Windows/WmicCpuInfoKeyNames.cs | 9 + .../Cpu/Windows/WmicCpuInfoParser.cs | 60 +++++ .../Portability/Cpu/WmicCpuInfoKeyNames.cs | 10 - .../Portability/Cpu/WmicCpuInfoParser.cs | 58 ----- .../Portability/Cpu/WmicCpuInfoProvider.cs | 30 --- .../Cpu/macOS/MacOsCpuInfoDetector.cs | 20 ++ .../Cpu/macOS/SysctlCpuInfoParser.cs | 54 ++++ .../Portability/RuntimeInformation.cs | 18 +- .../ExpectedBenchmarkResultsTests.cs | 2 +- .../Builders/HostEnvironmentInfoBuilder.cs | 14 +- .../Environments/ProcessorBrandStringTests.cs | 13 +- .../Portability/Cpu/CpuInfoFormatterTests.cs | 37 ++- .../Cpu/LinuxCpuInfoParserTests.cs | 111 ++++++++ .../Portability/Cpu/ProcCpuInfoParserTests.cs | 90 ------- .../Cpu/SysctlCpuInfoParserTests.cs | 65 +++-- .../Portability/Cpu/TestHelper.cs | 41 ++- .../Portability/Cpu/WmicCpuInfoParserTests.cs | 137 +++++----- 33 files changed, 854 insertions(+), 815 deletions(-) create mode 100644 src/BenchmarkDotNet/Portability/Cpu/CompositeCpuInfoDetector.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/ICpuInfoDetector.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/MosCpuInfoProvider.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoKeyNames.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoParser.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoProvider.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/SysctlCpuInfoParser.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/SysctlCpuInfoProvider.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/Windows/MosCpuInfoDetector.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/Windows/WindowsCpuInfoDetector.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoDetector.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoKeyNames.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoParser.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoKeyNames.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoParser.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoProvider.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/macOS/MacOsCpuInfoDetector.cs create mode 100644 src/BenchmarkDotNet/Portability/Cpu/macOS/SysctlCpuInfoParser.cs create mode 100644 tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs delete mode 100644 tests/BenchmarkDotNet.Tests/Portability/Cpu/ProcCpuInfoParserTests.cs diff --git a/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs b/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs index 882078df74..1f715f9cbe 100644 --- a/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs +++ b/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs @@ -3,153 +3,152 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; +using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Portability.Cpu; using Perfolizer.Horology; -namespace BenchmarkDotNet.Environments +namespace BenchmarkDotNet.Environments; + +public static class ProcessorBrandStringHelper { - public static class ProcessorBrandStringHelper + /// + /// Transform a processor brand string to a nice form for summary. + /// + /// The CPU information + /// Whether to include determined max frequency information + /// Prettified version + public static string Prettify(CpuInfo? cpuInfo, bool includeMaxFrequency = false) { - /// - /// Transform a processor brand string to a nice form for summary. - /// - /// The CPU information - /// Whether to include determined max frequency information - /// Prettified version - public static string Prettify(CpuInfo cpuInfo, bool includeMaxFrequency = false) - { - if (cpuInfo == null || string.IsNullOrEmpty(cpuInfo.ProcessorName)) - { - return "Unknown processor"; - } + string? processorName = cpuInfo?.ProcessorName; + if (processorName == null || processorName.IsBlank()) + return "Unknown processor"; - // Remove parts which don't provide any useful information for user - var processorName = cpuInfo.ProcessorName.Replace("@", "").Replace("(R)", "").Replace("(TM)", ""); + // Remove parts which don't provide any useful information for user + processorName = processorName.Replace("@", "").Replace("(R)", "").Replace("(TM)", ""); - // If we have found physical core(s), we can safely assume we can drop extra info from brand - if (cpuInfo.PhysicalCoreCount.HasValue && cpuInfo.PhysicalCoreCount.Value > 0) - processorName = Regex.Replace(processorName, @"(\w+?-Core Processor)", "").Trim(); + // If we have found physical core(s), we can safely assume we can drop extra info from brand + if (cpuInfo.PhysicalCoreCount is > 0) + processorName = Regex.Replace(processorName, @"(\w+?-Core Processor)", "").Trim(); - string frequencyString = GetBrandStyledActualFrequency(cpuInfo.NominalFrequency); - if (includeMaxFrequency && frequencyString != null && !processorName.Contains(frequencyString)) - { - // show Max only if there's already a frequency name to differentiate the two - string maxFrequency = processorName.Contains("Hz") ? $"(Max: {frequencyString})" : frequencyString; - processorName = $"{processorName} {maxFrequency}"; - } + string frequencyString = GetBrandStyledNominalFrequency(cpuInfo.NominalFrequency); + if (includeMaxFrequency && frequencyString != null && !processorName.Contains(frequencyString)) + { + // show Max only if there's already a frequency name to differentiate the two + string maxFrequency = processorName.Contains("Hz") ? $"(Max: {frequencyString})" : frequencyString; + processorName = $"{processorName} {maxFrequency}"; + } - // Remove double spaces - processorName = Regex.Replace(processorName.Trim(), @"\s+", " "); + // Remove double spaces + processorName = Regex.Replace(processorName.Trim(), @"\s+", " "); - // Add microarchitecture name if known - string microarchitecture = ParseMicroarchitecture(processorName); - if (microarchitecture != null) - processorName = $"{processorName} ({microarchitecture})"; + // Add microarchitecture name if known + string microarchitecture = ParseMicroarchitecture(processorName); + if (microarchitecture != null) + processorName = $"{processorName} ({microarchitecture})"; - return processorName; - } + return processorName; + } - /// - /// Presents actual processor's frequency into brand string format - /// - /// - private static string GetBrandStyledActualFrequency(Frequency? frequency) - { - if (frequency == null) - return null; - return $"{frequency.Value.ToGHz().ToString("N2", DefaultCultureInfo.Instance)}GHz"; - } + /// + /// Presents actual processor's frequency into brand string format + /// + /// + private static string? GetBrandStyledNominalFrequency(Frequency? frequency) + { + if (frequency == null) + return null; + return $"{frequency.Value.ToGHz().ToString("N2", DefaultCultureInfo.Instance)}GHz"; + } - /// - /// Parse a processor name and tries to return a microarchitecture name. - /// Works only for well-known microarchitectures. - /// - private static string? ParseMicroarchitecture(string processorName) + /// + /// Parse a processor name and tries to return a microarchitecture name. + /// Works only for well-known microarchitectures. + /// + private static string? ParseMicroarchitecture(string processorName) + { + if (processorName.StartsWith("Intel Core")) { - if (processorName.StartsWith("Intel Core")) + string model = processorName.Substring("Intel Core".Length).Trim(); + + // Core i3/5/7/9 + if ( + model.Length > 4 && + model[0] == 'i' && + (model[1] == '3' || model[1] == '5' || model[1] == '7' || model[1] == '9') && + (model[2] == '-' || model[2] == ' ')) { - string model = processorName.Substring("Intel Core".Length).Trim(); - - // Core i3/5/7/9 - if ( - model.Length > 4 && - model[0] == 'i' && - (model[1] == '3' || model[1] == '5' || model[1] == '7' || model[1] == '9') && - (model[2] == '-' || model[2] == ' ')) - { - string modelNumber = model.Substring(3); - if (modelNumber.StartsWith("CPU")) - modelNumber = modelNumber.Substring(3).Trim(); - if (modelNumber.Contains("CPU")) - modelNumber = modelNumber.Substring(0, modelNumber.IndexOf("CPU", StringComparison.Ordinal)).Trim(); - return ParseIntelCoreMicroarchitecture(modelNumber); - } + string modelNumber = model.Substring(3); + if (modelNumber.StartsWith("CPU")) + modelNumber = modelNumber.Substring(3).Trim(); + if (modelNumber.Contains("CPU")) + modelNumber = modelNumber.Substring(0, modelNumber.IndexOf("CPU", StringComparison.Ordinal)).Trim(); + return ParseIntelCoreMicroarchitecture(modelNumber); } - - return null; } - private static readonly Lazy> KnownMicroarchitectures = new Lazy>(() => + return null; + } + + private static readonly Lazy> KnownMicroarchitectures = new Lazy>(() => + { + var data = ResourceHelper.LoadResource("BenchmarkDotNet.Environments.microarchitectures.txt").Split('\r', '\n'); + var dictionary = new Dictionary(); + string? currentMicroarchitecture = null; + foreach (string line in data) { - var data = ResourceHelper.LoadResource("BenchmarkDotNet.Environments.microarchitectures.txt").Split('\r', '\n'); - var dictionary = new Dictionary(); - string? currentMicroarchitecture = null; - foreach (string line in data) + if (line.StartsWith("//") || string.IsNullOrWhiteSpace(line)) + continue; + if (line.StartsWith("#")) { - if (line.StartsWith("//") || string.IsNullOrWhiteSpace(line)) - continue; - if (line.StartsWith("#")) - { - currentMicroarchitecture = line.Substring(1).Trim(); - continue; - } - - string modelNumber = line.Trim(); - if (dictionary.ContainsKey(modelNumber)) - throw new Exception($"{modelNumber} is defined twice in microarchitectures.txt"); - if (currentMicroarchitecture == null) - throw new Exception($"{modelNumber} doesn't have defined microarchitecture in microarchitectures.txt"); - dictionary[modelNumber] = currentMicroarchitecture; + currentMicroarchitecture = line.Substring(1).Trim(); + continue; } - return dictionary; - }); + string modelNumber = line.Trim(); + if (dictionary.ContainsKey(modelNumber)) + throw new Exception($"{modelNumber} is defined twice in microarchitectures.txt"); + if (currentMicroarchitecture == null) + throw new Exception($"{modelNumber} doesn't have defined microarchitecture in microarchitectures.txt"); + dictionary[modelNumber] = currentMicroarchitecture; + } - // see http://www.intel.com/content/www/us/en/processors/processor-numbers.html - [SuppressMessage("ReSharper", "StringLiteralTypo")] - internal static string? ParseIntelCoreMicroarchitecture(string modelNumber) - { - if (KnownMicroarchitectures.Value.TryGetValue(modelNumber, out string? microarchitecture)) - return microarchitecture; + return dictionary; + }); + + // see http://www.intel.com/content/www/us/en/processors/processor-numbers.html + [SuppressMessage("ReSharper", "StringLiteralTypo")] + internal static string? ParseIntelCoreMicroarchitecture(string modelNumber) + { + if (KnownMicroarchitectures.Value.TryGetValue(modelNumber, out string? microarchitecture)) + return microarchitecture; - if (modelNumber.Length >= 3 && modelNumber.Substring(0, 3).All(char.IsDigit) && - (modelNumber.Length == 3 || !char.IsDigit(modelNumber[3]))) - return "Nehalem"; - if (modelNumber.Length >= 4 && modelNumber.Substring(0, 4).All(char.IsDigit)) + if (modelNumber.Length >= 3 && modelNumber.Substring(0, 3).All(char.IsDigit) && + (modelNumber.Length == 3 || !char.IsDigit(modelNumber[3]))) + return "Nehalem"; + if (modelNumber.Length >= 4 && modelNumber.Substring(0, 4).All(char.IsDigit)) + { + char generation = modelNumber[0]; + switch (generation) { - char generation = modelNumber[0]; - switch (generation) - { - case '2': - return "Sandy Bridge"; - case '3': - return "Ivy Bridge"; - case '4': - return "Haswell"; - case '5': - return "Broadwell"; - case '6': - return "Skylake"; - case '7': - return "Kaby Lake"; - case '8': - return "Coffee Lake"; - default: - return null; - } + case '2': + return "Sandy Bridge"; + case '3': + return "Ivy Bridge"; + case '4': + return "Haswell"; + case '5': + return "Broadwell"; + case '6': + return "Skylake"; + case '7': + return "Kaby Lake"; + case '8': + return "Coffee Lake"; + default: + return null; } - return null; } + return null; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/CompositeCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/CompositeCpuInfoDetector.cs new file mode 100644 index 0000000000..d6919d938f --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/CompositeCpuInfoDetector.cs @@ -0,0 +1,18 @@ +using System.Linq; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Portability.Cpu.Linux; +using BenchmarkDotNet.Portability.Cpu.macOS; +using BenchmarkDotNet.Portability.Cpu.Windows; + +namespace BenchmarkDotNet.Portability.Cpu; + +internal class CompositeCpuInfoDetector(params ICpuInfoDetector[] detectors) : ICpuInfoDetector +{ + public bool IsApplicable() => detectors.Any(loader => loader.IsApplicable()); + + public CpuInfo? Detect() => detectors + .Where(loader => loader.IsApplicable()) + .Select(loader => loader.Detect()) + .WhereNotNull() + .FirstOrDefault(); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/CpuInfo.cs b/src/BenchmarkDotNet/Portability/Cpu/CpuInfo.cs index 8c3227a25d..ee7cf310c7 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/CpuInfo.cs +++ b/src/BenchmarkDotNet/Portability/Cpu/CpuInfo.cs @@ -1,37 +1,33 @@ -using Perfolizer.Horology; +using BenchmarkDotNet.Portability.Cpu.Linux; +using BenchmarkDotNet.Portability.Cpu.macOS; +using BenchmarkDotNet.Portability.Cpu.Windows; +using Perfolizer.Horology; -namespace BenchmarkDotNet.Portability.Cpu +namespace BenchmarkDotNet.Portability.Cpu; + +public class CpuInfo( + string? processorName, + int? physicalProcessorCount, + int? physicalCoreCount, + int? logicalCoreCount, + Frequency? nominalFrequency, + Frequency? maxFrequency = null) { - public class CpuInfo - { - public string ProcessorName { get; } - public int? PhysicalProcessorCount { get; } - public int? PhysicalCoreCount { get; } - public int? LogicalCoreCount { get; } - public Frequency? NominalFrequency { get; } - public Frequency? MinFrequency { get; } - public Frequency? MaxFrequency { get; } + public static readonly CpuInfo Empty = new (null, null, null, null, null, null); + public static CpuInfo FromName(string processorName) => new (processorName, null, null, null, null); + public static CpuInfo FromNameAndFrequency(string processorName, Frequency nominalFrequency) => new (processorName, null, null, null, nominalFrequency); + + private static readonly CompositeCpuInfoDetector Detector = new ( + new WindowsCpuInfoDetector(), + new LinuxCpuInfoDetector(), + new MacOsCpuInfoDetector()); - internal CpuInfo(string processorName, Frequency? nominalFrequency) - : this(processorName, null, null, null, nominalFrequency, null, null) - { - } + public static CpuInfo? DetectCurrent() => Detector.Detect(); - public CpuInfo(string processorName, - int? physicalProcessorCount, - int? physicalCoreCount, - int? logicalCoreCount, - Frequency? nominalFrequency, - Frequency? minFrequency, - Frequency? maxFrequency) - { - ProcessorName = processorName; - PhysicalProcessorCount = physicalProcessorCount; - PhysicalCoreCount = physicalCoreCount; - LogicalCoreCount = logicalCoreCount; - NominalFrequency = nominalFrequency; - MinFrequency = minFrequency; - MaxFrequency = maxFrequency; - } - } + public string? ProcessorName { get; } = processorName; + public int? PhysicalProcessorCount { get; } = physicalProcessorCount; + public int? PhysicalCoreCount { get; } = physicalCoreCount; + public int? LogicalCoreCount { get; } = logicalCoreCount; + public Frequency? NominalFrequency { get; } = nominalFrequency ?? maxFrequency; + public Frequency? MaxFrequency { get; } = maxFrequency ?? nominalFrequency; } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/CpuInfoFormatter.cs b/src/BenchmarkDotNet/Portability/Cpu/CpuInfoFormatter.cs index e016ad1a4b..88301271e4 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/CpuInfoFormatter.cs +++ b/src/BenchmarkDotNet/Portability/Cpu/CpuInfoFormatter.cs @@ -1,50 +1,56 @@ using System.Collections.Generic; using BenchmarkDotNet.Environments; -namespace BenchmarkDotNet.Portability.Cpu +namespace BenchmarkDotNet.Portability.Cpu; + +public static class CpuInfoFormatter { - public static class CpuInfoFormatter + public static string Format(CpuInfo? cpuInfo) { - public static string Format(CpuInfo cpuInfo) - { - if (cpuInfo == null) - { - return "Unknown processor"; - } + if (cpuInfo == null) + return "Unknown processor"; - var parts = new List - { - ProcessorBrandStringHelper.Prettify(cpuInfo, includeMaxFrequency: true) - }; + var parts = new List + { + ProcessorBrandStringHelper.Prettify(cpuInfo, includeMaxFrequency: true) + }; - if (cpuInfo.PhysicalProcessorCount > 0) - parts.Add($", {cpuInfo.PhysicalProcessorCount} CPU"); + if (cpuInfo.PhysicalProcessorCount > 0) + parts.Add($", {cpuInfo.PhysicalProcessorCount} CPU"); - if (cpuInfo.LogicalCoreCount == 1) + switch (cpuInfo.LogicalCoreCount) + { + case 1: parts.Add(", 1 logical core"); - - if (cpuInfo.LogicalCoreCount > 1) + break; + case > 1: parts.Add($", {cpuInfo.LogicalCoreCount} logical cores"); + break; + } - if (cpuInfo.LogicalCoreCount > 0 && cpuInfo.PhysicalCoreCount > 0) - parts.Add(" and "); - else if (cpuInfo.PhysicalCoreCount > 0) - parts.Add(", "); + if (cpuInfo.LogicalCoreCount > 0 && cpuInfo.PhysicalCoreCount > 0) + parts.Add(" and "); + else if (cpuInfo.PhysicalCoreCount > 0) + parts.Add(", "); - if (cpuInfo.PhysicalCoreCount == 1) + switch (cpuInfo.PhysicalCoreCount) + { + case 1: parts.Add("1 physical core"); - if (cpuInfo.PhysicalCoreCount > 1) + break; + case > 1: parts.Add($"{cpuInfo.PhysicalCoreCount} physical cores"); + break; + } - string result = string.Join("", parts); - // The line with ProcessorBrandString is one of the longest lines in the summary. - // When people past in on GitHub, it can be a reason of an ugly horizontal scrollbar. - // To avoid this, we are trying to minimize this line and use the minimum possible number of characters. - // Here we are removing the repetitive "cores" word. - if (result.Contains("logical cores") && result.Contains("physical cores")) - result = result.Replace("logical cores", "logical"); + string result = string.Join("", parts); + // The line with ProcessorBrandString is one of the longest lines in the summary. + // When people past in on GitHub, it can be a reason of an ugly horizontal scrollbar. + // To avoid this, we are trying to minimize this line and use the minimum possible number of characters. + // Here we are removing the repetitive "cores" word. + if (result.Contains("logical cores") && result.Contains("physical cores")) + result = result.Replace("logical cores", "logical"); - return result; - } + return result; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/ICpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/ICpuInfoDetector.cs new file mode 100644 index 0000000000..748f9ce186 --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/ICpuInfoDetector.cs @@ -0,0 +1,10 @@ +namespace BenchmarkDotNet.Portability.Cpu; + +/// +/// Loads the for the current hardware +/// +internal interface ICpuInfoDetector +{ + bool IsApplicable(); + CpuInfo? Detect(); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs new file mode 100644 index 0000000000..b88b41c6be --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs @@ -0,0 +1,21 @@ +using BenchmarkDotNet.Helpers; + +namespace BenchmarkDotNet.Portability.Cpu.Linux; + +/// +/// CPU information from output of the `cat /proc/cpuinfo` and `lscpu` command. +/// Linux only. +/// +internal class LinuxCpuInfoDetector : ICpuInfoDetector +{ + public bool IsApplicable() => RuntimeInformation.IsLinux(); + + public CpuInfo? Detect() + { + if (!IsApplicable()) return null; + + string cpuInfo = ProcessHelper.RunAndReadOutput("cat", "/proc/cpuinfo") ?? ""; + string lscpu = ProcessHelper.RunAndReadOutput("/bin/bash", "-c \"lscpu\""); + return LinuxCpuInfoParser.Parse(cpuInfo, lscpu); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs b/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs new file mode 100644 index 0000000000..4533d60fee --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Helpers; +using Perfolizer.Horology; + +namespace BenchmarkDotNet.Portability.Cpu.Linux; + +internal static class LinuxCpuInfoParser +{ + private static class ProcCpu + { + internal const string PhysicalId = "physical id"; + internal const string CpuCores = "cpu cores"; + internal const string ModelName = "model name"; + internal const string MaxFrequency = "max freq"; + } + + private static class Lscpu + { + internal const string MaxFrequency = "CPU max MHz"; + internal const string ModelName = "Model name"; + internal const string CoresPerSocket = "Core(s) per socket"; + } + + /// Output of `cat /proc/cpuinfo` + /// Output of `lscpu` + internal static CpuInfo Parse(string? cpuInfo, string? lscpu) + { + var processorModelNames = new HashSet(); + var processorsToPhysicalCoreCount = new Dictionary(); + int logicalCoreCount = 0; + Frequency? maxFrequency = null; + + var logicalCores = SectionsHelper.ParseSections(cpuInfo, ':'); + foreach (var logicalCore in logicalCores) + { + if (logicalCore.TryGetValue(ProcCpu.PhysicalId, out string physicalId) && + logicalCore.TryGetValue(ProcCpu.CpuCores, out string cpuCoresValue) && + int.TryParse(cpuCoresValue, out int cpuCoreCount) && + cpuCoreCount > 0) + processorsToPhysicalCoreCount[physicalId] = cpuCoreCount; + + if (logicalCore.TryGetValue(ProcCpu.ModelName, out string modelName)) + { + processorModelNames.Add(modelName); + logicalCoreCount++; + } + + if (logicalCore.TryGetValue(ProcCpu.MaxFrequency, out string maxCpuFreqValue) && + Frequency.TryParseMHz(maxCpuFreqValue, out var maxCpuFreq)) + { + maxFrequency = maxCpuFreq; + } + } + + int? coresPerSocket = null; + if (lscpu != null) + { + var lscpuParts = lscpu.Split('\n') + .Where(line => line.Contains(':')) + .SelectMany(line => line.Split([':'], 2)) + .ToList(); + for (int i = 0; i + 1 < lscpuParts.Count; i += 2) + { + string name = lscpuParts[i].Trim(); + string value = lscpuParts[i + 1].Trim(); + + if (name.EqualsWithIgnoreCase(Lscpu.MaxFrequency) && + Frequency.TryParseMHz(value.Replace(',', '.'), out var maxFrequencyMHz)) // Example: `CPU max MHz: 3200,0000` + maxFrequency = Frequency.FromMHz(maxFrequencyMHz); + + if (name.EqualsWithIgnoreCase(Lscpu.ModelName)) + processorModelNames.Add(value); + + if (name.EqualsWithIgnoreCase(Lscpu.CoresPerSocket) && + int.TryParse(value, out int coreCount)) + coresPerSocket = coreCount; + } + } + + var nominalFrequency = processorModelNames + .Select(ParseFrequencyFromBrandString) + .WhereNotNull() + .FirstOrDefault() ?? maxFrequency; + string processorName = processorModelNames.Count > 0 ? string.Join(", ", processorModelNames) : null; + int? physicalProcessorCount = processorsToPhysicalCoreCount.Count > 0 ? processorsToPhysicalCoreCount.Count : null; + int? physicalCoreCount = processorsToPhysicalCoreCount.Count > 0 ? processorsToPhysicalCoreCount.Values.Sum() : coresPerSocket; + return new CpuInfo( + processorName, + physicalProcessorCount, + physicalCoreCount, + logicalCoreCount > 0 ? logicalCoreCount : null, + nominalFrequency, + maxFrequency); + } + + internal static Frequency? ParseFrequencyFromBrandString(string brandString) + { + const string pattern = "(\\d.\\d+)GHz"; + var matches = Regex.Matches(brandString, pattern, RegexOptions.IgnoreCase); + if (matches.Count > 0 && matches[0].Groups.Count > 1) + { + string match = Regex.Matches(brandString, pattern, RegexOptions.IgnoreCase)[0].Groups[1].ToString(); + return Frequency.TryParseGHz(match, out var result) ? result : null; + } + + return null; + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/MosCpuInfoProvider.cs b/src/BenchmarkDotNet/Portability/Cpu/MosCpuInfoProvider.cs deleted file mode 100644 index 2edc7cabcb..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/MosCpuInfoProvider.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Management; -using Perfolizer.Horology; - -namespace BenchmarkDotNet.Portability.Cpu -{ - internal static class MosCpuInfoProvider - { -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] -#endif - internal static readonly Lazy MosCpuInfo = new Lazy(Load); - -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] -#endif - private static CpuInfo Load() - { - var processorModelNames = new HashSet(); - uint physicalCoreCount = 0; - uint logicalCoreCount = 0; - int processorsCount = 0; - uint nominalClockSpeed = 0; - uint maxClockSpeed = 0; - uint minClockSpeed = 0; - - - using (var mosProcessor = new ManagementObjectSearcher("SELECT * FROM Win32_Processor")) - { - foreach (var moProcessor in mosProcessor.Get().Cast()) - { - string name = moProcessor[WmicCpuInfoKeyNames.Name]?.ToString(); - if (!string.IsNullOrEmpty(name)) - { - processorModelNames.Add(name); - processorsCount++; - physicalCoreCount += (uint) moProcessor[WmicCpuInfoKeyNames.NumberOfCores]; - logicalCoreCount += (uint) moProcessor[WmicCpuInfoKeyNames.NumberOfLogicalProcessors]; - maxClockSpeed = (uint) moProcessor[WmicCpuInfoKeyNames.MaxClockSpeed]; - } - } - } - - return new CpuInfo( - processorModelNames.Count > 0 ? string.Join(", ", processorModelNames) : null, - processorsCount > 0 ? processorsCount : (int?) null, - physicalCoreCount > 0 ? (int?) physicalCoreCount : null, - logicalCoreCount > 0 ? (int?) logicalCoreCount : null, - nominalClockSpeed > 0 && logicalCoreCount > 0 ? Frequency.FromMHz(nominalClockSpeed) : (Frequency?) null, - minClockSpeed > 0 && logicalCoreCount > 0 ? Frequency.FromMHz(minClockSpeed) : (Frequency?) null, - maxClockSpeed > 0 && logicalCoreCount > 0 ? Frequency.FromMHz(maxClockSpeed) : (Frequency?) null); - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoKeyNames.cs b/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoKeyNames.cs deleted file mode 100644 index 60284c5363..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoKeyNames.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BenchmarkDotNet.Portability.Cpu -{ - internal static class ProcCpuInfoKeyNames - { - internal const string PhysicalId = "physical id"; - internal const string CpuCores = "cpu cores"; - internal const string ModelName = "model name"; - internal const string MaxFrequency = "max freq"; - internal const string MinFrequency = "min freq"; - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoParser.cs b/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoParser.cs deleted file mode 100644 index e3d0a14c46..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoParser.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using BenchmarkDotNet.Helpers; -using Perfolizer.Horology; - -namespace BenchmarkDotNet.Portability.Cpu -{ - internal static class ProcCpuInfoParser - { - internal static CpuInfo ParseOutput(string? content) - { - var logicalCores = SectionsHelper.ParseSections(content, ':'); - var processorModelNames = new HashSet(); - var processorsToPhysicalCoreCount = new Dictionary(); - - int logicalCoreCount = 0; - var nominalFrequency = Frequency.Zero; - var minFrequency = Frequency.Zero; - var maxFrequency = Frequency.Zero; - - foreach (var logicalCore in logicalCores) - { - if (logicalCore.TryGetValue(ProcCpuInfoKeyNames.PhysicalId, out string physicalId) && - logicalCore.TryGetValue(ProcCpuInfoKeyNames.CpuCores, out string cpuCoresValue) && - int.TryParse(cpuCoresValue, out int cpuCoreCount) && - cpuCoreCount > 0) - processorsToPhysicalCoreCount[physicalId] = cpuCoreCount; - - if (logicalCore.TryGetValue(ProcCpuInfoKeyNames.ModelName, out string modelName)) - { - processorModelNames.Add(modelName); - nominalFrequency = ParseFrequencyFromBrandString(modelName); - logicalCoreCount++; - } - - if (logicalCore.TryGetValue(ProcCpuInfoKeyNames.MinFrequency, out string minCpuFreqValue) - && Frequency.TryParseMHz(minCpuFreqValue, out var minCpuFreq)) - { - minFrequency = minCpuFreq; - } - - if (logicalCore.TryGetValue(ProcCpuInfoKeyNames.MaxFrequency, out string maxCpuFreqValue) - && Frequency.TryParseMHz(maxCpuFreqValue, out var maxCpuFreq)) - { - maxFrequency = maxCpuFreq; - } - } - - return new CpuInfo( - processorModelNames.Count > 0 ? string.Join(", ", processorModelNames) : null, - processorsToPhysicalCoreCount.Count > 0 ? processorsToPhysicalCoreCount.Count : (int?) null, - processorsToPhysicalCoreCount.Count > 0 ? processorsToPhysicalCoreCount.Values.Sum() : (int?) null, - logicalCoreCount > 0 ? logicalCoreCount : (int?) null, - nominalFrequency > 0 ? nominalFrequency : (Frequency?) null, - minFrequency > 0 ? minFrequency : (Frequency?) null, - maxFrequency > 0 ? maxFrequency : (Frequency?) null); - } - - internal static Frequency ParseFrequencyFromBrandString(string brandString) - { - const string pattern = "(\\d.\\d+)GHz"; - var matches = Regex.Matches(brandString, pattern, RegexOptions.IgnoreCase); - if (matches.Count > 0 && matches[0].Groups.Count > 1) - { - string match = Regex.Matches(brandString, pattern, RegexOptions.IgnoreCase)[0].Groups[1].ToString(); - return Frequency.TryParseGHz(match, out var result) ? result : Frequency.Zero; - } - - return 0d; - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoProvider.cs b/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoProvider.cs deleted file mode 100644 index 7cadad1ff2..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/ProcCpuInfoProvider.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using BenchmarkDotNet.Helpers; -using Perfolizer.Horology; - -namespace BenchmarkDotNet.Portability.Cpu -{ - /// - /// CPU information from output of the `cat /proc/info` command. - /// Linux only. - /// - internal static class ProcCpuInfoProvider - { - internal static readonly Lazy ProcCpuInfo = new (Load); - - private static CpuInfo? Load() - { - if (RuntimeInformation.IsLinux()) - { - string content = ProcessHelper.RunAndReadOutput("cat", "/proc/cpuinfo") ?? ""; - string output = GetCpuSpeed() ?? ""; - content += output; - return ProcCpuInfoParser.ParseOutput(content); - } - return null; - } - - private static string? GetCpuSpeed() - { - try - { - string[]? output = ProcessHelper.RunAndReadOutput("/bin/bash", "-c \"lscpu | grep MHz\"")? - .Split('\n') - .SelectMany(x => x.Split(':')) - .ToArray(); - - return ParseCpuFrequencies(output); - } - catch (Exception) - { - return null; - } - } - - private static string? ParseCpuFrequencies(string[]? input) - { - // Example of output we trying to parse: - // - // CPU MHz: 949.154 - // CPU max MHz: 3200,0000 - // CPU min MHz: 800,0000 - - if (input == null) - return null; - - var output = new StringBuilder(); - for (int i = 0; i + 1 < input.Length; i += 2) - { - string name = input[i].Trim(); - string value = input[i + 1].Trim(); - - if (name.EqualsWithIgnoreCase("CPU min MHz")) - if (Frequency.TryParseMHz(value.Replace(',', '.'), out var minFrequency)) - output.Append($"\n{ProcCpuInfoKeyNames.MinFrequency}\t:{minFrequency.ToMHz()}"); - - if (name.EqualsWithIgnoreCase("CPU max MHz")) - if (Frequency.TryParseMHz(value.Replace(',', '.'), out var maxFrequency)) - output.Append($"\n{ProcCpuInfoKeyNames.MaxFrequency}\t:{maxFrequency.ToMHz()}"); - } - - return output.ToString(); - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/SysctlCpuInfoParser.cs b/src/BenchmarkDotNet/Portability/Cpu/SysctlCpuInfoParser.cs deleted file mode 100644 index 7e61bbd3ec..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/SysctlCpuInfoParser.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using BenchmarkDotNet.Helpers; -using BenchmarkDotNet.Extensions; - -namespace BenchmarkDotNet.Portability.Cpu -{ - internal static class SysctlCpuInfoParser - { - [SuppressMessage("ReSharper", "StringLiteralTypo")] - internal static CpuInfo ParseOutput(string? content) - { - var sysctl = SectionsHelper.ParseSection(content, ':'); - string processorName = sysctl.GetValueOrDefault("machdep.cpu.brand_string"); - var physicalProcessorCount = GetPositiveIntValue(sysctl, "hw.packages"); - var physicalCoreCount = GetPositiveIntValue(sysctl, "hw.physicalcpu"); - var logicalCoreCount = GetPositiveIntValue(sysctl, "hw.logicalcpu"); - var nominalFrequency = GetPositiveLongValue(sysctl, "hw.cpufrequency"); - var minFrequency = GetPositiveLongValue(sysctl, "hw.cpufrequency_min"); - var maxFrequency = GetPositiveLongValue(sysctl, "hw.cpufrequency_max"); - return new CpuInfo(processorName, physicalProcessorCount, physicalCoreCount, logicalCoreCount, nominalFrequency, minFrequency, maxFrequency); - } - - private static int? GetPositiveIntValue(Dictionary sysctl, string keyName) - { - if (sysctl.TryGetValue(keyName, out string value) && - int.TryParse(value, out int result) && - result > 0) - return result; - return null; - } - - private static long? GetPositiveLongValue(Dictionary sysctl, string keyName) - { - if (sysctl.TryGetValue(keyName, out string value) && - long.TryParse(value, out long result) && - result > 0) - return result; - return null; - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/SysctlCpuInfoProvider.cs b/src/BenchmarkDotNet/Portability/Cpu/SysctlCpuInfoProvider.cs deleted file mode 100644 index 32a207c1c8..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/SysctlCpuInfoProvider.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using BenchmarkDotNet.Helpers; - -namespace BenchmarkDotNet.Portability.Cpu -{ - /// - /// CPU information from output of the `sysctl -a` command. - /// MacOSX only. - /// - internal static class SysctlCpuInfoProvider - { - internal static readonly Lazy SysctlCpuInfo = new Lazy(Load); - - private static CpuInfo? Load() - { - if (RuntimeInformation.IsMacOS()) - { - string content = ProcessHelper.RunAndReadOutput("sysctl", "-a"); - return SysctlCpuInfoParser.ParseOutput(content); - } - return null; - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/MosCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/Windows/MosCpuInfoDetector.cs new file mode 100644 index 0000000000..5a1c6f2cdb --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/Windows/MosCpuInfoDetector.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using Perfolizer.Horology; + +namespace BenchmarkDotNet.Portability.Cpu.Windows; + +internal class MosCpuInfoDetector : ICpuInfoDetector +{ +#if NET6_0_OR_GREATER + [System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif + public bool IsApplicable() => RuntimeInformation.IsWindows() && + RuntimeInformation.IsFullFramework && + !RuntimeInformation.IsMono; + +#if NET6_0_OR_GREATER + [System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif + public CpuInfo? Detect() + { + if (!IsApplicable()) return null; + + var processorModelNames = new HashSet(); + int physicalCoreCount = 0; + int logicalCoreCount = 0; + int processorsCount = 0; + int sumMaxFrequency = 0; + + using (var mosProcessor = new ManagementObjectSearcher("SELECT * FROM Win32_Processor")) + { + foreach (var moProcessor in mosProcessor.Get().Cast()) + { + string name = moProcessor[WmicCpuInfoKeyNames.Name]?.ToString(); + if (!string.IsNullOrEmpty(name)) + { + processorModelNames.Add(name); + processorsCount++; + physicalCoreCount += (int)(uint)moProcessor[WmicCpuInfoKeyNames.NumberOfCores]; + logicalCoreCount += (int)(uint)moProcessor[WmicCpuInfoKeyNames.NumberOfLogicalProcessors]; + sumMaxFrequency = (int)(uint)moProcessor[WmicCpuInfoKeyNames.MaxClockSpeed]; + } + } + } + + string processorName = processorModelNames.Count > 0 ? string.Join(", ", processorModelNames) : null; + Frequency? maxFrequency = sumMaxFrequency > 0 && processorsCount > 0 + ? Frequency.FromMHz(sumMaxFrequency * 1.0 / processorsCount) + : null; + + return new CpuInfo( + processorName, + GetCount(processorsCount), GetCount(physicalCoreCount), GetCount(logicalCoreCount), + maxFrequency, maxFrequency); + + int? GetCount(int count) => count > 0 ? count : null; + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/WindowsCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/Windows/WindowsCpuInfoDetector.cs new file mode 100644 index 0000000000..9846c8b12a --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/Windows/WindowsCpuInfoDetector.cs @@ -0,0 +1,3 @@ +namespace BenchmarkDotNet.Portability.Cpu.Windows; + +internal class WindowsCpuInfoDetector() : CompositeCpuInfoDetector(new MosCpuInfoDetector(), new WmicCpuInfoDetector()); \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoDetector.cs new file mode 100644 index 0000000000..6dbf1fad8d --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoDetector.cs @@ -0,0 +1,28 @@ +using System.IO; +using BenchmarkDotNet.Helpers; + +namespace BenchmarkDotNet.Portability.Cpu.Windows; + +/// +/// CPU information from output of the `wmic cpu get Name, NumberOfCores, NumberOfLogicalProcessors /Format:List` command. +/// Windows only. +/// +internal class WmicCpuInfoDetector : ICpuInfoDetector +{ + private const string DefaultWmicPath = @"C:\Windows\System32\wbem\WMIC.exe"; + + public bool IsApplicable() => RuntimeInformation.IsWindows(); + + public CpuInfo? Detect() + { + if (!IsApplicable()) return null; + + const string argList = $"{WmicCpuInfoKeyNames.Name}, " + + $"{WmicCpuInfoKeyNames.NumberOfCores}, " + + $"{WmicCpuInfoKeyNames.NumberOfLogicalProcessors}, " + + $"{WmicCpuInfoKeyNames.MaxClockSpeed}"; + string wmicPath = File.Exists(DefaultWmicPath) ? DefaultWmicPath : "wmic"; + string wmicOutput = ProcessHelper.RunAndReadOutput(wmicPath, $"cpu get {argList} /Format:List"); + return WmicCpuInfoParser.Parse(wmicOutput); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoKeyNames.cs b/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoKeyNames.cs new file mode 100644 index 0000000000..1e08cc0bfb --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoKeyNames.cs @@ -0,0 +1,9 @@ +namespace BenchmarkDotNet.Portability.Cpu.Windows; + +internal static class WmicCpuInfoKeyNames +{ + internal const string NumberOfLogicalProcessors = "NumberOfLogicalProcessors"; + internal const string NumberOfCores = "NumberOfCores"; + internal const string Name = "Name"; + internal const string MaxClockSpeed = "MaxClockSpeed"; +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoParser.cs b/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoParser.cs new file mode 100644 index 0000000000..47ede5f0cc --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoParser.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using BenchmarkDotNet.Helpers; +using Perfolizer.Horology; + +namespace BenchmarkDotNet.Portability.Cpu.Windows; + +internal static class WmicCpuInfoParser +{ + /// + /// Parses wmic output and returns + /// + /// Output of `wmic cpu get Name, NumberOfCores, NumberOfLogicalProcessors /Format:List` + internal static CpuInfo Parse(string? wmicOutput) + { + var processorModelNames = new HashSet(); + int physicalCoreCount = 0; + int logicalCoreCount = 0; + int processorsCount = 0; + var sumMaxFrequency = Frequency.Zero; + + var processors = SectionsHelper.ParseSections(wmicOutput, '='); + foreach (var processor in processors) + { + if (processor.TryGetValue(WmicCpuInfoKeyNames.NumberOfCores, out string numberOfCoresValue) && + int.TryParse(numberOfCoresValue, out int numberOfCores) && + numberOfCores > 0) + physicalCoreCount += numberOfCores; + + if (processor.TryGetValue(WmicCpuInfoKeyNames.NumberOfLogicalProcessors, out string numberOfLogicalValue) && + int.TryParse(numberOfLogicalValue, out int numberOfLogical) && + numberOfLogical > 0) + logicalCoreCount += numberOfLogical; + + if (processor.TryGetValue(WmicCpuInfoKeyNames.Name, out string name)) + { + processorModelNames.Add(name); + processorsCount++; + } + + if (processor.TryGetValue(WmicCpuInfoKeyNames.MaxClockSpeed, out string frequencyValue) + && int.TryParse(frequencyValue, out int frequency) + && frequency > 0) + { + sumMaxFrequency += frequency; + } + } + + string? processorName = processorModelNames.Count > 0 ? string.Join(", ", processorModelNames) : null; + Frequency? maxFrequency = sumMaxFrequency > 0 && processorsCount > 0 + ? Frequency.FromMHz(sumMaxFrequency / processorsCount) + : null; + + return new CpuInfo( + processorName, + GetCount(processorsCount), GetCount(physicalCoreCount), GetCount(logicalCoreCount), + maxFrequency, maxFrequency); + + int? GetCount(int count) => count > 0 ? count : null; + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoKeyNames.cs b/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoKeyNames.cs deleted file mode 100644 index 9dd2209304..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoKeyNames.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BenchmarkDotNet.Portability.Cpu -{ - internal static class WmicCpuInfoKeyNames - { - internal const string NumberOfLogicalProcessors = "NumberOfLogicalProcessors"; - internal const string NumberOfCores = "NumberOfCores"; - internal const string Name = "Name"; - internal const string MaxClockSpeed = "MaxClockSpeed"; - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoParser.cs b/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoParser.cs deleted file mode 100644 index 441b1a8b10..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoParser.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using BenchmarkDotNet.Helpers; -using Perfolizer.Horology; - -namespace BenchmarkDotNet.Portability.Cpu -{ - internal static class WmicCpuInfoParser - { - internal static CpuInfo ParseOutput(string? content) - { - var processors = SectionsHelper.ParseSections(content, '='); - - var processorModelNames = new HashSet(); - int physicalCoreCount = 0; - int logicalCoreCount = 0; - int processorsCount = 0; - - var currentClockSpeed = Frequency.Zero; - var maxClockSpeed = Frequency.Zero; - var minClockSpeed = Frequency.Zero; - - foreach (var processor in processors) - { - if (processor.TryGetValue(WmicCpuInfoKeyNames.NumberOfCores, out string numberOfCoresValue) && - int.TryParse(numberOfCoresValue, out int numberOfCores) && - numberOfCores > 0) - physicalCoreCount += numberOfCores; - - if (processor.TryGetValue(WmicCpuInfoKeyNames.NumberOfLogicalProcessors, out string numberOfLogicalValue) && - int.TryParse(numberOfLogicalValue, out int numberOfLogical) && - numberOfLogical > 0) - logicalCoreCount += numberOfLogical; - - if (processor.TryGetValue(WmicCpuInfoKeyNames.Name, out string name)) - { - processorModelNames.Add(name); - processorsCount++; - } - - if (processor.TryGetValue(WmicCpuInfoKeyNames.MaxClockSpeed, out string frequencyValue) - && int.TryParse(frequencyValue, out int frequency) - && frequency > 0) - { - maxClockSpeed += frequency; - } - } - - return new CpuInfo( - processorModelNames.Count > 0 ? string.Join(", ", processorModelNames) : null, - processorsCount > 0 ? processorsCount : (int?) null, - physicalCoreCount > 0 ? physicalCoreCount : (int?) null, - logicalCoreCount > 0 ? logicalCoreCount : (int?) null, - currentClockSpeed > 0 && processorsCount > 0 ? Frequency.FromMHz(currentClockSpeed / processorsCount) : (Frequency?) null, - minClockSpeed > 0 && processorsCount > 0 ? Frequency.FromMHz(minClockSpeed / processorsCount) : (Frequency?) null, - maxClockSpeed > 0 && processorsCount > 0 ? Frequency.FromMHz(maxClockSpeed / processorsCount) : (Frequency?) null); - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoProvider.cs b/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoProvider.cs deleted file mode 100644 index d259200701..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/WmicCpuInfoProvider.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.IO; -using BenchmarkDotNet.Helpers; - -namespace BenchmarkDotNet.Portability.Cpu -{ - /// - /// CPU information from output of the `wmic cpu get Name, NumberOfCores, NumberOfLogicalProcessors /Format:List` command. - /// Windows only. - /// - internal static class WmicCpuInfoProvider - { - internal static readonly Lazy WmicCpuInfo = new (Load); - - private const string DefaultWmicPath = @"C:\Windows\System32\wbem\WMIC.exe"; - - private static CpuInfo? Load() - { - if (RuntimeInformation.IsWindows()) - { - const string argList = $"{WmicCpuInfoKeyNames.Name}, {WmicCpuInfoKeyNames.NumberOfCores}, " + - $"{WmicCpuInfoKeyNames.NumberOfLogicalProcessors}, {WmicCpuInfoKeyNames.MaxClockSpeed}"; - string wmicPath = File.Exists(DefaultWmicPath) ? DefaultWmicPath : "wmic"; - string content = ProcessHelper.RunAndReadOutput(wmicPath, $"cpu get {argList} /Format:List"); - return WmicCpuInfoParser.ParseOutput(content); - } - return null; - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/macOS/MacOsCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/macOS/MacOsCpuInfoDetector.cs new file mode 100644 index 0000000000..40b675b299 --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/macOS/MacOsCpuInfoDetector.cs @@ -0,0 +1,20 @@ +using BenchmarkDotNet.Helpers; + +namespace BenchmarkDotNet.Portability.Cpu.macOS; + +/// +/// CPU information from output of the `sysctl -a` command. +/// MacOSX only. +/// +internal class MacOsCpuInfoDetector : ICpuInfoDetector +{ + public bool IsApplicable() => RuntimeInformation.IsMacOS(); + + public CpuInfo? Detect() + { + if (!IsApplicable()) return null; + + string sysctlOutput = ProcessHelper.RunAndReadOutput("sysctl", "-a"); + return SysctlCpuInfoParser.Parse(sysctlOutput); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/macOS/SysctlCpuInfoParser.cs b/src/BenchmarkDotNet/Portability/Cpu/macOS/SysctlCpuInfoParser.cs new file mode 100644 index 0000000000..f29afc1c8e --- /dev/null +++ b/src/BenchmarkDotNet/Portability/Cpu/macOS/SysctlCpuInfoParser.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Helpers; + +namespace BenchmarkDotNet.Portability.Cpu.macOS; + +internal static class SysctlCpuInfoParser +{ + private static class Sysctl + { + internal const string ProcessorName = "machdep.cpu.brand_string"; + internal const string PhysicalProcessorCount = "hw.packages"; + internal const string PhysicalCoreCount = "hw.physicalcpu"; + internal const string LogicalCoreCount = "hw.logicalcpu"; + internal const string NominalFrequency = "hw.cpufrequency"; + internal const string MaxFrequency = "hw.cpufrequency_max"; + } + + /// Output of `sysctl -a` + [SuppressMessage("ReSharper", "StringLiteralTypo")] + internal static CpuInfo Parse(string? sysctlOutput) + { + var sysctl = SectionsHelper.ParseSection(sysctlOutput, ':'); + string processorName = sysctl.GetValueOrDefault(Sysctl.ProcessorName); + int? physicalProcessorCount = GetPositiveIntValue(sysctl, Sysctl.PhysicalProcessorCount); + int? physicalCoreCount = GetPositiveIntValue(sysctl, Sysctl.PhysicalCoreCount); + int? logicalCoreCount = GetPositiveIntValue(sysctl, Sysctl.LogicalCoreCount); + long? nominalFrequency = GetPositiveLongValue(sysctl, Sysctl.NominalFrequency); + long? maxFrequency = GetPositiveLongValue(sysctl, Sysctl.MaxFrequency); + return new CpuInfo( + processorName, + physicalProcessorCount, physicalCoreCount, logicalCoreCount, + nominalFrequency, maxFrequency); + } + + private static int? GetPositiveIntValue(Dictionary sysctl, string keyName) + { + if (sysctl.TryGetValue(keyName, out string value) && + int.TryParse(value, out int result) && + result > 0) + return result; + return null; + } + + private static long? GetPositiveLongValue(Dictionary sysctl, string keyName) + { + if (sysctl.TryGetValue(keyName, out string value) && + long.TryParse(value, out long result) && + result > 0) + return result; + return null; + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs index 6064b24dfa..95a3874d87 100644 --- a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs +++ b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs @@ -12,6 +12,9 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Portability.Cpu; +using BenchmarkDotNet.Portability.Cpu.Linux; +using BenchmarkDotNet.Portability.Cpu.macOS; +using BenchmarkDotNet.Portability.Cpu.Windows; using JetBrains.Annotations; using Microsoft.Win32; using static System.Runtime.InteropServices.RuntimeInformation; @@ -238,19 +241,8 @@ private static bool IsUnderWsl() return null; } - internal static CpuInfo GetCpuInfo() - { - if (IsWindows() && IsFullFramework && !IsMono) - return MosCpuInfoProvider.MosCpuInfo.Value; - if (IsWindows()) - return WmicCpuInfoProvider.WmicCpuInfo.Value; - if (IsLinux()) - return ProcCpuInfoProvider.ProcCpuInfo.Value; - if (IsMacOS()) - return SysctlCpuInfoProvider.SysctlCpuInfo.Value; - - return null; - } + private static readonly Lazy LazyCpuInfo = new (CpuInfo.DetectCurrent); + internal static CpuInfo? GetCpuInfo() => LazyCpuInfo.Value; internal static string GetRuntimeVersion() { diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs index 7f950136fb..b1309efdf0 100644 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs @@ -110,7 +110,7 @@ private void AssertZeroResults(Type benchmarkType, IConfig config) .AddDiagnoser(new MemoryDiagnoser(new MemoryDiagnoserConfig(false))) ); - var cpuResolution = RuntimeInformation.GetCpuInfo().MaxFrequency?.ToResolution() ?? FallbackCpuResolutionValue; + var cpuResolution = RuntimeInformation.GetCpuInfo()?.MaxFrequency?.ToResolution() ?? FallbackCpuResolutionValue; var threshold = new NumberValue(cpuResolution.Nanoseconds).ToThreshold(); foreach (var report in summary.Reports) diff --git a/tests/BenchmarkDotNet.Tests/Builders/HostEnvironmentInfoBuilder.cs b/tests/BenchmarkDotNet.Tests/Builders/HostEnvironmentInfoBuilder.cs index d22256f0a9..aadebcab6d 100644 --- a/tests/BenchmarkDotNet.Tests/Builders/HostEnvironmentInfoBuilder.cs +++ b/tests/BenchmarkDotNet.Tests/Builders/HostEnvironmentInfoBuilder.cs @@ -23,13 +23,13 @@ public class HostEnvironmentInfoBuilder private string osVersion = "Microsoft Windows NT 10.0.x.mock"; private string runtimeVersion = "Clr 4.0.x.mock"; - private CpuInfo cpuInfo = new CpuInfo("MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", - physicalProcessorCount: 1, - physicalCoreCount: 4, - logicalCoreCount: 8, - nominalFrequency: Frequency.FromMHz(3100), - maxFrequency: Frequency.FromMHz(3100), - minFrequency: Frequency.FromMHz(3100)); + private CpuInfo cpuInfo = + new ("MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", + physicalProcessorCount: 1, + physicalCoreCount: 4, + logicalCoreCount: 8, + nominalFrequency: Frequency.FromMHz(3100), + maxFrequency: Frequency.FromMHz(3100)); private VirtualMachineHypervisor? virtualMachineHypervisor = HyperV.Default; diff --git a/tests/BenchmarkDotNet.Tests/Environments/ProcessorBrandStringTests.cs b/tests/BenchmarkDotNet.Tests/Environments/ProcessorBrandStringTests.cs index 0bd9fbf608..127c96ca1d 100644 --- a/tests/BenchmarkDotNet.Tests/Environments/ProcessorBrandStringTests.cs +++ b/tests/BenchmarkDotNet.Tests/Environments/ProcessorBrandStringTests.cs @@ -19,8 +19,11 @@ public class ProcessorBrandStringTests [InlineData("Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz", "Intel Core i7-7700 CPU 3.60GHz (Kaby Lake)")] [InlineData("Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz ", "Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R)")] [InlineData("Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz", "Intel Core i7-8700K CPU 3.70GHz (Coffee Lake)")] - public void IntelCoreIsPrettified(string originalName, string prettifiedName) => - Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(new CpuInfo(originalName, nominalFrequency: null))); + public void IntelCoreIsPrettified(string originalName, string prettifiedName) + { + var actual = CpuInfo.FromName(originalName); + Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(actual)); + } [Theory] [InlineData("Intel(R) Pentium(TM) G4560 CPU @ 3.50GHz", "Intel Pentium G4560 CPU 3.50GHz (Max: 3.70GHz)", 3.7)] @@ -31,7 +34,8 @@ public void IntelCoreIsPrettified(string originalName, string prettifiedName) => [InlineData("Intel(R) Core(TM) i7-5775R CPU @ 3.30GHz", "Intel Core i7-5775R CPU 3.30GHz (Max: 3.40GHz) (Broadwell)", 3.4)] public void CoreIsPrettifiedWithDiffFrequencies(string originalName, string prettifiedName, double actualFrequency) { - Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(new CpuInfo(originalName, nominalFrequency: Frequency.FromGHz(actualFrequency)), includeMaxFrequency: true)); + var actual = CpuInfo.FromNameAndFrequency(originalName, Frequency.FromGHz(actualFrequency)); + Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(actual, includeMaxFrequency: true)); } [Theory] @@ -45,7 +49,6 @@ public void AmdIsPrettifiedWithDiffFrequencies(string originalName, string prett physicalCoreCount, logicalCoreCount, Frequency.FromGHz(actualFrequency), - minFrequency: null, maxFrequency: null); Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(cpuInfo, includeMaxFrequency: true)); @@ -65,7 +68,7 @@ public void IntelCoreMicroarchitecture(string modelNumber, string microarchitect [InlineData(null, "Unknown processor")] public void UnknownProcessorDoesNotThrow(string? originalName, string prettifiedName) { - var cpuInfo = new CpuInfo(originalName, nominalFrequency: null); + var cpuInfo = new CpuInfo(originalName, null, null, null, null); Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(cpuInfo, includeMaxFrequency: true)); } diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/CpuInfoFormatterTests.cs b/tests/BenchmarkDotNet.Tests/Portability/Cpu/CpuInfoFormatterTests.cs index 83d14685a4..1fd7b14545 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/CpuInfoFormatterTests.cs +++ b/tests/BenchmarkDotNet.Tests/Portability/Cpu/CpuInfoFormatterTests.cs @@ -5,27 +5,26 @@ using VerifyXunit; using Xunit; -namespace BenchmarkDotNet.Tests.Portability.Cpu +namespace BenchmarkDotNet.Tests.Portability.Cpu; + +[Collection("VerifyTests")] +[UsesVerify] +public class CpuInfoFormatterTests { - [Collection("VerifyTests")] - [UsesVerify] - public class CpuInfoFormatterTests + [Fact] + public Task FormatTest() { - [Fact] - public Task FormatTest() + var captions = new StringBuilder(); + foreach (string? processorName in new[] { null, "", "Intel" }) + foreach (int? physicalProcessorCount in new int?[] { null, 0, 1, 2 }) + foreach (int? physicalCoreCount in new int?[] { null, 0, 1, 2 }) + foreach (int? logicalCoreCount in new int?[] { null, 0, 1, 2 }) { - var captions = new StringBuilder(); - foreach (var processorName in new[] { null, "", "Intel" }) - foreach (var physicalProcessorCount in new int?[] { null, 0, 1, 2 }) - foreach (var physicalCoreCount in new int?[] { null, 0, 1, 2 }) - foreach (var logicalCoreCount in new int?[] { null, 0, 1, 2 }) - { - var mockCpuInfo = new CpuInfo(processorName, physicalProcessorCount, physicalCoreCount, logicalCoreCount, null, null, null); - captions.AppendLine(CpuInfoFormatter.Format(mockCpuInfo)); - } - - var settings = VerifySettingsFactory.Create(); - return Verifier.Verify(captions.ToString(), settings); + var mockCpuInfo = new CpuInfo(processorName, physicalProcessorCount, physicalCoreCount, logicalCoreCount, null, null); + captions.AppendLine(CpuInfoFormatter.Format(mockCpuInfo)); } + + var settings = VerifySettingsFactory.Create(); + return Verifier.Verify(captions.ToString(), settings); } -} +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs b/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs new file mode 100644 index 0000000000..a3b6f2b3ce --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs @@ -0,0 +1,111 @@ +using BenchmarkDotNet.Portability.Cpu; +using BenchmarkDotNet.Portability.Cpu.Linux; +using Xunit; +using Xunit.Abstractions; +using static Perfolizer.Horology.Frequency; + +namespace BenchmarkDotNet.Tests.Portability.Cpu; + +// ReSharper disable StringLiteralTypo +public class LinuxCpuInfoParserTests(ITestOutputHelper output) +{ + private ITestOutputHelper Output { get; } = output; + + [Fact] + public void EmptyTest() + { + var actual = LinuxCpuInfoParser.Parse("", ""); + var expected = CpuInfo.Empty; + Output.AssertEqual(expected, actual); + } + + [Fact] + public void MalformedTest() + { + var actual = LinuxCpuInfoParser.Parse("malformedkey: malformedvalue\n\nmalformedkey2: malformedvalue2", null); + var expected = CpuInfo.Empty; + Output.AssertEqual(expected, actual); + } + + [Fact] + public void TwoProcessorWithDifferentCoresCountTest() + { + string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoProcessorWithDifferentCoresCount.txt"); + var actual = LinuxCpuInfoParser.Parse(cpuInfo, null); + var expected = new CpuInfo( + "Unknown processor with 2 cores and hyper threading, Unknown processor with 4 cores", + 2, 6, 8, 2.5 * GHz, 2.5 * GHz); + Output.AssertEqual(expected, actual); + } + + + [Fact] + public void RealOneProcessorTwoCoresTest() + { + string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoRealOneProcessorTwoCores.txt"); + var actual = LinuxCpuInfoParser.Parse(cpuInfo, null); + var expected = new CpuInfo( + "Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz", + 1, 2, 4, 2.3 * GHz, 2.3 * GHz); + Output.AssertEqual(expected, actual); + } + + [Fact] + public void RealOneProcessorFourCoresTest() + { + string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoRealOneProcessorFourCores.txt"); + var actual = LinuxCpuInfoParser.Parse(cpuInfo, null); + var expected = new CpuInfo( + "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", + 1, 4, 8, 2.5 * GHz, 2.5 * GHz); + Output.AssertEqual(expected, actual); + } + + // https://github.com/dotnet/BenchmarkDotNet/issues/2577 + [Fact] + public void Issue2577Test() + { + const string cpuInfo = + """ + processor : 0 + BogoMIPS : 50.00 + Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp + CPU implementer : 0x41 + CPU architecture: 8 + CPU variant : 0x3 + CPU part : 0xd0c + CPU revision : 1 + """; + const string lscpu = + """ + Architecture: aarch64 + CPU op-mode(s): 32-bit, 64-bit + Byte Order: Little Endian + CPU(s): 16 + On-line CPU(s) list: 0-15 + Vendor ID: ARM + Model name: Neoverse-N1 + Model: 1 + Thread(s) per core: 1 + Core(s) per socket: 16 + Socket(s): 1 + Stepping: r3p1 + BogoMIPS: 50.00 + Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp + """; + var actual = LinuxCpuInfoParser.Parse(cpuInfo, lscpu); + var expected = new CpuInfo("Neoverse-N1", null, 16, null, null, null); + Output.AssertEqual(expected, actual); + } + + [Theory] + [InlineData("Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", 2.50)] + [InlineData("Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz", 2.30)] + [InlineData("Unknown processor with 2 cores and hyper threading, Unknown processor with 4 cores", 0)] + [InlineData("Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz", 3.30)] + public void ParseFrequencyFromBrandStringTests(string brandString, double expectedGHz) + { + var frequency = LinuxCpuInfoParser.ParseFrequencyFromBrandString(brandString) ?? Zero; + Assert.Equal(FromGHz(expectedGHz), frequency); + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/ProcCpuInfoParserTests.cs b/tests/BenchmarkDotNet.Tests/Portability/Cpu/ProcCpuInfoParserTests.cs deleted file mode 100644 index 6d2faa423e..0000000000 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/ProcCpuInfoParserTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -using BenchmarkDotNet.Portability.Cpu; -using Perfolizer.Horology; -using Xunit; - -namespace BenchmarkDotNet.Tests.Portability.Cpu -{ - public class ProcCpuInfoParserTests - { - [Fact] - public void EmptyTest() - { - var parser = ProcCpuInfoParser.ParseOutput(string.Empty); - Assert.Null(parser.ProcessorName); - Assert.Null(parser.PhysicalProcessorCount); - Assert.Null(parser.PhysicalCoreCount); - Assert.Null(parser.LogicalCoreCount); - Assert.Null(parser.NominalFrequency); - - Assert.Null(parser.MaxFrequency); - Assert.Null(parser.MinFrequency); - } - - [Fact] - public void MalformedTest() - { - var parser = ProcCpuInfoParser.ParseOutput("malformedkey: malformedvalue\n\nmalformedkey2: malformedvalue2"); - Assert.Null(parser.ProcessorName); - Assert.Null(parser.PhysicalProcessorCount); - Assert.Null(parser.PhysicalCoreCount); - Assert.Null(parser.LogicalCoreCount); - Assert.Null(parser.NominalFrequency); - Assert.Null(parser.MaxFrequency); - Assert.Null(parser.MinFrequency); - } - - [Fact] - public void TwoProcessorWithDifferentCoresCountTest() - { - string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoProcessorWithDifferentCoresCount.txt"); - var parser = ProcCpuInfoParser.ParseOutput(cpuInfo); - Assert.Equal("Unknown processor with 2 cores and hyper threading, Unknown processor with 4 cores", parser.ProcessorName); - Assert.Equal(2, parser.PhysicalProcessorCount); - Assert.Equal(6, parser.PhysicalCoreCount); - Assert.Equal(8, parser.LogicalCoreCount); - Assert.Null(parser.NominalFrequency); - Assert.Equal(0.8 * Frequency.GHz, parser.MinFrequency); - Assert.Equal(2.5 * Frequency.GHz, parser.MaxFrequency); - } - - - [Fact] - public void RealOneProcessorTwoCoresTest() - { - string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoRealOneProcessorTwoCores.txt"); - var parser = ProcCpuInfoParser.ParseOutput(cpuInfo); - Assert.Equal("Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz", parser.ProcessorName); - Assert.Equal(1, parser.PhysicalProcessorCount); - Assert.Equal(2, parser.PhysicalCoreCount); - Assert.Equal(4, parser.LogicalCoreCount); - Assert.Equal(2.3 * Frequency.GHz, parser.NominalFrequency); - Assert.Equal(0.8 * Frequency.GHz, parser.MinFrequency); - Assert.Equal(2.3 * Frequency.GHz, parser.MaxFrequency); - } - - [Fact] - public void RealOneProcessorFourCoresTest() - { - string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoRealOneProcessorFourCores.txt"); - var parser = ProcCpuInfoParser.ParseOutput(cpuInfo); - Assert.Equal("Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", parser.ProcessorName); - Assert.Equal(1, parser.PhysicalProcessorCount); - Assert.Equal(4, parser.PhysicalCoreCount); - Assert.Equal(8, parser.LogicalCoreCount); - Assert.Equal(2.5 * Frequency.GHz, parser.NominalFrequency); - Assert.Equal(0.8 * Frequency.GHz, parser.MinFrequency); - Assert.Equal(2.5 * Frequency.GHz, parser.MaxFrequency); - } - - [Theory] - [InlineData("Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", 2.50)] - [InlineData("Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz", 2.30)] - [InlineData("Unknown processor with 2 cores and hyper threading, Unknown processor with 4 cores", 0)] - [InlineData("Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz", 3.30)] - public void ParseFrequencyFromBrandStringTests(string brandString, double expectedGHz) - { - var frequency = ProcCpuInfoParser.ParseFrequencyFromBrandString(brandString); - Assert.Equal(Frequency.FromGHz(expectedGHz), frequency); - } - } -} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/SysctlCpuInfoParserTests.cs b/tests/BenchmarkDotNet.Tests/Portability/Cpu/SysctlCpuInfoParserTests.cs index 5d2ba82520..2aca9acb89 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/SysctlCpuInfoParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/Portability/Cpu/SysctlCpuInfoParserTests.cs @@ -1,43 +1,40 @@ using BenchmarkDotNet.Portability.Cpu; -using Perfolizer.Horology; +using BenchmarkDotNet.Portability.Cpu.macOS; using Xunit; +using Xunit.Abstractions; +using static Perfolizer.Horology.Frequency; -namespace BenchmarkDotNet.Tests.Portability.Cpu +namespace BenchmarkDotNet.Tests.Portability.Cpu; + +// ReSharper disable StringLiteralTypo +public class SysctlCpuInfoParserTests(ITestOutputHelper output) { - public class SysctlCpuInfoParserTests + private ITestOutputHelper Output { get; } = output; + + [Fact] + public void EmptyTest() { - [Fact] - public void EmptyTest() - { - var parser = SysctlCpuInfoParser.ParseOutput(string.Empty); - Assert.Null(parser.ProcessorName); - Assert.Null(parser.PhysicalProcessorCount); - Assert.Null(parser.PhysicalCoreCount); - Assert.Null(parser.LogicalCoreCount); - Assert.Null(parser.NominalFrequency); - } + var actual = SysctlCpuInfoParser.Parse(string.Empty); + var expected = CpuInfo.Empty; + Output.AssertEqual(expected, actual); + } - [Fact] - public void MalformedTest() - { - var parser = SysctlCpuInfoParser.ParseOutput("malformedkey=malformedvalue\n\nmalformedkey2=malformedvalue2"); - Assert.Null(parser.ProcessorName); - Assert.Null(parser.PhysicalProcessorCount); - Assert.Null(parser.PhysicalCoreCount); - Assert.Null(parser.LogicalCoreCount); - Assert.Null(parser.NominalFrequency); - } + [Fact] + public void MalformedTest() + { + var actual = SysctlCpuInfoParser.Parse("malformedkey=malformedvalue\n\nmalformedkey2=malformedvalue2"); + var expected = CpuInfo.Empty; + Output.AssertEqual(expected, actual); + } - [Fact] - public void RealOneProcessorFourCoresTest() - { - string cpuInfo = TestHelper.ReadTestFile("SysctlRealOneProcessorFourCores.txt"); - var parser = SysctlCpuInfoParser.ParseOutput(cpuInfo); - Assert.Equal("Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz", parser.ProcessorName); - Assert.Equal(1, parser.PhysicalProcessorCount); - Assert.Equal(4, parser.PhysicalCoreCount); - Assert.Equal(8, parser.LogicalCoreCount); - Assert.Equal(2200 * Frequency.MHz, parser.NominalFrequency); - } + [Fact] + public void RealOneProcessorFourCoresTest() + { + string cpuInfo = TestHelper.ReadTestFile("SysctlRealOneProcessorFourCores.txt"); + var actual = SysctlCpuInfoParser.Parse(cpuInfo); + var expected = new CpuInfo( + "Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz", + 1, 4, 8, 2200 * MHz, 2200 * MHz); + Output.AssertEqual(expected, actual); } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestHelper.cs b/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestHelper.cs index e4af5025c0..d95a8b5dfb 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestHelper.cs +++ b/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestHelper.cs @@ -1,20 +1,37 @@ using System.IO; using System.Reflection; +using BenchmarkDotNet.Portability.Cpu; +using JetBrains.Annotations; +using Xunit; +using Xunit.Abstractions; -namespace BenchmarkDotNet.Tests.Portability.Cpu +namespace BenchmarkDotNet.Tests.Portability.Cpu; + +public static class TestHelper { - public static class TestHelper + public static string ReadTestFile(string name) { - public static string ReadTestFile(string name) - { - var assembly = typeof(TestHelper).GetTypeInfo().Assembly; - string resourceName = $"{typeof(TestHelper).Namespace}.TestFiles.{name}"; + var assembly = typeof(TestHelper).GetTypeInfo().Assembly; + string resourceName = $"{typeof(TestHelper).Namespace}.TestFiles.{name}"; + + using var stream = assembly.GetManifestResourceStream(resourceName); + if (stream == null) + throw new FileNotFoundException($"Resource {resourceName} not found in {assembly.FullName}"); - using (var stream = assembly.GetManifestResourceStream(resourceName)) - using (var reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } - } + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + [AssertionMethod] + public static void AssertEqual(this ITestOutputHelper output, CpuInfo expected, CpuInfo actual) + { + output.WriteLine($"Expected : {CpuInfoFormatter.Format(expected)}"); + output.WriteLine($"Actual : {CpuInfoFormatter.Format(actual)}"); + Assert.Equal(expected.ProcessorName, actual.ProcessorName); + Assert.Equal(expected.PhysicalProcessorCount, actual.PhysicalProcessorCount); + Assert.Equal(expected.PhysicalCoreCount, actual.PhysicalCoreCount); + Assert.Equal(expected.LogicalCoreCount, actual.LogicalCoreCount); + Assert.Equal(expected.NominalFrequency, actual.NominalFrequency); + Assert.Equal(expected.MaxFrequency, actual.MaxFrequency); } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/WmicCpuInfoParserTests.cs b/tests/BenchmarkDotNet.Tests/Portability/Cpu/WmicCpuInfoParserTests.cs index b1faa4b521..98f170ed03 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/WmicCpuInfoParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/Portability/Cpu/WmicCpuInfoParserTests.cs @@ -1,37 +1,36 @@ using BenchmarkDotNet.Portability.Cpu; -using Perfolizer.Horology; +using BenchmarkDotNet.Portability.Cpu.Windows; using Xunit; +using Xunit.Abstractions; +using static Perfolizer.Horology.Frequency; -namespace BenchmarkDotNet.Tests.Portability.Cpu +namespace BenchmarkDotNet.Tests.Portability.Cpu; + +// ReSharper disable StringLiteralTypo +public class WmicCpuInfoParserTests(ITestOutputHelper output) { - public class WmicCpuInfoParserTests + private ITestOutputHelper Output { get; } = output; + + [Fact] + public void EmptyTest() { - [Fact] - public void EmptyTest() - { - var parser = WmicCpuInfoParser.ParseOutput(string.Empty); - Assert.Null(parser.ProcessorName); - Assert.Null(parser.PhysicalProcessorCount); - Assert.Null(parser.PhysicalCoreCount); - Assert.Null(parser.LogicalCoreCount); - Assert.Null(parser.NominalFrequency); - } + var actual = WmicCpuInfoParser.Parse(string.Empty); + var expected = CpuInfo.Empty; + Output.AssertEqual(expected, actual); + } - [Fact] - public void MalformedTest() - { - var parser = WmicCpuInfoParser.ParseOutput("malformedkey=malformedvalue\n\nmalformedkey2=malformedvalue2"); - Assert.Null(parser.ProcessorName); - Assert.Null(parser.PhysicalProcessorCount); - Assert.Null(parser.PhysicalCoreCount); - Assert.Null(parser.LogicalCoreCount); - Assert.Null(parser.NominalFrequency); - } + [Fact] + public void MalformedTest() + { + var actual = WmicCpuInfoParser.Parse("malformedkey=malformedvalue\n\nmalformedkey2=malformedvalue2"); + var expected = CpuInfo.Empty; + Output.AssertEqual(expected, actual); + } - [Fact] - public void RealTwoProcessorEightCoresTest() - { - const string cpuInfo = @" + [Fact] + public void RealTwoProcessorEightCoresTest() + { + const string cpuInfo = @" MaxClockSpeed=2400 Name=Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz @@ -45,45 +44,43 @@ public void RealTwoProcessorEightCoresTest() NumberOfLogicalProcessors=16 "; - var parser = WmicCpuInfoParser.ParseOutput(cpuInfo); - Assert.Equal("Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz", parser.ProcessorName); - Assert.Equal(2, parser.PhysicalProcessorCount); - Assert.Equal(16, parser.PhysicalCoreCount); - Assert.Equal(32, parser.LogicalCoreCount); - Assert.Equal(2400 * Frequency.MHz, parser.MaxFrequency); - } + var actual = WmicCpuInfoParser.Parse(cpuInfo); + var expected = new CpuInfo( + "Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz", + 2, 16, 32, 2400 * MHz, 2400 * MHz); + Output.AssertEqual(expected, actual); + } - [Fact] - public void RealTwoProcessorEightCoresWithWmicBugTest() - { - const string cpuInfo = - "\r\r\n" + - "\r\r\n" + - "MaxClockSpeed=3111\r\r\n" + - "Name=Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz\r\r\n" + - "NumberOfCores=8\r\r\n" + - "NumberOfLogicalProcessors=16\r\r\n" + - "\r\r\n" + - "\r\r\n" + - "MaxClockSpeed=3111\r\r\n" + - "Name=Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz\r\r\n" + - "NumberOfCores=8\r\r\n" + - "NumberOfLogicalProcessors=16\r\r\n" + - "\r\r\n" + - "\r\r\n" + - "\r\r\n"; - var parser = WmicCpuInfoParser.ParseOutput(cpuInfo); - Assert.Equal("Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz", parser.ProcessorName); - Assert.Equal(2, parser.PhysicalProcessorCount); - Assert.Equal(16, parser.PhysicalCoreCount); - Assert.Equal(32, parser.LogicalCoreCount); - Assert.Equal(3111 * Frequency.MHz, parser.MaxFrequency); - } + [Fact] + public void RealTwoProcessorEightCoresWithWmicBugTest() + { + const string cpuInfo = + "\r\r\n" + + "\r\r\n" + + "MaxClockSpeed=3111\r\r\n" + + "Name=Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz\r\r\n" + + "NumberOfCores=8\r\r\n" + + "NumberOfLogicalProcessors=16\r\r\n" + + "\r\r\n" + + "\r\r\n" + + "MaxClockSpeed=3111\r\r\n" + + "Name=Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz\r\r\n" + + "NumberOfCores=8\r\r\n" + + "NumberOfLogicalProcessors=16\r\r\n" + + "\r\r\n" + + "\r\r\n" + + "\r\r\n"; + var actual = WmicCpuInfoParser.Parse(cpuInfo); + var expected = new CpuInfo( + "Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz", + 2, 16, 32, 3111 * MHz, 3111 * MHz); + Output.AssertEqual(expected, actual); + } - [Fact] - public void RealOneProcessorFourCoresTest() - { - const string cpuInfo = @" + [Fact] + public void RealOneProcessorFourCoresTest() + { + const string cpuInfo = @" MaxClockSpeed=2500 Name=Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz @@ -92,12 +89,10 @@ public void RealOneProcessorFourCoresTest() "; - var parser = WmicCpuInfoParser.ParseOutput(cpuInfo); - Assert.Equal("Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", parser.ProcessorName); - Assert.Equal(1, parser.PhysicalProcessorCount); - Assert.Equal(4, parser.PhysicalCoreCount); - Assert.Equal(8, parser.LogicalCoreCount); - Assert.Equal(2500 * Frequency.MHz, parser.MaxFrequency); - } + var actual = WmicCpuInfoParser.Parse(cpuInfo); + var expected = new CpuInfo( + "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", + 1, 4, 8, 2500 * MHz, 2500 * MHz); + Output.AssertEqual(expected, actual); } } \ No newline at end of file From 5e9b35abeda9c6cc254eb989902a54ba9d4c1027 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Mon, 26 Aug 2024 15:20:08 +0200 Subject: [PATCH 28/57] Fix lscpu cpu frequency parsing --- BenchmarkDotNet.sln.DotSettings | 1 + .../Cpu/Linux/LinuxCpuInfoParser.cs | 4 +- .../Cpu/LinuxCpuInfoParserTests.cs | 36 + .../Cpu/TestFiles/ryzen9-cpuinfo.txt | 896 ++++++++++++++++++ 4 files changed, 935 insertions(+), 2 deletions(-) create mode 100644 tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ryzen9-cpuinfo.txt diff --git a/BenchmarkDotNet.sln.DotSettings b/BenchmarkDotNet.sln.DotSettings index a2d44733f3..fc698670f8 100644 --- a/BenchmarkDotNet.sln.DotSettings +++ b/BenchmarkDotNet.sln.DotSettings @@ -177,6 +177,7 @@ True True True + True True True True diff --git a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs b/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs index 4533d60fee..6e10ee9f17 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs +++ b/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs @@ -68,8 +68,8 @@ internal static CpuInfo Parse(string? cpuInfo, string? lscpu) string value = lscpuParts[i + 1].Trim(); if (name.EqualsWithIgnoreCase(Lscpu.MaxFrequency) && - Frequency.TryParseMHz(value.Replace(',', '.'), out var maxFrequencyMHz)) // Example: `CPU max MHz: 3200,0000` - maxFrequency = Frequency.FromMHz(maxFrequencyMHz); + Frequency.TryParseMHz(value.Replace(',', '.'), out var maxFrequencyParsed)) // Example: `CPU max MHz: 3200,0000` + maxFrequency = maxFrequencyParsed; if (name.EqualsWithIgnoreCase(Lscpu.ModelName)) processorModelNames.Add(value); diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs b/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs index a3b6f2b3ce..ab9a4ee76c 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs @@ -98,6 +98,42 @@ public void Issue2577Test() Output.AssertEqual(expected, actual); } + [Fact] + public void AmdRyzen9_7950X() + { + string cpuInfo = TestHelper.ReadTestFile("ryzen9-cpuinfo.txt"); + const string lscpu = + """ + Architecture: x86_64 + CPU op-mode(s): 32-bit, 64-bit + Address sizes: 48 bits physical, 48 bits virtual + Byte Order: Little Endian + CPU(s): 32 + On-line CPU(s) list: 0-31 + Vendor ID: AuthenticAMD + Model name: AMD Ryzen 9 7950X 16-Core Processor + CPU family: 25 + Model: 97 + Thread(s) per core: 2 + Core(s) per socket: 16 + Socket(s): 1 + Stepping: 2 + CPU(s) scaling MHz: 41% + CPU max MHz: 5881.0000 + CPU min MHz: 400.0000 + BogoMIPS: 8983.23 + Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl + pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb + bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512 + cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean + flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succo + r smca fsrm flush_l1d + """; + var actual = LinuxCpuInfoParser.Parse(cpuInfo, lscpu); + var expected = new CpuInfo("AMD Ryzen 9 7950X 16-Core Processor", 1, 16, 32, 5881 * MHz, 5881 * MHz); + Output.AssertEqual(expected, actual); + } + [Theory] [InlineData("Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", 2.50)] [InlineData("Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz", 2.30)] diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ryzen9-cpuinfo.txt b/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ryzen9-cpuinfo.txt new file mode 100644 index 0000000000..d942a8d484 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ryzen9-cpuinfo.txt @@ -0,0 +1,896 @@ +processor : 0 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5346.955 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 0 +cpu cores : 16 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 1 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5487.348 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 1 +cpu cores : 16 +apicid : 2 +initial apicid : 2 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 2 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5479.603 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 2 +cpu cores : 16 +apicid : 4 +initial apicid : 4 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 3 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5601.456 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 3 +cpu cores : 16 +apicid : 6 +initial apicid : 6 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 4 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5410.513 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 4 +cpu cores : 16 +apicid : 8 +initial apicid : 8 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 5 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5493.255 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 5 +cpu cores : 16 +apicid : 10 +initial apicid : 10 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 6 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 4422.161 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 6 +cpu cores : 16 +apicid : 12 +initial apicid : 12 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 7 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5505.019 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 7 +cpu cores : 16 +apicid : 14 +initial apicid : 14 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 8 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5414.920 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 8 +cpu cores : 16 +apicid : 16 +initial apicid : 16 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 9 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5060.967 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 9 +cpu cores : 16 +apicid : 18 +initial apicid : 18 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 10 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 3352.555 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 10 +cpu cores : 16 +apicid : 20 +initial apicid : 20 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 11 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5400.772 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 11 +cpu cores : 16 +apicid : 22 +initial apicid : 22 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 12 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5415.333 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 12 +cpu cores : 16 +apicid : 24 +initial apicid : 24 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 13 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 3236.400 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 13 +cpu cores : 16 +apicid : 26 +initial apicid : 26 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 14 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 4519.257 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 14 +cpu cores : 16 +apicid : 28 +initial apicid : 28 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 15 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5414.841 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 15 +cpu cores : 16 +apicid : 30 +initial apicid : 30 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 16 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 4738.471 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 0 +cpu cores : 16 +apicid : 1 +initial apicid : 1 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 17 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5599.000 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 1 +cpu cores : 16 +apicid : 3 +initial apicid : 3 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 18 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5477.067 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 2 +cpu cores : 16 +apicid : 5 +initial apicid : 5 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 19 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5507.543 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 3 +cpu cores : 16 +apicid : 7 +initial apicid : 7 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 20 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 4401.379 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 4 +cpu cores : 16 +apicid : 9 +initial apicid : 9 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 21 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 400.000 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 5 +cpu cores : 16 +apicid : 11 +initial apicid : 11 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 22 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5607.945 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 6 +cpu cores : 16 +apicid : 13 +initial apicid : 13 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 23 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5280.720 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 7 +cpu cores : 16 +apicid : 15 +initial apicid : 15 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 24 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5414.893 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 8 +cpu cores : 16 +apicid : 17 +initial apicid : 17 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 25 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5414.888 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 9 +cpu cores : 16 +apicid : 19 +initial apicid : 19 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 26 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5385.641 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 10 +cpu cores : 16 +apicid : 21 +initial apicid : 21 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 27 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 3130.265 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 11 +cpu cores : 16 +apicid : 23 +initial apicid : 23 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 28 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5400.876 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 12 +cpu cores : 16 +apicid : 25 +initial apicid : 25 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 29 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 4279.794 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 13 +cpu cores : 16 +apicid : 27 +initial apicid : 27 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 30 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 3124.980 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 14 +cpu cores : 16 +apicid : 29 +initial apicid : 29 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + +processor : 31 +vendor_id : AuthenticAMD +cpu family : 25 +model : 97 +model name : AMD Ryzen 9 7950X 16-Core Processor +stepping : 2 +microcode : 0xa601201 +cpu MHz : 5389.561 +cache size : 1024 KB +physical id : 0 +siblings : 32 +core id : 15 +cpu cores : 16 +apicid : 31 +initial apicid : 31 +fpu : yes +fpu_exception : yes +cpuid level : 16 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass srso +bogomips : 8983.23 +TLB size : 3584 4K pages +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] + From a58872b82739b14935217adfa25db209d4419f4f Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Mon, 26 Aug 2024 19:17:58 +0200 Subject: [PATCH 29/57] Make lscpu call language-invariant, fix #2577 --- src/BenchmarkDotNet/Helpers/ProcessHelper.cs | 6 +++++- .../Portability/Cpu/Linux/LinuxCpuInfoDetector.cs | 13 +++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/BenchmarkDotNet/Helpers/ProcessHelper.cs b/src/BenchmarkDotNet/Helpers/ProcessHelper.cs index 1b75c6392f..caa3410b21 100644 --- a/src/BenchmarkDotNet/Helpers/ProcessHelper.cs +++ b/src/BenchmarkDotNet/Helpers/ProcessHelper.cs @@ -12,7 +12,8 @@ internal static class ProcessHelper /// Run external process and return the console output. /// In the case of any exception, null will be returned. /// - internal static string? RunAndReadOutput(string fileName, string arguments = "", ILogger? logger = null) + internal static string? RunAndReadOutput(string fileName, string arguments = "", ILogger? logger = null, + Dictionary? environmentVariables = null) { var processStartInfo = new ProcessStartInfo { @@ -24,6 +25,9 @@ internal static class ProcessHelper RedirectStandardOutput = true, RedirectStandardError = true }; + if (environmentVariables != null) + foreach (var variable in environmentVariables) + processStartInfo.Environment[variable.Key] = variable.Value; using (var process = new Process { StartInfo = processStartInfo }) using (new ConsoleExitHandler(process, logger ?? NullLogger.Instance)) { diff --git a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs index b88b41c6be..8f6641d9d7 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs +++ b/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs @@ -1,4 +1,5 @@ -using BenchmarkDotNet.Helpers; +using System.Collections.Generic; +using BenchmarkDotNet.Helpers; namespace BenchmarkDotNet.Portability.Cpu.Linux; @@ -14,8 +15,16 @@ internal class LinuxCpuInfoDetector : ICpuInfoDetector { if (!IsApplicable()) return null; + // lscpu output respects the system locale, so we should force language invariant environment for correct parsing + var languageInvariantEnvironment = new Dictionary + { + ["LC_ALL"] = "C", + ["LANG"] = "C", + ["LANGUAGE"] = "C" + }; + string cpuInfo = ProcessHelper.RunAndReadOutput("cat", "/proc/cpuinfo") ?? ""; - string lscpu = ProcessHelper.RunAndReadOutput("/bin/bash", "-c \"lscpu\""); + string lscpu = ProcessHelper.RunAndReadOutput("/bin/bash", "-c \"lscpu\"", environmentVariables: languageInvariantEnvironment); return LinuxCpuInfoParser.Parse(cpuInfo, lscpu); } } \ No newline at end of file From ae4bb9ba1b9363f6f0af33bd02a0c96dcd25101c Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Tue, 27 Aug 2024 02:00:42 +0200 Subject: [PATCH 30/57] Bump Perfolizer + Initial Phd adoption 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 --- .../HardwareCounters.cs | 3 +- .../JitDiagnoser.cs | 3 +- .../DotMemoryDiagnoser.cs | 6 +- .../DotTraceDiagnoser.cs | 6 +- .../Analysers/ZeroMeasurementAnalyser.cs | 2 +- .../Exporters/PhdExporterAttribute.cs | 10 + src/BenchmarkDotNet/BenchmarkDotNet.csproj | 3 +- src/BenchmarkDotNet/Configs/DefaultConfig.cs | 3 +- .../Cpu/HardwareIntrinsics.cs | 5 +- .../Detectors/Cpu/ICpuDetector.cs | 12 + .../Cpu/Linux/LinuxCpuDetector.cs} | 10 +- .../Cpu/Linux/LinuxCpuInfoParser.cs | 22 +- .../Cpu/Windows/MosCpuDetector.cs} | 29 +- .../Cpu/Windows/WindowsCpuDetector.cs | 3 + .../Cpu/Windows/WmicCpuDetector.cs} | 10 +- .../Cpu/Windows/WmicCpuInfoKeyNames.cs | 2 +- .../Cpu/Windows/WmicCpuInfoParser.cs | 23 +- .../Cpu/macOS/MacOsCpuDetector.cs} | 10 +- .../Cpu/macOS/SysctlCpuInfoParser.cs | 32 +- src/BenchmarkDotNet/Detectors/CpuDetector.cs | 29 + src/BenchmarkDotNet/Detectors/OsDetector.cs | 178 + .../Diagnosers/DiagnosersLoader.cs | 5 +- .../Diagnosers/PerfCollectProfiler.cs | 3 +- .../Diagnosers/UnresolvedDiagnoser.cs | 5 +- .../Disassemblers/ClrMdV2Disassembler.cs | 9 +- .../Disassemblers/DisassemblyDiagnoser.cs | 9 +- .../Disassemblers/WindowsDisassembler.cs | 3 +- .../Environments/BenchmarkEnvironmentInfo.cs | 2 +- .../Environments/HostEnvironmentInfo.cs | 47 +- .../Environments/OsBrandStringHelper.cs | 278 -- .../ProcessorBrandStringHelper.cs | 154 - .../Environments/Runtimes/ClrRuntime.cs | 3 +- .../Environments/microarchitectures.txt | 183 - .../Exporters/Json/JsonExporterBase.cs | 73 +- ...{SimpleJson.cs => SimpleJsonSerializer.cs} | 8 +- .../Exporters/PhdJsonExporter.cs | 18 + .../Exporters/PhdMdExporter.cs | 24 + .../Exporters/RPlotExporter.cs | 5 +- .../Exporters/Xml/SummaryDto.cs | 15 +- .../Extensions/MathExtensions.cs | 9 + .../Extensions/ProcessExtensions.cs | 7 +- .../Helpers/ArtifactFileNameHelper.cs | 3 +- .../Helpers/ExternalToolsHelper.cs | 3 +- .../Helpers/TaskbarProgress.cs | 3 +- .../Helpers/UserInteractionHelper.cs | 3 +- src/BenchmarkDotNet/Jobs/EnvironmentMode.cs | 18 +- src/BenchmarkDotNet/Jobs/RunMode.cs | 29 +- src/BenchmarkDotNet/Loggers/ConsoleLogger.cs | 3 +- src/BenchmarkDotNet/Phd/BdnBenchmark.cs | 38 + src/BenchmarkDotNet/Phd/BdnEnvironment.cs | 12 + src/BenchmarkDotNet/Phd/BdnExecution.cs | 84 + src/BenchmarkDotNet/Phd/BdnHost.cs | 17 + src/BenchmarkDotNet/Phd/BdnInfo.cs | 11 + src/BenchmarkDotNet/Phd/BdnJob.cs | 8 + src/BenchmarkDotNet/Phd/BdnLifecycle.cs | 57 + src/BenchmarkDotNet/Phd/BdnSchema.cs | 19 + .../Cpu/CompositeCpuInfoDetector.cs | 18 - .../Portability/Cpu/CpuInfo.cs | 33 - .../Portability/Cpu/CpuInfoFormatter.cs | 56 - .../Portability/Cpu/ICpuInfoDetector.cs | 10 - .../Cpu/Windows/WindowsCpuInfoDetector.cs | 3 - .../Portability/RuntimeInformation.cs | 185 +- .../Properties/BenchmarkDotNetInfo.cs | 10 + .../Reports/BenchmarkReport.cs | 57 + src/BenchmarkDotNet/Reports/Summary.cs | 46 +- .../Running/BenchmarkRunnerClean.cs | 3 +- src/BenchmarkDotNet/Running/ConsoleTitler.cs | 3 +- .../Running/PowerManagementApplier.cs | 5 +- .../CsProj/CsProjClassicNetToolchain.cs | 3 +- .../CustomDotNetCliToolchainBuilder.cs | 3 +- .../DotNetCli/DotNetCliCommandExecutor.cs | 3 +- .../Toolchains/GeneratorBase.cs | 5 +- .../InProcess/Emit/InProcessEmitExecutor.cs | 3 +- .../NoEmit/InProcessNoEmitExecutor.cs | 3 +- .../MonoAotLLVM/MonoAotLLVMGenerator.cs | 3 +- .../Toolchains/MonoWasm/WasmToolchain.cs | 3 +- .../Toolchains/NativeAot/Generator.cs | 5 +- .../Toolchains/Roslyn/Generator.cs | 5 +- .../Toolchains/ToolchainExtensions.cs | 5 +- .../ExpectedBenchmarkResultsTests.cs | 5 +- .../BuildTimeoutTests.cs | 3 +- .../ContinuousIntegration.cs | 3 +- .../DisassemblyDiagnoserTests.cs | 13 +- .../DotMemoryTests.cs | 3 +- .../DotTraceTests.cs | 3 +- .../MemoryDiagnoserTests.cs | 3 +- .../NativeAotTests.cs | 3 +- .../ThreadingDiagnoserTests.cs | 3 +- .../Attributes/ParamsAllValuesVerifyTests.cs | 3 +- .../BenchmarkDotNet.Tests.csproj | 22 +- .../Builders/HostEnvironmentInfoBuilder.cs | 31 +- .../Builders/VerifySettingsFactory.cs | 15 - .../Detectors/Cpu/CpuInfoFormatterTests.cs | 39 + .../Cpu/LinuxCpuInfoParserTests.cs | 58 +- .../Cpu/SysctlCpuInfoParserTests.cs | 23 +- ...puInfoProcessorWithDifferentCoresCount.txt | 0 .../ProcCpuInfoRealOneProcessorFourCores.txt | 0 .../ProcCpuInfoRealOneProcessorTwoCores.txt | 0 .../SysctlRealOneProcessorFourCores.txt | 0 .../Cpu/TestFiles/ryzen9-cpuinfo.txt | 0 .../Cpu/TestHelper.cs | 15 +- ...InfoFormatterTests.FormatTest.verified.txt | 0 .../Cpu/WmicCpuInfoParserTests.cs | 46 +- .../Environments/HostEnvironmentInfoTests.cs | 5 +- .../Environments/OsBrandStringTests.cs | 70 - .../Environments/ProcessorBrandStringTests.cs | 76 - .../Exporters/CommonExporterVerifyTests.cs | 3 +- .../Exporters/MarkdownExporterVerifyTests.cs | 31 +- .../Infra/TestOutputPresenter.cs | 9 + .../Infra/VerifyHelper.cs | 16 + .../LightJsonSerializerTests.cs | 56 + .../Mocks/MockFactory.cs | 7 +- .../Phd/Infra/PhdMock.cs | 13 + .../Phd/Infra/PhdTestExtensions.cs | 22 + tests/BenchmarkDotNet.Tests/Phd/PhdTests.cs | 173 + ...hd.PhdIndexTest_key=default01.verified.txt | 190 + ...hd.PhdIndexTest_key=default02.verified.txt | 391 ++ ...hd.PhdIndexTest_key=default03.verified.txt | 218 + ...hd.PhdIndexTest_key=default04.verified.txt | 404 ++ ...hd.PhdIndexTest_key=default05.verified.txt | 3873 +++++++++++++++++ ...Phd.PhdIndexTest_key=params01.verified.txt | 31 + .../Phd.PhdIndexTest_key=sort01.verified.txt | 22 + ...hd.PhdTableTest_key=default01.verified.txt | 123 + ...hd.PhdTableTest_key=default02.verified.txt | 189 + ...hd.PhdTableTest_key=default03.verified.txt | 133 + ...hd.PhdTableTest_key=default04.verified.txt | 194 + ...hd.PhdTableTest_key=default05.verified.txt | 1276 ++++++ ...Phd.PhdTableTest_key=params01.verified.txt | 66 + .../Phd.PhdTableTest_key=sort01.verified.txt | 53 + .../Portability/Cpu/CpuInfoFormatterTests.cs | 30 - .../RuntimeVersionDetectionTests.cs | 3 +- .../BenchmarkDotNet.Tests/SimpleJsonTests.cs | 2 +- .../ConfigCompatibilityValidatorTests.cs | 3 +- 133 files changed, 8619 insertions(+), 1393 deletions(-) create mode 100644 src/BenchmarkDotNet/Attributes/Exporters/PhdExporterAttribute.cs rename src/BenchmarkDotNet/{Portability => Detectors}/Cpu/HardwareIntrinsics.cs (99%) create mode 100644 src/BenchmarkDotNet/Detectors/Cpu/ICpuDetector.cs rename src/BenchmarkDotNet/{Portability/Cpu/Linux/LinuxCpuInfoDetector.cs => Detectors/Cpu/Linux/LinuxCpuDetector.cs} (77%) rename src/BenchmarkDotNet/{Portability => Detectors}/Cpu/Linux/LinuxCpuInfoParser.cs (87%) rename src/BenchmarkDotNet/{Portability/Cpu/Windows/MosCpuInfoDetector.cs => Detectors/Cpu/Windows/MosCpuDetector.cs} (68%) create mode 100644 src/BenchmarkDotNet/Detectors/Cpu/Windows/WindowsCpuDetector.cs rename src/BenchmarkDotNet/{Portability/Cpu/Windows/WmicCpuInfoDetector.cs => Detectors/Cpu/Windows/WmicCpuDetector.cs} (78%) rename src/BenchmarkDotNet/{Portability => Detectors}/Cpu/Windows/WmicCpuInfoKeyNames.cs (84%) rename src/BenchmarkDotNet/{Portability => Detectors}/Cpu/Windows/WmicCpuInfoParser.cs (74%) rename src/BenchmarkDotNet/{Portability/Cpu/macOS/MacOsCpuInfoDetector.cs => Detectors/Cpu/macOS/MacOsCpuDetector.cs} (58%) rename src/BenchmarkDotNet/{Portability => Detectors}/Cpu/macOS/SysctlCpuInfoParser.cs (55%) create mode 100644 src/BenchmarkDotNet/Detectors/CpuDetector.cs create mode 100644 src/BenchmarkDotNet/Detectors/OsDetector.cs delete mode 100644 src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs delete mode 100644 src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs delete mode 100644 src/BenchmarkDotNet/Environments/microarchitectures.txt rename src/BenchmarkDotNet/Exporters/Json/{SimpleJson.cs => SimpleJsonSerializer.cs} (99%) create mode 100644 src/BenchmarkDotNet/Exporters/PhdJsonExporter.cs create mode 100644 src/BenchmarkDotNet/Exporters/PhdMdExporter.cs create mode 100644 src/BenchmarkDotNet/Extensions/MathExtensions.cs create mode 100644 src/BenchmarkDotNet/Phd/BdnBenchmark.cs create mode 100644 src/BenchmarkDotNet/Phd/BdnEnvironment.cs create mode 100644 src/BenchmarkDotNet/Phd/BdnExecution.cs create mode 100644 src/BenchmarkDotNet/Phd/BdnHost.cs create mode 100644 src/BenchmarkDotNet/Phd/BdnInfo.cs create mode 100644 src/BenchmarkDotNet/Phd/BdnJob.cs create mode 100644 src/BenchmarkDotNet/Phd/BdnLifecycle.cs create mode 100644 src/BenchmarkDotNet/Phd/BdnSchema.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/CompositeCpuInfoDetector.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/CpuInfo.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/CpuInfoFormatter.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/ICpuInfoDetector.cs delete mode 100644 src/BenchmarkDotNet/Portability/Cpu/Windows/WindowsCpuInfoDetector.cs delete mode 100644 tests/BenchmarkDotNet.Tests/Builders/VerifySettingsFactory.cs create mode 100644 tests/BenchmarkDotNet.Tests/Detectors/Cpu/CpuInfoFormatterTests.cs rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/LinuxCpuInfoParserTests.cs (78%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/SysctlCpuInfoParserTests.cs (61%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/TestFiles/ProcCpuInfoProcessorWithDifferentCoresCount.txt (100%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/TestFiles/ProcCpuInfoRealOneProcessorFourCores.txt (100%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/TestFiles/ProcCpuInfoRealOneProcessorTwoCores.txt (100%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/TestFiles/SysctlRealOneProcessorFourCores.txt (100%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/TestFiles/ryzen9-cpuinfo.txt (100%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/TestHelper.cs (71%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/VerifiedFiles/CpuInfoFormatterTests.FormatTest.verified.txt (100%) rename tests/BenchmarkDotNet.Tests/{Portability => Detectors}/Cpu/WmicCpuInfoParserTests.cs (64%) delete mode 100644 tests/BenchmarkDotNet.Tests/Environments/OsBrandStringTests.cs delete mode 100644 tests/BenchmarkDotNet.Tests/Environments/ProcessorBrandStringTests.cs create mode 100644 tests/BenchmarkDotNet.Tests/Infra/TestOutputPresenter.cs create mode 100644 tests/BenchmarkDotNet.Tests/Infra/VerifyHelper.cs create mode 100644 tests/BenchmarkDotNet.Tests/LightJsonSerializerTests.cs create mode 100644 tests/BenchmarkDotNet.Tests/Phd/Infra/PhdMock.cs create mode 100644 tests/BenchmarkDotNet.Tests/Phd/Infra/PhdTestExtensions.cs create mode 100644 tests/BenchmarkDotNet.Tests/Phd/PhdTests.cs create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default01.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default02.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default03.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default04.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default05.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=params01.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=sort01.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default01.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default02.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default03.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default04.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default05.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=params01.verified.txt create mode 100644 tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=sort01.verified.txt delete mode 100644 tests/BenchmarkDotNet.Tests/Portability/Cpu/CpuInfoFormatterTests.cs diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/HardwareCounters.cs b/src/BenchmarkDotNet.Diagnostics.Windows/HardwareCounters.cs index 8b3c46b4d5..d159e72a65 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/HardwareCounters.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/HardwareCounters.cs @@ -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; @@ -31,7 +32,7 @@ private static readonly Dictionary EtwTranslations public static IEnumerable 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; diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs index b189af0acb..0d06faf75b 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Loggers; @@ -33,7 +34,7 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) public IEnumerable Validate(ValidationParameters validationParameters) { - if (!RuntimeInformation.IsWindows()) + if (!OsDetector.IsWindows()) { yield return new ValidationError(true, $"{GetType().Name} is supported only on Windows"); } diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs index 24e62feb31..5639aa0580 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs @@ -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; @@ -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"); } diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs index 2aada424b7..da06c83472 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs @@ -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; @@ -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"); } diff --git a/src/BenchmarkDotNet/Analysers/ZeroMeasurementAnalyser.cs b/src/BenchmarkDotNet/Analysers/ZeroMeasurementAnalyser.cs index 9269b3ebf0..4d83a5d154 100644 --- a/src/BenchmarkDotNet/Analysers/ZeroMeasurementAnalyser.cs +++ b/src/BenchmarkDotNet/Analysers/ZeroMeasurementAnalyser.cs @@ -19,7 +19,7 @@ private ZeroMeasurementAnalyser() { } protected override IEnumerable 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(); diff --git a/src/BenchmarkDotNet/Attributes/Exporters/PhdExporterAttribute.cs b/src/BenchmarkDotNet/Attributes/Exporters/PhdExporterAttribute.cs new file mode 100644 index 0000000000..9169c82084 --- /dev/null +++ b/src/BenchmarkDotNet/Attributes/Exporters/PhdExporterAttribute.cs @@ -0,0 +1,10 @@ +using System; +using BenchmarkDotNet.Exporters; + +namespace BenchmarkDotNet.Attributes; + +/// +/// IMPORTANT: Not fully implemented yet +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] +public class PhdExporterAttribute() : ExporterConfigBaseAttribute(new PhdJsonExporter(), new PhdMdExporter()); \ No newline at end of file diff --git a/src/BenchmarkDotNet/BenchmarkDotNet.csproj b/src/BenchmarkDotNet/BenchmarkDotNet.csproj index 4bdceb0f8a..547c50b6e5 100644 --- a/src/BenchmarkDotNet/BenchmarkDotNet.csproj +++ b/src/BenchmarkDotNet/BenchmarkDotNet.csproj @@ -13,14 +13,13 @@ - - + diff --git a/src/BenchmarkDotNet/Configs/DefaultConfig.cs b/src/BenchmarkDotNet/Configs/DefaultConfig.cs index 2323d4fdc4..b70333cc1d 100644 --- a/src/BenchmarkDotNet/Configs/DefaultConfig.cs +++ b/src/BenchmarkDotNet/Configs/DefaultConfig.cs @@ -4,6 +4,7 @@ using System.IO; using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.EventProcessors; using BenchmarkDotNet.Exporters; @@ -90,7 +91,7 @@ public string ArtifactsPath { get { - var root = RuntimeInformation.IsAndroid() ? + var root = OsDetector.IsAndroid() ? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) : Directory.GetCurrentDirectory(); return Path.Combine(root, "BenchmarkDotNet.Artifacts"); diff --git a/src/BenchmarkDotNet/Portability/Cpu/HardwareIntrinsics.cs b/src/BenchmarkDotNet/Detectors/Cpu/HardwareIntrinsics.cs similarity index 99% rename from src/BenchmarkDotNet/Portability/Cpu/HardwareIntrinsics.cs rename to src/BenchmarkDotNet/Detectors/Cpu/HardwareIntrinsics.cs index be1d40c9ec..a8c7655272 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/HardwareIntrinsics.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/HardwareIntrinsics.cs @@ -2,15 +2,14 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Numerics; -using BenchmarkDotNet.Environments; using System.Text; - +using BenchmarkDotNet.Environments; #if NET6_0_OR_GREATER using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.Arm; #endif -namespace BenchmarkDotNet.Portability.Cpu +namespace BenchmarkDotNet.Detectors.Cpu { internal static class HardwareIntrinsics { diff --git a/src/BenchmarkDotNet/Detectors/Cpu/ICpuDetector.cs b/src/BenchmarkDotNet/Detectors/Cpu/ICpuDetector.cs new file mode 100644 index 0000000000..6c0fcf683c --- /dev/null +++ b/src/BenchmarkDotNet/Detectors/Cpu/ICpuDetector.cs @@ -0,0 +1,12 @@ +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Detectors.Cpu; + +/// +/// Loads the for the current hardware +/// +public interface ICpuDetector +{ + bool IsApplicable(); + PhdCpu? Detect(); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs b/src/BenchmarkDotNet/Detectors/Cpu/Linux/LinuxCpuDetector.cs similarity index 77% rename from src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs rename to src/BenchmarkDotNet/Detectors/Cpu/Linux/LinuxCpuDetector.cs index 8f6641d9d7..fd3c98d049 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoDetector.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/Linux/LinuxCpuDetector.cs @@ -1,17 +1,19 @@ using System.Collections.Generic; using BenchmarkDotNet.Helpers; +using BenchmarkDotNet.Portability; +using Perfolizer.Phd.Dto; -namespace BenchmarkDotNet.Portability.Cpu.Linux; +namespace BenchmarkDotNet.Detectors.Cpu.Linux; /// /// CPU information from output of the `cat /proc/cpuinfo` and `lscpu` command. /// Linux only. /// -internal class LinuxCpuInfoDetector : ICpuInfoDetector +internal class LinuxCpuDetector : ICpuDetector { - public bool IsApplicable() => RuntimeInformation.IsLinux(); + public bool IsApplicable() => OsDetector.IsLinux(); - public CpuInfo? Detect() + public PhdCpu? Detect() { if (!IsApplicable()) return null; diff --git a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs b/src/BenchmarkDotNet/Detectors/Cpu/Linux/LinuxCpuInfoParser.cs similarity index 87% rename from src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs rename to src/BenchmarkDotNet/Detectors/Cpu/Linux/LinuxCpuInfoParser.cs index 6e10ee9f17..b1e554a6fd 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/Linux/LinuxCpuInfoParser.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/Linux/LinuxCpuInfoParser.cs @@ -3,9 +3,11 @@ using System.Text.RegularExpressions; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; +using BenchmarkDotNet.Portability; using Perfolizer.Horology; +using Perfolizer.Phd.Dto; -namespace BenchmarkDotNet.Portability.Cpu.Linux; +namespace BenchmarkDotNet.Detectors.Cpu.Linux; internal static class LinuxCpuInfoParser { @@ -26,7 +28,7 @@ private static class Lscpu /// Output of `cat /proc/cpuinfo` /// Output of `lscpu` - internal static CpuInfo Parse(string? cpuInfo, string? lscpu) + internal static PhdCpu Parse(string? cpuInfo, string? lscpu) { var processorModelNames = new HashSet(); var processorsToPhysicalCoreCount = new Dictionary(); @@ -87,13 +89,15 @@ internal static CpuInfo Parse(string? cpuInfo, string? lscpu) string processorName = processorModelNames.Count > 0 ? string.Join(", ", processorModelNames) : null; int? physicalProcessorCount = processorsToPhysicalCoreCount.Count > 0 ? processorsToPhysicalCoreCount.Count : null; int? physicalCoreCount = processorsToPhysicalCoreCount.Count > 0 ? processorsToPhysicalCoreCount.Values.Sum() : coresPerSocket; - return new CpuInfo( - processorName, - physicalProcessorCount, - physicalCoreCount, - logicalCoreCount > 0 ? logicalCoreCount : null, - nominalFrequency, - maxFrequency); + return new PhdCpu + { + ProcessorName = processorName, + PhysicalProcessorCount = physicalProcessorCount, + PhysicalCoreCount = physicalCoreCount, + LogicalCoreCount = logicalCoreCount > 0 ? logicalCoreCount : null, + NominalFrequencyHz = nominalFrequency?.Hertz.RoundToLong(), + MaxFrequencyHz = maxFrequency?.Hertz.RoundToLong() + }; } internal static Frequency? ParseFrequencyFromBrandString(string brandString) diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/MosCpuInfoDetector.cs b/src/BenchmarkDotNet/Detectors/Cpu/Windows/MosCpuDetector.cs similarity index 68% rename from src/BenchmarkDotNet/Portability/Cpu/Windows/MosCpuInfoDetector.cs rename to src/BenchmarkDotNet/Detectors/Cpu/Windows/MosCpuDetector.cs index 5a1c6f2cdb..8c0b2718eb 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/Windows/MosCpuInfoDetector.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/Windows/MosCpuDetector.cs @@ -1,24 +1,26 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Management; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Portability; using Perfolizer.Horology; +using Perfolizer.Phd.Dto; -namespace BenchmarkDotNet.Portability.Cpu.Windows; +namespace BenchmarkDotNet.Detectors.Cpu.Windows; -internal class MosCpuInfoDetector : ICpuInfoDetector +internal class MosCpuDetector : ICpuDetector { #if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - public bool IsApplicable() => RuntimeInformation.IsWindows() && + public bool IsApplicable() => OsDetector.IsWindows() && RuntimeInformation.IsFullFramework && !RuntimeInformation.IsMono; #if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - public CpuInfo? Detect() + public PhdCpu? Detect() { if (!IsApplicable()) return null; @@ -49,11 +51,14 @@ public bool IsApplicable() => RuntimeInformation.IsWindows() && ? Frequency.FromMHz(sumMaxFrequency * 1.0 / processorsCount) : null; - return new CpuInfo( - processorName, - GetCount(processorsCount), GetCount(physicalCoreCount), GetCount(logicalCoreCount), - maxFrequency, maxFrequency); - - int? GetCount(int count) => count > 0 ? count : null; + return new PhdCpu + { + ProcessorName = processorName, + PhysicalProcessorCount = processorsCount > 0 ? processorsCount : null, + PhysicalCoreCount = physicalCoreCount > 0 ? physicalCoreCount : null, + LogicalCoreCount = logicalCoreCount > 0 ? logicalCoreCount : null, + NominalFrequencyHz = maxFrequency?.Hertz.RoundToLong(), + MaxFrequencyHz = maxFrequency?.Hertz.RoundToLong() + }; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Detectors/Cpu/Windows/WindowsCpuDetector.cs b/src/BenchmarkDotNet/Detectors/Cpu/Windows/WindowsCpuDetector.cs new file mode 100644 index 0000000000..9969a1bca0 --- /dev/null +++ b/src/BenchmarkDotNet/Detectors/Cpu/Windows/WindowsCpuDetector.cs @@ -0,0 +1,3 @@ +namespace BenchmarkDotNet.Detectors.Cpu.Windows; + +internal class WindowsCpuDetector() : CpuDetector(new MosCpuDetector(), new WmicCpuDetector()); \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoDetector.cs b/src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuDetector.cs similarity index 78% rename from src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoDetector.cs rename to src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuDetector.cs index 6dbf1fad8d..e7ae5401bc 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoDetector.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuDetector.cs @@ -1,19 +1,21 @@ using System.IO; using BenchmarkDotNet.Helpers; +using BenchmarkDotNet.Portability; +using Perfolizer.Phd.Dto; -namespace BenchmarkDotNet.Portability.Cpu.Windows; +namespace BenchmarkDotNet.Detectors.Cpu.Windows; /// /// CPU information from output of the `wmic cpu get Name, NumberOfCores, NumberOfLogicalProcessors /Format:List` command. /// Windows only. /// -internal class WmicCpuInfoDetector : ICpuInfoDetector +internal class WmicCpuDetector : ICpuDetector { private const string DefaultWmicPath = @"C:\Windows\System32\wbem\WMIC.exe"; - public bool IsApplicable() => RuntimeInformation.IsWindows(); + public bool IsApplicable() => OsDetector.IsWindows(); - public CpuInfo? Detect() + public PhdCpu? Detect() { if (!IsApplicable()) return null; diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoKeyNames.cs b/src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuInfoKeyNames.cs similarity index 84% rename from src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoKeyNames.cs rename to src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuInfoKeyNames.cs index 1e08cc0bfb..65d8ecf7d8 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoKeyNames.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuInfoKeyNames.cs @@ -1,4 +1,4 @@ -namespace BenchmarkDotNet.Portability.Cpu.Windows; +namespace BenchmarkDotNet.Detectors.Cpu.Windows; internal static class WmicCpuInfoKeyNames { diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoParser.cs b/src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuInfoParser.cs similarity index 74% rename from src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoParser.cs rename to src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuInfoParser.cs index 47ede5f0cc..af6ed8c12e 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/Windows/WmicCpuInfoParser.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/Windows/WmicCpuInfoParser.cs @@ -1,16 +1,18 @@ using System.Collections.Generic; +using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using Perfolizer.Horology; +using Perfolizer.Phd.Dto; -namespace BenchmarkDotNet.Portability.Cpu.Windows; +namespace BenchmarkDotNet.Detectors.Cpu.Windows; internal static class WmicCpuInfoParser { /// - /// Parses wmic output and returns + /// Parses wmic output and returns /// /// Output of `wmic cpu get Name, NumberOfCores, NumberOfLogicalProcessors /Format:List` - internal static CpuInfo Parse(string? wmicOutput) + internal static PhdCpu Parse(string? wmicOutput) { var processorModelNames = new HashSet(); int physicalCoreCount = 0; @@ -50,11 +52,14 @@ internal static CpuInfo Parse(string? wmicOutput) ? Frequency.FromMHz(sumMaxFrequency / processorsCount) : null; - return new CpuInfo( - processorName, - GetCount(processorsCount), GetCount(physicalCoreCount), GetCount(logicalCoreCount), - maxFrequency, maxFrequency); - - int? GetCount(int count) => count > 0 ? count : null; + return new PhdCpu + { + ProcessorName = processorName, + PhysicalProcessorCount = processorsCount > 0 ? processorsCount : null, + PhysicalCoreCount = physicalCoreCount > 0 ? physicalCoreCount : null, + LogicalCoreCount = logicalCoreCount > 0 ? logicalCoreCount : null, + NominalFrequencyHz = maxFrequency?.Hertz.RoundToLong(), + MaxFrequencyHz = maxFrequency?.Hertz.RoundToLong() + }; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/macOS/MacOsCpuInfoDetector.cs b/src/BenchmarkDotNet/Detectors/Cpu/macOS/MacOsCpuDetector.cs similarity index 58% rename from src/BenchmarkDotNet/Portability/Cpu/macOS/MacOsCpuInfoDetector.cs rename to src/BenchmarkDotNet/Detectors/Cpu/macOS/MacOsCpuDetector.cs index 40b675b299..54bf602020 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/macOS/MacOsCpuInfoDetector.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/macOS/MacOsCpuDetector.cs @@ -1,16 +1,18 @@ using BenchmarkDotNet.Helpers; +using BenchmarkDotNet.Portability; +using Perfolizer.Phd.Dto; -namespace BenchmarkDotNet.Portability.Cpu.macOS; +namespace BenchmarkDotNet.Detectors.Cpu.macOS; /// /// CPU information from output of the `sysctl -a` command. /// MacOSX only. /// -internal class MacOsCpuInfoDetector : ICpuInfoDetector +internal class MacOsCpuDetector : ICpuDetector { - public bool IsApplicable() => RuntimeInformation.IsMacOS(); + public bool IsApplicable() => OsDetector.IsMacOS(); - public CpuInfo? Detect() + public PhdCpu? Detect() { if (!IsApplicable()) return null; diff --git a/src/BenchmarkDotNet/Portability/Cpu/macOS/SysctlCpuInfoParser.cs b/src/BenchmarkDotNet/Detectors/Cpu/macOS/SysctlCpuInfoParser.cs similarity index 55% rename from src/BenchmarkDotNet/Portability/Cpu/macOS/SysctlCpuInfoParser.cs rename to src/BenchmarkDotNet/Detectors/Cpu/macOS/SysctlCpuInfoParser.cs index f29afc1c8e..9a96a5b677 100644 --- a/src/BenchmarkDotNet/Portability/Cpu/macOS/SysctlCpuInfoParser.cs +++ b/src/BenchmarkDotNet/Detectors/Cpu/macOS/SysctlCpuInfoParser.cs @@ -2,8 +2,9 @@ using System.Diagnostics.CodeAnalysis; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; +using Perfolizer.Phd.Dto; -namespace BenchmarkDotNet.Portability.Cpu.macOS; +namespace BenchmarkDotNet.Detectors.Cpu.macOS; internal static class SysctlCpuInfoParser { @@ -19,22 +20,27 @@ private static class Sysctl /// Output of `sysctl -a` [SuppressMessage("ReSharper", "StringLiteralTypo")] - internal static CpuInfo Parse(string? sysctlOutput) + internal static PhdCpu Parse(string? sysctlOutput) { var sysctl = SectionsHelper.ParseSection(sysctlOutput, ':'); string processorName = sysctl.GetValueOrDefault(Sysctl.ProcessorName); - int? physicalProcessorCount = GetPositiveIntValue(sysctl, Sysctl.PhysicalProcessorCount); - int? physicalCoreCount = GetPositiveIntValue(sysctl, Sysctl.PhysicalCoreCount); - int? logicalCoreCount = GetPositiveIntValue(sysctl, Sysctl.LogicalCoreCount); - long? nominalFrequency = GetPositiveLongValue(sysctl, Sysctl.NominalFrequency); - long? maxFrequency = GetPositiveLongValue(sysctl, Sysctl.MaxFrequency); - return new CpuInfo( - processorName, - physicalProcessorCount, physicalCoreCount, logicalCoreCount, - nominalFrequency, maxFrequency); + int? physicalProcessorCount = PositiveIntValue(sysctl, Sysctl.PhysicalProcessorCount); + int? physicalCoreCount = PositiveIntValue(sysctl, Sysctl.PhysicalCoreCount); + int? logicalCoreCount = PositiveIntValue(sysctl, Sysctl.LogicalCoreCount); + long? nominalFrequency = PositiveLongValue(sysctl, Sysctl.NominalFrequency); + long? maxFrequency = PositiveLongValue(sysctl, Sysctl.MaxFrequency); + return new PhdCpu + { + ProcessorName = processorName, + PhysicalProcessorCount = physicalProcessorCount, + PhysicalCoreCount = physicalCoreCount, + LogicalCoreCount = logicalCoreCount, + NominalFrequencyHz = nominalFrequency, + MaxFrequencyHz = maxFrequency + }; } - private static int? GetPositiveIntValue(Dictionary sysctl, string keyName) + private static int? PositiveIntValue(Dictionary sysctl, string keyName) { if (sysctl.TryGetValue(keyName, out string value) && int.TryParse(value, out int result) && @@ -43,7 +49,7 @@ internal static CpuInfo Parse(string? sysctlOutput) return null; } - private static long? GetPositiveLongValue(Dictionary sysctl, string keyName) + private static long? PositiveLongValue(Dictionary sysctl, string keyName) { if (sysctl.TryGetValue(keyName, out string value) && long.TryParse(value, out long result) && diff --git a/src/BenchmarkDotNet/Detectors/CpuDetector.cs b/src/BenchmarkDotNet/Detectors/CpuDetector.cs new file mode 100644 index 0000000000..4cf183430d --- /dev/null +++ b/src/BenchmarkDotNet/Detectors/CpuDetector.cs @@ -0,0 +1,29 @@ +using System; +using System.Linq; +using BenchmarkDotNet.Detectors.Cpu; +using BenchmarkDotNet.Detectors.Cpu.Linux; +using BenchmarkDotNet.Detectors.Cpu.macOS; +using BenchmarkDotNet.Detectors.Cpu.Windows; +using BenchmarkDotNet.Extensions; +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Detectors; + +public class CpuDetector(params ICpuDetector[] detectors) : ICpuDetector +{ + public static CpuDetector CrossPlatform => new ( + new WindowsCpuDetector(), + new LinuxCpuDetector(), + new MacOsCpuDetector()); + + private static readonly Lazy LazyCpu = new (() => CrossPlatform.Detect()); + public static PhdCpu? Cpu => LazyCpu.Value; + + public bool IsApplicable() => detectors.Any(loader => loader.IsApplicable()); + + public PhdCpu? Detect() => detectors + .Where(loader => loader.IsApplicable()) + .Select(loader => loader.Detect()) + .WhereNotNull() + .FirstOrDefault(); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Detectors/OsDetector.cs b/src/BenchmarkDotNet/Detectors/OsDetector.cs new file mode 100644 index 0000000000..2afd862f2f --- /dev/null +++ b/src/BenchmarkDotNet/Detectors/OsDetector.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.IO; +using BenchmarkDotNet.Helpers; +using Microsoft.Win32; +using Perfolizer.Phd.Dto; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Extensions; +using static System.Runtime.InteropServices.RuntimeInformation; +using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment; + +namespace BenchmarkDotNet.Detectors; + +public class OsDetector +{ + public static readonly OsDetector Instance = new (); + private OsDetector() { } + + internal static string ExecutableExtension => IsWindows() ? ".exe" : string.Empty; + internal static string ScriptFileExtension => IsWindows() ? ".bat" : ".sh"; + + private readonly Lazy os = new (ResolveOs); + public static PhdOs GetOs() => Instance.os.Value; + + private static PhdOs ResolveOs() + { + if (IsMacOS()) + { + string systemVersion = ExternalToolsHelper.MacSystemProfilerData.Value.GetValueOrDefault("System Version") ?? ""; + string kernelVersion = ExternalToolsHelper.MacSystemProfilerData.Value.GetValueOrDefault("Kernel Version") ?? ""; + return new PhdOs + { + Name = "macOS", + Version = systemVersion, + KernelVersion = kernelVersion + }; + } + + if (IsLinux()) + { + try + { + string version = LinuxOsReleaseHelper.GetNameByOsRelease(File.ReadAllLines("/etc/os-release")); + bool wsl = IsUnderWsl(); + return new PhdOs + { + Name = "Linux", + Version = version, + Container = wsl ? "WSL" : null + }; + } + catch (Exception) + { + // Ignore + } + } + + string operatingSystem = RuntimeEnvironment.OperatingSystem; + string operatingSystemVersion = RuntimeEnvironment.OperatingSystemVersion; + if (IsWindows()) + { + int? ubr = GetWindowsUbr(); + if (ubr != null) + operatingSystemVersion += $".{ubr}"; + } + return new PhdOs + { + Name = operatingSystem, + Version = operatingSystemVersion + }; + } + + private static bool IsUnderWsl() + { + if (!IsLinux()) + return false; + try + { + return File.Exists("/proc/sys/fs/binfmt_misc/WSLInterop"); // https://superuser.com/a/1749811 + } + catch (Exception) + { + return false; + } + } + + // TODO: Introduce a common util API for registry calls, use it also in BenchmarkDotNet.Toolchains.CsProj.GetCurrentVersionBasedOnWindowsRegistry + /// + /// On Windows, this method returns UBR (Update Build Revision) based on Registry. + /// Returns null if the value is not available + /// + /// + private static int? GetWindowsUbr() + { + if (IsWindows()) + { + try + { + using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) + using (var ndpKey = baseKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")) + { + if (ndpKey == null) + return null; + + return Convert.ToInt32(ndpKey.GetValue("UBR")); + } + } + catch (Exception) + { + return null; + } + } + return null; + } + +#if NET6_0_OR_GREATER + [System.Runtime.Versioning.SupportedOSPlatformGuard("windows")] +#endif + internal static bool IsWindows() => +#if NET6_0_OR_GREATER + OperatingSystem.IsWindows(); // prefer linker-friendly OperatingSystem APIs +#else + IsOSPlatform(OSPlatform.Windows); +#endif + +#if NET6_0_OR_GREATER + [System.Runtime.Versioning.SupportedOSPlatformGuard("linux")] +#endif + internal static bool IsLinux() => +#if NET6_0_OR_GREATER + OperatingSystem.IsLinux(); +#else + IsOSPlatform(OSPlatform.Linux); +#endif + +#if NET6_0_OR_GREATER + [System.Runtime.Versioning.SupportedOSPlatformGuard("macos")] +#endif + // ReSharper disable once InconsistentNaming + internal static bool IsMacOS() => +#if NET6_0_OR_GREATER + OperatingSystem.IsMacOS(); +#else + IsOSPlatform(OSPlatform.OSX); +#endif + +#if NET6_0_OR_GREATER + [System.Runtime.Versioning.SupportedOSPlatformGuard("android")] +#endif + internal static bool IsAndroid() => +#if NET6_0_OR_GREATER + OperatingSystem.IsAndroid(); +#else + Type.GetType("Java.Lang.Object, Mono.Android") != null; +#endif + +#if NET6_0_OR_GREATER + [System.Runtime.Versioning.SupportedOSPlatformGuard("ios")] +#endif + // ReSharper disable once InconsistentNaming + internal static bool IsIOS() => +#if NET6_0_OR_GREATER + OperatingSystem.IsIOS(); +#else + Type.GetType("Foundation.NSObject, Xamarin.iOS") != null; +#endif + +#if NET6_0_OR_GREATER + [System.Runtime.Versioning.SupportedOSPlatformGuard("tvos")] +#endif + // ReSharper disable once InconsistentNaming + internal static bool IsTvOS() => +#if NET6_0_OR_GREATER + OperatingSystem.IsTvOS(); +#else + IsOSPlatform(OSPlatform.Create("TVOS")); +#endif +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Diagnosers/DiagnosersLoader.cs b/src/BenchmarkDotNet/Diagnosers/DiagnosersLoader.cs index 1e96f8e495..40e8f231e7 100644 --- a/src/BenchmarkDotNet/Diagnosers/DiagnosersLoader.cs +++ b/src/BenchmarkDotNet/Diagnosers/DiagnosersLoader.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Threading; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Portability; @@ -40,11 +41,11 @@ private static IEnumerable LoadDiagnosers() { yield return EventPipeProfiler.Default; - if (RuntimeInformation.IsLinux()) + if (OsDetector.IsLinux()) yield return PerfCollectProfiler.Default; } - if (!RuntimeInformation.IsWindows()) + if (!OsDetector.IsWindows()) yield break; foreach (var windowsDiagnoser in LoadWindowsDiagnosers()) diff --git a/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs b/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs index 6dd5bdd2e9..f1b584ce09 100644 --- a/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs +++ b/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Runtime.InteropServices; using BenchmarkDotNet.Analysers; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Extensions; @@ -53,7 +54,7 @@ public class PerfCollectProfiler : IProfiler public IEnumerable Validate(ValidationParameters validationParameters) { - if (!RuntimeInformation.IsLinux()) + if (!OsDetector.IsLinux()) { yield return new ValidationError(true, "The PerfCollectProfiler works only on Linux!"); yield break; diff --git a/src/BenchmarkDotNet/Diagnosers/UnresolvedDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/UnresolvedDiagnoser.cs index f00bb4ed67..43cc910559 100644 --- a/src/BenchmarkDotNet/Diagnosers/UnresolvedDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/UnresolvedDiagnoser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using BenchmarkDotNet.Analysers; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Loggers; @@ -31,10 +32,10 @@ public IEnumerable Validate(ValidationParameters validationPara => new[] { new ValidationError(false, GetErrorMessage()) }; private string GetErrorMessage() => $@"Unable to resolve {unresolved.Name} diagnoser using dynamic assembly loading. - {(RuntimeInformation.IsFullFramework || RuntimeInformation.IsWindows() + {(RuntimeInformation.IsFullFramework || OsDetector.IsWindows() ? "Please make sure that you have installed the latest BenchmarkDotNet.Diagnostics.Windows package. " + Environment.NewLine + "If you are using `dotnet build` you also need to consume one of its public types to make sure that MSBuild copies it to the output directory. " + "The alternative is to use `true` in your project file." - : $"Please make sure that it's supported on {RuntimeInformation.GetOsVersion()}")}"; + : $"Please make sure that it's supported on {OsDetector.GetOs()}")}"; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs b/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs index 16b3891d24..aff7d276d6 100644 --- a/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs +++ b/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Portability; namespace BenchmarkDotNet.Disassemblers @@ -19,11 +20,11 @@ internal abstract class ClrMdV2Disassembler private static ulong GetMinValidAddress() { // https://github.com/dotnet/BenchmarkDotNet/pull/2413#issuecomment-1688100117 - if (RuntimeInformation.IsWindows()) + if (OsDetector.IsWindows()) return ushort.MaxValue + 1; - if (RuntimeInformation.IsLinux()) + if (OsDetector.IsLinux()) return (ulong) Environment.SystemPageSize; - if (RuntimeInformation.IsMacOS()) + if (OsDetector.IsMacOS()) return RuntimeInformation.GetCurrentPlatform() switch { Environments.Platform.X86 or Environments.Platform.X64 => 4096, @@ -335,7 +336,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, protected void FlushCachedDataIfNeeded(IDataReader dataTargetDataReader, ulong address, byte[] buffer) { - if (!RuntimeInformation.IsWindows()) + if (!OsDetector.IsWindows()) { if (dataTargetDataReader.Read(address, buffer) <= 0) { diff --git a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs index 2dd18a1b61..7d0f95326c 100644 --- a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs +++ b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs @@ -3,6 +3,7 @@ using System.Linq; using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Disassemblers; using BenchmarkDotNet.Disassemblers.Exporters; using BenchmarkDotNet.Engines; @@ -75,7 +76,7 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) case HostSignal.AfterAll when ShouldUseSameArchitectureDisassembler(benchmark, parameters): results.Add(benchmark, sameArchitectureDisassembler.Disassemble(parameters)); break; - case HostSignal.AfterAll when RuntimeInformation.IsWindows() && !ShouldUseMonoDisassembler(benchmark): + case HostSignal.AfterAll when OsDetector.IsWindows() && !ShouldUseMonoDisassembler(benchmark): results.Add(benchmark, windowsDifferentArchitectureDisassembler.Disassemble(parameters)); break; case HostSignal.SeparateLogic when ShouldUseMonoDisassembler(benchmark): @@ -112,7 +113,7 @@ public IEnumerable Validate(ValidationParameters validationPara if (ShouldUseClrMdDisassembler(benchmark)) { - if (RuntimeInformation.IsLinux()) + if (OsDetector.IsLinux()) { var runtime = benchmark.Job.ResolveValue(EnvironmentMode.RuntimeCharacteristic, EnvironmentResolver.Instance); @@ -143,13 +144,13 @@ private static bool ShouldUseMonoDisassembler(BenchmarkCase benchmarkCase) // when we add macOS support, RuntimeInformation.IsMacOS() needs to be added here private static bool ShouldUseClrMdDisassembler(BenchmarkCase benchmarkCase) - => !ShouldUseMonoDisassembler(benchmarkCase) && (RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux()); + => !ShouldUseMonoDisassembler(benchmarkCase) && (OsDetector.IsWindows() || OsDetector.IsLinux()); private static bool ShouldUseSameArchitectureDisassembler(BenchmarkCase benchmarkCase, DiagnoserActionParameters parameters) { if (ShouldUseClrMdDisassembler(benchmarkCase)) { - if (RuntimeInformation.IsWindows()) + if (OsDetector.IsWindows()) { return WindowsDisassembler.GetDisassemblerArchitecture(parameters.Process, benchmarkCase.Job.Environment.Platform) == RuntimeInformation.GetCurrentPlatform(); diff --git a/src/BenchmarkDotNet/Disassemblers/WindowsDisassembler.cs b/src/BenchmarkDotNet/Disassemblers/WindowsDisassembler.cs index 6bec70162a..8051ba3463 100644 --- a/src/BenchmarkDotNet/Disassemblers/WindowsDisassembler.cs +++ b/src/BenchmarkDotNet/Disassemblers/WindowsDisassembler.cs @@ -7,6 +7,7 @@ using System.Text; using System.Xml; using System.Xml.Serialization; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Extensions; @@ -165,7 +166,7 @@ public static bool Is64Bit(Process process) if (Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE") == "x86") return false; - if (RuntimeInformation.IsWindows()) + if (OsDetector.IsWindows()) { IsWow64Process(process.Handle, out bool isWow64); diff --git a/src/BenchmarkDotNet/Environments/BenchmarkEnvironmentInfo.cs b/src/BenchmarkDotNet/Environments/BenchmarkEnvironmentInfo.cs index e13d50b650..f2e2f34e26 100644 --- a/src/BenchmarkDotNet/Environments/BenchmarkEnvironmentInfo.cs +++ b/src/BenchmarkDotNet/Environments/BenchmarkEnvironmentInfo.cs @@ -2,10 +2,10 @@ using System.Diagnostics; using System.Linq; using System.Runtime; +using BenchmarkDotNet.Detectors.Cpu; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; -using BenchmarkDotNet.Portability.Cpu; using BenchmarkDotNet.Properties; using BenchmarkDotNet.Validators; using JetBrains.Annotations; diff --git a/src/BenchmarkDotNet/Environments/HostEnvironmentInfo.cs b/src/BenchmarkDotNet/Environments/HostEnvironmentInfo.cs index 7983f82ebc..78e1624f4a 100644 --- a/src/BenchmarkDotNet/Environments/HostEnvironmentInfo.cs +++ b/src/BenchmarkDotNet/Environments/HostEnvironmentInfo.cs @@ -2,16 +2,20 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Phd; using BenchmarkDotNet.Portability; -using BenchmarkDotNet.Portability.Cpu; using BenchmarkDotNet.Properties; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Toolchains.DotNetCli; using JetBrains.Annotations; +using Perfolizer.Helpers; using Perfolizer.Horology; using Perfolizer.Metrology; +using Perfolizer.Phd; +using Perfolizer.Phd.Dto; namespace BenchmarkDotNet.Environments { @@ -32,15 +36,12 @@ public class HostEnvironmentInfo : BenchmarkEnvironmentInfo public string BenchmarkDotNetVersion { get; protected set; } - /// - /// Could be expensive - /// - public Lazy OsVersion { get; protected set; } - /// /// is expensive to call (1s) /// - public Lazy CpuInfo { get; protected set; } + public Lazy Cpu { get; protected set; } + + public Lazy Os { get; protected set; } /// /// .NET Core SDK version @@ -65,35 +66,36 @@ public class HostEnvironmentInfo : BenchmarkEnvironmentInfo public Lazy> AntivirusProducts { get; } - public Lazy VirtualMachineHypervisor { get; protected set; } + // TODO: Join with PhdOs + public Lazy VirtualMachineHypervisor { get; protected set; } protected HostEnvironmentInfo() { BenchmarkDotNetVersion = BenchmarkDotNetInfo.Instance.BrandVersion; - OsVersion = new Lazy(RuntimeInformation.GetOsVersion); - CpuInfo = new Lazy(RuntimeInformation.GetCpuInfo); ChronometerFrequency = Chronometer.Frequency; HardwareTimerKind = Chronometer.HardwareTimerKind; DotNetSdkVersion = new Lazy(DotNetCliCommandExecutor.GetDotNetSdkVersion); IsMonoInstalled = new Lazy(() => !string.IsNullOrEmpty(ProcessHelper.RunAndReadOutput("mono", "--version"))); AntivirusProducts = new Lazy>(RuntimeInformation.GetAntivirusProducts); VirtualMachineHypervisor = new Lazy(RuntimeInformation.GetVirtualMachineHypervisor); + Os = new Lazy(OsDetector.GetOs); + Cpu = new Lazy(CpuDetector.CrossPlatform.Detect); } public new static HostEnvironmentInfo GetCurrent() => current ??= new HostEnvironmentInfo(); public override IEnumerable ToFormattedString() { - string vmName = VirtualMachineHypervisor.Value?.Name; + string? vmName = VirtualMachineHypervisor.Value?.Name; if (!string.IsNullOrEmpty(vmName)) - yield return $"{BenchmarkDotNetCaption} v{BenchmarkDotNetVersion}, {OsVersion.Value} ({vmName})"; + yield return $"{BenchmarkDotNetCaption} v{BenchmarkDotNetVersion}, {Os.Value.ToBrandString()} ({vmName})"; else if (RuntimeInformation.IsRunningInContainer) - yield return $"{BenchmarkDotNetCaption} v{BenchmarkDotNetVersion}, {OsVersion.Value} (container)"; + yield return $"{BenchmarkDotNetCaption} v{BenchmarkDotNetVersion}, {Os.Value.ToBrandString()} (container)"; else - yield return $"{BenchmarkDotNetCaption} v{BenchmarkDotNetVersion}, {OsVersion.Value}"; + yield return $"{BenchmarkDotNetCaption} v{BenchmarkDotNetVersion}, {Os.Value.ToBrandString()}"; - yield return CpuInfoFormatter.Format(CpuInfo.Value); + yield return Cpu.Value.ToFullBrandName(); if (HardwareTimerKind != HardwareTimerKind.Unknown) { string frequency = ChronometerFrequency.ToString(FrequencyUnit.Hz, unitPresentation: UnitHelper.DefaultPresentation); @@ -104,7 +106,7 @@ public override IEnumerable ToFormattedString() if (RuntimeInformation.IsNetCore && IsDotNetCliInstalled()) { - // this wonderful version number contains words like "preview" and ... 5 segments so it can not be parsed by Version.Parse. Example: "5.0.100-preview.8.20362.3" + // this wonderful version number contains words like "preview" and ... 5 segments, so it can not be parsed by Version.Parse. Example: "5.0.100-preview.8.20362.3" if (int.TryParse(new string(DotNetSdkVersion.Value.TrimStart().TakeWhile(char.IsDigit).ToArray()), out int major) && major >= 5) yield return $".NET SDK {DotNetSdkVersion.Value}"; else @@ -131,5 +133,18 @@ public static string GetInformation() sb.AppendLine(Summary.BuildAllRuntimes(hostEnvironmentInfo, Array.Empty())); return sb.ToString(); } + + public BdnHost ToPhd() => new () + { + Cpu = Cpu.Value, + Os = Os.Value, + RuntimeVersion = RuntimeVersion, + HasAttachedDebugger = HasAttachedDebugger, + HasRyuJit = HasRyuJit, + Configuration = Configuration, + DotNetSdkVersion = DotNetSdkVersion.Value, + ChronometerFrequency = ChronometerFrequency.Hertz, + HardwareTimerKind = HardwareTimerKind.ToString() + }; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs b/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs deleted file mode 100644 index 045c1f7543..0000000000 --- a/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs +++ /dev/null @@ -1,278 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using BenchmarkDotNet.Extensions; - -namespace BenchmarkDotNet.Environments -{ - [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] - public class OsBrandStringHelper - { - // See https://en.wikipedia.org/wiki/Ver_(command) - // See https://docs.microsoft.com/en-us/windows/release-health/release-information - // See https://docs.microsoft.com/en-us/windows/release-health/windows11-release-information - private static readonly Dictionary WindowsBrandVersions = new Dictionary - { - { "1.04", "1.0" }, - { "2.11", "2.0" }, - { "3", "3.0" }, - { "3.10.528", "NT 3.1" }, - { "3.11", "for Workgroups 3.11" }, - { "3.50.807", "NT 3.5" }, - { "3.51.1057", "NT 3.51" }, - { "4.00.950", "95" }, - { "4.00.1111", "95 OSR2" }, - { "4.03.1212-1214", "95 OSR2.1" }, - { "4.03.1214", "95 OSR2.5" }, - { "4.00.1381", "NT 4.0" }, - { "4.10.1998", "98" }, - { "4.10.2222", "98 SE" }, - { "4.90.2380.2", "ME Beta" }, - { "4.90.2419", "ME Beta 2" }, - { "4.90.3000", "ME" }, - { "5.00.1515", "NT 5.0 Beta" }, - { "5.00.2031", "2000 Beta 3" }, - { "5.00.2128", "2000 RC2" }, - { "5.00.2183", "2000 RC3" }, - { "5.00.2195", "2000" }, - { "5.0.2195", "2000 Professional" }, - { "5.1.2505", "XP RC1" }, - { "5.1.2600", "XP" }, - { "5.1.2600.1105-1106", "XP SP1" }, - { "5.1.2600.2180", "XP SP2" }, - { "5.2.3541", ".NET Server interim" }, - { "5.2.3590", ".NET Server Beta 3" }, - { "5.2.3660", ".NET Server RC1" }, - { "5.2.3718", ".NET Server 2003 RC2" }, - { "5.2.3763", "Server 2003 Beta" }, - { "5.2.3790", "XP Professional x64 Edition" }, - { "5.2.3790.1180", "Server 2003 SP1" }, - { "5.2.3790.1218", "Server 2003" }, - { "6.0.5048", "Longhorn" }, - { "6.0.5112", "Vista Beta 1" }, - { "6.0.5219", "Vista CTP" }, - { "6.0.5259", "Vista TAP Preview" }, - { "6.0.5270", "Vista CTP December" }, - { "6.0.5308", "Vista CTP February" }, - { "6.0.5342", "Vista CTP Refresh" }, - { "6.0.5365", "Vista April EWD" }, - { "6.0.5381", "Vista Beta 2 Preview" }, - { "6.0.5384", "Vista Beta 2" }, - { "6.0.5456", "Vista Pre-RC1 Build 5456" }, - { "6.0.5472", "Vista Pre-RC1 Build 5472" }, - { "6.0.5536", "Vista Pre-RC1 Build 5536" }, - { "6.0.5600.16384", "Vista RC1" }, - { "6.0.5700", "Vista Pre-RC2" }, - { "6.0.5728", "Vista Pre-RC2 Build 5728" }, - { "6.0.5744.16384", "Vista RC2" }, - { "6.0.5808", "Vista Pre-RTM Build 5808" }, - { "6.0.5824", "Vista Pre-RTM Build 5824" }, - { "6.0.5840", "Vista Pre-RTM Build 5840" }, - { "6.0.6000", "Vista" }, - { "6.0.6000.16386", "Vista RTM" }, - { "6.0.6001", "Vista SP1" }, - { "6.0.6002", "Vista SP2" }, - { "6.1.7600", "7" }, - { "6.1.7600.16385", "7" }, - { "6.1.7601", "7 SP1" }, - { "6.1.8400", "Home Server 2011" }, - { "6.2.8102", "8 Developer Preview" }, - { "6.2.9200", "8" }, - { "6.2.9200.16384", "8 RTM" }, - { "6.2.10211", "Phone 8" }, - { "6.3.9600", "8.1" }, - { "6.4.9841", "10 Technical Preview 1" }, - { "6.4.9860", "10 Technical Preview 2" }, - { "6.4.9879", "10 Technical Preview 3" }, - { "10.0.9926", "10 Technical Preview 4" }, - { "10.0.10041", "10 Technical Preview 5" }, - { "10.0.10049", "10 Technical Preview 6" }, - { "10.0.10240", "10 Threshold 1 [1507, RTM]" }, - { "10.0.10586", "10 Threshold 2 [1511, November Update]" }, - { "10.0.14393", "10 Redstone 1 [1607, Anniversary Update]" }, - { "10.0.15063", "10 Redstone 2 [1703, Creators Update]" }, - { "10.0.16299", "10 Redstone 3 [1709, Fall Creators Update]" }, - { "10.0.17134", "10 Redstone 4 [1803, April 2018 Update]" }, - { "10.0.17763", "10 Redstone 5 [1809, October 2018 Update]" }, - { "10.0.18362", "10 19H1 [1903, May 2019 Update]" }, - { "10.0.18363", "10 19H2 [1909, November 2019 Update]" }, - { "10.0.19041", "10 20H1 [2004, May 2020 Update]" }, - { "10.0.19042", "10 20H2 [20H2, October 2020 Update]" }, - { "10.0.19043", "10 21H1 [21H1, May 2021 Update]" }, - { "10.0.19044", "10 21H2 [21H2, November 2021 Update]" }, - { "10.0.19045", "10 22H2 [22H2, 2022 Update]" }, - { "10.0.22000", "11 21H2 [21H2, Sun Valley]" }, - { "10.0.22621", "11 22H2 [22H2, Sun Valley 2]" }, - }; - - private class Windows1XVersion - { - private string? CodeVersion { get; } - private string? CodeName { get; } - private string? MarketingName { get; } - private int BuildNumber { get; } - - private string MarketingNumber => BuildNumber >= 22000 ? "11" : "10"; - private string? ShortifiedCodeName => CodeName?.Replace(" ", ""); - private string? ShortifiedMarketingName => MarketingName?.Replace(" ", ""); - - private Windows1XVersion(string? codeVersion, string? codeName, string? marketingName, int buildNumber) - { - CodeVersion = codeVersion; - CodeName = codeName; - MarketingName = marketingName; - BuildNumber = buildNumber; - } - - private string ToFullVersion(int? ubr = null) - => ubr == null ? $"10.0.{BuildNumber}" : $"10.0.{BuildNumber}.{ubr}"; - - private static string Collapse(params string[] values) => string.Join("/", values.Where(v => !string.IsNullOrEmpty(v))); - - // The line with OsBrandString is one of the longest lines in the summary. - // When people past in on GitHub, it can be a reason of an ugly horizontal scrollbar. - // To avoid this, we are trying to minimize this line and use the minimum possible number of characters. - public string ToPrettifiedString(int? ubr) - => CodeVersion == ShortifiedCodeName - ? $"{MarketingNumber} ({Collapse(ToFullVersion(ubr), CodeVersion, ShortifiedMarketingName)})" - : $"{MarketingNumber} ({Collapse(ToFullVersion(ubr), CodeVersion, ShortifiedMarketingName, ShortifiedCodeName)})"; - - // See https://en.wikipedia.org/wiki/Windows_10_version_history - // See https://en.wikipedia.org/wiki/Windows_11_version_history - private static readonly List WellKnownVersions = new () - { - // Windows 10 - new Windows1XVersion("1507", "Threshold 1", "RTM", 10240), - new Windows1XVersion("1511", "Threshold 2", "November Update", 10586), - new Windows1XVersion("1607", "Redstone 1", "Anniversary Update", 14393), - new Windows1XVersion("1703", "Redstone 2", "Creators Update", 15063), - new Windows1XVersion("1709", "Redstone 3", "Fall Creators Update", 16299), - new Windows1XVersion("1803", "Redstone 4", "April 2018 Update", 17134), - new Windows1XVersion("1809", "Redstone 5", "October 2018 Update", 17763), - new Windows1XVersion("1903", "19H1", "May 2019 Update", 18362), - new Windows1XVersion("1909", "19H2", "November 2019 Update", 18363), - new Windows1XVersion("2004", "20H1", "May 2020 Update", 19041), - new Windows1XVersion("20H2", "20H2", "October 2020 Update", 19042), - new Windows1XVersion("21H1", "21H1", "May 2021 Update", 19043), - new Windows1XVersion("21H2", "21H2", "November 2021 Update", 19044), - new Windows1XVersion("22H2", "22H2", "2022 Update", 19045), - // Windows 11 - new Windows1XVersion("21H2", "Sun Valley", null, 22000), - new Windows1XVersion("22H2", "Sun Valley 2", "2022 Update", 22621), - new Windows1XVersion("23H2", "Sun Valley 3", "2023 Update", 22631), - }; - - public static Windows1XVersion? Resolve(string osVersionString) - { - var windows1XVersion = WellKnownVersions.FirstOrDefault(v => osVersionString == $"10.0.{v.BuildNumber}"); - if (windows1XVersion != null) - return windows1XVersion; - if (Version.TryParse(osVersionString, out var osVersion)) - { - if (osVersion.Major == 10 && osVersion.Minor == 0) - return new Windows1XVersion(null, null, null, osVersion.Build); - } - return null; - } - } - - /// - /// Transform an operation system name and version to a nice form for summary. - /// - /// Original operation system name - /// Original operation system version - /// UBR (Update Build Revision), the revision number of Windows version (if available) - /// Prettified operation system title - public static string Prettify(string osName, string osVersion, int? windowsUbr = null) - { - if (osName == "Windows") - return PrettifyWindows(osVersion, windowsUbr); - return $"{osName} {osVersion}"; - } - - private static string PrettifyWindows(string osVersion, int? windowsUbr) - { - var windows1XVersion = Windows1XVersion.Resolve(osVersion); - if (windows1XVersion != null) - return "Windows " + windows1XVersion.ToPrettifiedString(windowsUbr); - - string brandVersion = WindowsBrandVersions.GetValueOrDefault(osVersion); - string completeOsVersion = windowsUbr != null && osVersion.Count(c => c == '.') == 2 - ? osVersion + "." + windowsUbr - : osVersion; - string fullVersion = brandVersion == null ? osVersion : brandVersion + " (" + completeOsVersion + ")"; - return "Windows " + fullVersion; - } - - private class MacOSXVersion - { - private int DarwinVersion { get; } - private string CodeName { get; } - - private MacOSXVersion(int darwinVersion, string codeName) - { - DarwinVersion = darwinVersion; - CodeName = codeName; - } - - private static readonly List WellKnownVersions = new List - { - new MacOSXVersion(6, "Jaguar"), - new MacOSXVersion(7, "Panther"), - new MacOSXVersion(8, "Tiger"), - new MacOSXVersion(9, "Leopard"), - new MacOSXVersion(10, "Snow Leopard"), - new MacOSXVersion(11, "Lion"), - new MacOSXVersion(12, "Mountain Lion"), - new MacOSXVersion(13, "Mavericks"), - new MacOSXVersion(14, "Yosemite"), - new MacOSXVersion(15, "El Capitan"), - new MacOSXVersion(16, "Sierra"), - new MacOSXVersion(17, "High Sierra"), - new MacOSXVersion(18, "Mojave"), - new MacOSXVersion(19, "Catalina"), - new MacOSXVersion(20, "Big Sur"), - new MacOSXVersion(21, "Monterey"), - new MacOSXVersion(22, "Ventura"), - new MacOSXVersion(23, "Sonoma"), - new MacOSXVersion(24, "Sequoia"), - }; - - public static string? ResolveCodeName(string kernelVersion) - { - if (string.IsNullOrWhiteSpace(kernelVersion)) - return null; - - kernelVersion = kernelVersion.ToLowerInvariant().Trim(); - if (kernelVersion.StartsWith("darwin")) - kernelVersion = kernelVersion.Substring(6).Trim(); - var numbers = kernelVersion.Split('.'); - if (numbers.Length == 0) - return null; - - string majorVersionStr = numbers[0]; - if (int.TryParse(majorVersionStr, out int majorVersion)) - return WellKnownVersions.FirstOrDefault(v => v.DarwinVersion == majorVersion)?.CodeName; - return null; - } - } - - public static string PrettifyMacOSX(string systemVersion, string kernelVersion) - { - string codeName = MacOSXVersion.ResolveCodeName(kernelVersion); - if (codeName != null) - { - int firstDigitIndex = systemVersion.IndexOfAny("0123456789".ToCharArray()); - if (firstDigitIndex == -1) - return $"{systemVersion} {codeName} [{kernelVersion}]"; - - string systemVersionTitle = systemVersion.Substring(0, firstDigitIndex).Trim(); - string systemVersionNumbers = systemVersion.Substring(firstDigitIndex).Trim(); - return $"{systemVersionTitle} {codeName} {systemVersionNumbers} [{kernelVersion}]"; - } - - return $"{systemVersion} [{kernelVersion}]"; - } - } -} diff --git a/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs b/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs deleted file mode 100644 index 1f715f9cbe..0000000000 --- a/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text.RegularExpressions; -using BenchmarkDotNet.Extensions; -using BenchmarkDotNet.Helpers; -using BenchmarkDotNet.Portability.Cpu; -using Perfolizer.Horology; - -namespace BenchmarkDotNet.Environments; - -public static class ProcessorBrandStringHelper -{ - /// - /// Transform a processor brand string to a nice form for summary. - /// - /// The CPU information - /// Whether to include determined max frequency information - /// Prettified version - public static string Prettify(CpuInfo? cpuInfo, bool includeMaxFrequency = false) - { - string? processorName = cpuInfo?.ProcessorName; - if (processorName == null || processorName.IsBlank()) - return "Unknown processor"; - - // Remove parts which don't provide any useful information for user - processorName = processorName.Replace("@", "").Replace("(R)", "").Replace("(TM)", ""); - - // If we have found physical core(s), we can safely assume we can drop extra info from brand - if (cpuInfo.PhysicalCoreCount is > 0) - processorName = Regex.Replace(processorName, @"(\w+?-Core Processor)", "").Trim(); - - string frequencyString = GetBrandStyledNominalFrequency(cpuInfo.NominalFrequency); - if (includeMaxFrequency && frequencyString != null && !processorName.Contains(frequencyString)) - { - // show Max only if there's already a frequency name to differentiate the two - string maxFrequency = processorName.Contains("Hz") ? $"(Max: {frequencyString})" : frequencyString; - processorName = $"{processorName} {maxFrequency}"; - } - - // Remove double spaces - processorName = Regex.Replace(processorName.Trim(), @"\s+", " "); - - // Add microarchitecture name if known - string microarchitecture = ParseMicroarchitecture(processorName); - if (microarchitecture != null) - processorName = $"{processorName} ({microarchitecture})"; - - return processorName; - } - - /// - /// Presents actual processor's frequency into brand string format - /// - /// - private static string? GetBrandStyledNominalFrequency(Frequency? frequency) - { - if (frequency == null) - return null; - return $"{frequency.Value.ToGHz().ToString("N2", DefaultCultureInfo.Instance)}GHz"; - } - - /// - /// Parse a processor name and tries to return a microarchitecture name. - /// Works only for well-known microarchitectures. - /// - private static string? ParseMicroarchitecture(string processorName) - { - if (processorName.StartsWith("Intel Core")) - { - string model = processorName.Substring("Intel Core".Length).Trim(); - - // Core i3/5/7/9 - if ( - model.Length > 4 && - model[0] == 'i' && - (model[1] == '3' || model[1] == '5' || model[1] == '7' || model[1] == '9') && - (model[2] == '-' || model[2] == ' ')) - { - string modelNumber = model.Substring(3); - if (modelNumber.StartsWith("CPU")) - modelNumber = modelNumber.Substring(3).Trim(); - if (modelNumber.Contains("CPU")) - modelNumber = modelNumber.Substring(0, modelNumber.IndexOf("CPU", StringComparison.Ordinal)).Trim(); - return ParseIntelCoreMicroarchitecture(modelNumber); - } - } - - return null; - } - - private static readonly Lazy> KnownMicroarchitectures = new Lazy>(() => - { - var data = ResourceHelper.LoadResource("BenchmarkDotNet.Environments.microarchitectures.txt").Split('\r', '\n'); - var dictionary = new Dictionary(); - string? currentMicroarchitecture = null; - foreach (string line in data) - { - if (line.StartsWith("//") || string.IsNullOrWhiteSpace(line)) - continue; - if (line.StartsWith("#")) - { - currentMicroarchitecture = line.Substring(1).Trim(); - continue; - } - - string modelNumber = line.Trim(); - if (dictionary.ContainsKey(modelNumber)) - throw new Exception($"{modelNumber} is defined twice in microarchitectures.txt"); - if (currentMicroarchitecture == null) - throw new Exception($"{modelNumber} doesn't have defined microarchitecture in microarchitectures.txt"); - dictionary[modelNumber] = currentMicroarchitecture; - } - - return dictionary; - }); - - // see http://www.intel.com/content/www/us/en/processors/processor-numbers.html - [SuppressMessage("ReSharper", "StringLiteralTypo")] - internal static string? ParseIntelCoreMicroarchitecture(string modelNumber) - { - if (KnownMicroarchitectures.Value.TryGetValue(modelNumber, out string? microarchitecture)) - return microarchitecture; - - if (modelNumber.Length >= 3 && modelNumber.Substring(0, 3).All(char.IsDigit) && - (modelNumber.Length == 3 || !char.IsDigit(modelNumber[3]))) - return "Nehalem"; - if (modelNumber.Length >= 4 && modelNumber.Substring(0, 4).All(char.IsDigit)) - { - char generation = modelNumber[0]; - switch (generation) - { - case '2': - return "Sandy Bridge"; - case '3': - return "Ivy Bridge"; - case '4': - return "Haswell"; - case '5': - return "Broadwell"; - case '6': - return "Skylake"; - case '7': - return "Kaby Lake"; - case '8': - return "Coffee Lake"; - default: - return null; - } - } - return null; - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Environments/Runtimes/ClrRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/ClrRuntime.cs index be5a84cce2..21bb2821df 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/ClrRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/ClrRuntime.cs @@ -1,4 +1,5 @@ using System; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; @@ -44,7 +45,7 @@ public static ClrRuntime CreateForLocalFullNetFrameworkBuild(string version) internal static ClrRuntime GetCurrentVersion() { - if (!RuntimeInformation.IsWindows()) + if (!OsDetector.IsWindows()) { throw new NotSupportedException(".NET Framework supports Windows OS only."); } diff --git a/src/BenchmarkDotNet/Environments/microarchitectures.txt b/src/BenchmarkDotNet/Environments/microarchitectures.txt deleted file mode 100644 index f06d295390..0000000000 --- a/src/BenchmarkDotNet/Environments/microarchitectures.txt +++ /dev/null @@ -1,183 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Kaby Lake -// See: https://en.wikipedia.org/wiki/Kaby_Lake -//////////////////////////////////////////////////////////////////////////////// - -# Kaby Lake -7Y30 -7Y32 -7Y54 -7Y57 -7Y75 -3865U -3965U -4410Y -4415U -7100 -7100H -7100T -7100U -7101E -7101TE -7130U -7167U -7200U -7260U -7267U -7287U -7300 -7300HQ -7300T -7300U -7320 -7350K -7360U -7400 -7400T -7440HQ -7500 -7500T -7500U -7560U -7567U -7600 -7600K -7600T -7600U -7640X -7660U -7700 -7700HQ -7700K -7700T -7740X -7820HK -7820HQ -7920HQ -8130U -E3-1220 v6 -E3-1225 v6 -E3-1230 v6 -E3-1240 v6 -E3-1245 v6 -E3-1270 v6 -E3-1275 v6 -E3-1280 v6 -E3-1285 v6 -E3-1505L v6 -E3-1505M v6 -E3-1535M v6 -G3930 -G3930T -G3950 -G4560 -G4560T -G4600 -G4600T -G4620 - -# Kaby Lake R -8250U -8350U -8550U -8650U - -# Kaby Lake G -8305G -8705G -8706G -8709G -8809G - -# Amber Lake Y -8100Y -8200Y -8210Y -8500Y - -//////////////////////////////////////////////////////////////////////////////// -// Coffee Lake -// See: https://en.wikipedia.org/wiki/Coffee_Lake -//////////////////////////////////////////////////////////////////////////////// - -# Coffee Lake -610 -2104G -2124 -2124G -2126G -2134 -2136 -2144G -2146G -2174G -2176G -2176M -2186G -2186M -8086K -8100 -8100H -8100T -8109U -8259U -8269U -8300 -8300H -8300T -8350K -8400 -8400B -8400H -8400T -8500 -8500B -8500T -8559U -8600 -8600K -8600T -8700 -8700B -8700K -8700T -8750H -8850H -8950HK -9350KF -9400 -9400F -9600K -9600KF -9700K -9700KF -9900K -9900KF -G4900 -G4900T -G4920 -G5400 -G5400T -G5500 -G5500T -G5600 - -//////////////////////////////////////////////////////////////////////////////// -// Cannon Lake -// See: https://en.wikipedia.org/wiki/Cannon_Lake_(microarchitecture) -//////////////////////////////////////////////////////////////////////////////// - -# Cannon Lake -8121U - -//////////////////////////////////////////////////////////////////////////////// -// Whiskey Lake -// See: https://en.wikipedia.org/wiki/Whiskey_Lake_(microarchitecture) -//////////////////////////////////////////////////////////////////////////////// - -# Whiskey Lake -8565U -8265U -8145U -5405U -4205U \ No newline at end of file diff --git a/src/BenchmarkDotNet/Exporters/Json/JsonExporterBase.cs b/src/BenchmarkDotNet/Exporters/Json/JsonExporterBase.cs index 8ce4472a5a..3727ecac80 100644 --- a/src/BenchmarkDotNet/Exporters/Json/JsonExporterBase.cs +++ b/src/BenchmarkDotNet/Exporters/Json/JsonExporterBase.cs @@ -3,8 +3,9 @@ using BenchmarkDotNet.Environments; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Reports; +using Perfolizer.Helpers; using Perfolizer.Horology; -using JsonSerializer = SimpleJson.SimpleJson; +using SimpleJson; namespace BenchmarkDotNet.Exporters.Json { @@ -23,8 +24,8 @@ protected JsonExporterBase(bool indentJson = false, bool excludeMeasurements = f public override void ExportToLog(Summary summary, ILogger logger) { - JsonSerializer.CurrentJsonSerializerStrategy.Indent = IndentJson; - logger.WriteLine(JsonSerializer.SerializeObject(GetDataToSerialize(summary))); + SimpleJsonSerializer.CurrentJsonSerializerStrategy.Indent = IndentJson; + logger.WriteLine(SimpleJsonSerializer.SerializeObject(GetDataToSerialize(summary))); } protected virtual IReadOnlyDictionary GetDataToSerialize(Summary summary) @@ -47,11 +48,11 @@ protected virtual IReadOnlyDictionary GetDataToSerialize(HostEnv { { nameof(HostEnvironmentInfo.BenchmarkDotNetCaption), HostEnvironmentInfo.BenchmarkDotNetCaption }, { nameof(environmentInfo.BenchmarkDotNetVersion), environmentInfo.BenchmarkDotNetVersion }, - { "OsVersion", environmentInfo.OsVersion.Value }, - { "ProcessorName", ProcessorBrandStringHelper.Prettify(environmentInfo.CpuInfo.Value) }, - { "PhysicalProcessorCount", environmentInfo.CpuInfo.Value?.PhysicalProcessorCount }, - { "PhysicalCoreCount", environmentInfo.CpuInfo.Value?.PhysicalCoreCount }, - { "LogicalCoreCount", environmentInfo.CpuInfo.Value?.LogicalCoreCount }, + { "OsVersion", environmentInfo.Os.Value.ToBrandString() }, + { "ProcessorName", environmentInfo.Cpu.Value.ToShortBrandName() }, + { "PhysicalProcessorCount", environmentInfo.Cpu.Value?.PhysicalProcessorCount }, + { "PhysicalCoreCount", environmentInfo.Cpu.Value?.PhysicalCoreCount }, + { "LogicalCoreCount", environmentInfo.Cpu.Value?.LogicalCoreCount }, { nameof(environmentInfo.RuntimeVersion), environmentInfo.RuntimeVersion }, { nameof(environmentInfo.Architecture), environmentInfo.Architecture }, { nameof(environmentInfo.HasAttachedDebugger), environmentInfo.HasAttachedDebugger }, @@ -83,40 +84,40 @@ protected virtual IReadOnlyDictionary GetDataToSerialize(Benchma { "Statistics", report.ResultStatistics } }; - // We show MemoryDiagnoser's results only if it is being used - if (report.BenchmarkCase.Config.HasMemoryDiagnoser()) + // We show MemoryDiagnoser's results only if it is being used + if (report.BenchmarkCase.Config.HasMemoryDiagnoser()) + { + benchmark.Add("Memory", new { - benchmark.Add("Memory", new + report.GcStats.Gen0Collections, + report.GcStats.Gen1Collections, + report.GcStats.Gen2Collections, + report.GcStats.TotalOperations, + BytesAllocatedPerOperation = report.GcStats.GetBytesAllocatedPerOperation(report.BenchmarkCase) + }); + } + + if (ExcludeMeasurements == false) + { + // We construct Measurements manually, so that we can have the IterationMode enum as text, rather than an integer + benchmark.Add("Measurements", + report.AllMeasurements.Select(m => new { - report.GcStats.Gen0Collections, - report.GcStats.Gen1Collections, - report.GcStats.Gen2Collections, - report.GcStats.TotalOperations, - BytesAllocatedPerOperation = report.GcStats.GetBytesAllocatedPerOperation(report.BenchmarkCase) - }); - } + IterationMode = m.IterationMode.ToString(), + IterationStage = m.IterationStage.ToString(), + m.LaunchIndex, + m.IterationIndex, + m.Operations, + m.Nanoseconds + })); - if (ExcludeMeasurements == false) + if (report.Metrics.Any()) { - // We construct Measurements manually, so that we can have the IterationMode enum as text, rather than an integer - benchmark.Add("Measurements", - report.AllMeasurements.Select(m => new - { - IterationMode = m.IterationMode.ToString(), - IterationStage = m.IterationStage.ToString(), - m.LaunchIndex, - m.IterationIndex, - m.Operations, - m.Nanoseconds - })); - - if (report.Metrics.Any()) - { - benchmark.Add("Metrics", report.Metrics.Values); - } + benchmark.Add("Metrics", report.Metrics.Values); } + } - return benchmark; + return benchmark; } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Exporters/Json/SimpleJson.cs b/src/BenchmarkDotNet/Exporters/Json/SimpleJsonSerializer.cs similarity index 99% rename from src/BenchmarkDotNet/Exporters/Json/SimpleJson.cs rename to src/BenchmarkDotNet/Exporters/Json/SimpleJsonSerializer.cs index 5670d64588..e461c8a5c6 100644 --- a/src/BenchmarkDotNet/Exporters/Json/SimpleJson.cs +++ b/src/BenchmarkDotNet/Exporters/Json/SimpleJsonSerializer.cs @@ -105,7 +105,7 @@ public JsonArray(int capacity) : base(capacity) { } /// The json representation of the array. public override string ToString() { - return SimpleJson.SerializeObject(this) ?? string.Empty; + return SimpleJsonSerializer.SerializeObject(this) ?? string.Empty; } } @@ -344,7 +344,7 @@ IEnumerator IEnumerable.GetEnumerator() /// public override string ToString() { - return SimpleJson.SerializeObject(this); + return SimpleJsonSerializer.SerializeObject(this); } #if SIMPLE_JSON_DYNAMIC @@ -501,7 +501,7 @@ namespace SimpleJson #else public #endif - static class SimpleJson + static class SimpleJsonSerializer { private const int TOKEN_NONE = 0; private const int TOKEN_CURLY_OPEN = 1; @@ -522,7 +522,7 @@ static class SimpleJson private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; private static readonly string EscapeCharactersString = new string(EscapeCharacters); - static SimpleJson() + static SimpleJsonSerializer() { EscapeTable = new char[93]; EscapeTable['"'] = '"'; diff --git a/src/BenchmarkDotNet/Exporters/PhdJsonExporter.cs b/src/BenchmarkDotNet/Exporters/PhdJsonExporter.cs new file mode 100644 index 0000000000..25f14af3f0 --- /dev/null +++ b/src/BenchmarkDotNet/Exporters/PhdJsonExporter.cs @@ -0,0 +1,18 @@ +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Reports; +using Perfolizer.Json; + +namespace BenchmarkDotNet.Exporters; + +/// +/// IMPORTANT: Not fully implemented yet +/// +public class PhdJsonExporter(LightJsonSettings? jsonSettings = null) : ExporterBase +{ + protected override string FileExtension => "phd.json"; + + public override void ExportToLog(Summary summary, ILogger logger) + { + logger.WriteLine(LightJsonSerializer.Serialize(summary.ToPhd(), jsonSettings)); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Exporters/PhdMdExporter.cs b/src/BenchmarkDotNet/Exporters/PhdMdExporter.cs new file mode 100644 index 0000000000..bc1c1e4609 --- /dev/null +++ b/src/BenchmarkDotNet/Exporters/PhdMdExporter.cs @@ -0,0 +1,24 @@ +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Reports; +using Perfolizer.Json; +using Perfolizer.Phd.Presenting; +using Perfolizer.Phd.Tables; +using Perfolizer.Presenting; + +namespace BenchmarkDotNet.Exporters; + +/// +/// IMPORTANT: Not fully implemented yet +/// +public class PhdMdExporter : ExporterBase +{ + protected override string FileExtension => "phd.md"; + + public override void ExportToLog(Summary summary, ILogger logger) + { + var table = new PhdTable(summary.ToPhd()); + var presenter = new StringPresenter(); + new PhdMarkdownTablePresenter(presenter).Present(table, new PhdTableStyle()); + logger.WriteLine(presenter.Dump()); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Exporters/RPlotExporter.cs b/src/BenchmarkDotNet/Exporters/RPlotExporter.cs index 5c23f3b598..c90338f490 100644 --- a/src/BenchmarkDotNet/Exporters/RPlotExporter.cs +++ b/src/BenchmarkDotNet/Exporters/RPlotExporter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Exporters.Csv; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Loggers; @@ -82,7 +83,7 @@ public void ExportToLog(Summary summary, ILogger logger) private static bool TryFindRScript(ILogger consoleLogger, out string? rscriptPath) { - string rscriptExecutable = RuntimeInformation.IsWindows() ? "Rscript.exe" : "Rscript"; + string rscriptExecutable = OsDetector.IsWindows() ? "Rscript.exe" : "Rscript"; rscriptPath = null; string rHome = Environment.GetEnvironmentVariable("R_HOME"); @@ -101,7 +102,7 @@ private static bool TryFindRScript(ILogger consoleLogger, out string? rscriptPat if (rscriptPath != null) return true; - if (RuntimeInformation.IsWindows()) + if (OsDetector.IsWindows()) { string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); string programFilesR = Path.Combine(programFiles, "R"); diff --git a/src/BenchmarkDotNet/Exporters/Xml/SummaryDto.cs b/src/BenchmarkDotNet/Exporters/Xml/SummaryDto.cs index 79f8e51f1e..3087250b52 100644 --- a/src/BenchmarkDotNet/Exporters/Xml/SummaryDto.cs +++ b/src/BenchmarkDotNet/Exporters/Xml/SummaryDto.cs @@ -4,6 +4,7 @@ using BenchmarkDotNet.Mathematics; using BenchmarkDotNet.Reports; using JetBrains.Annotations; +using Perfolizer.Helpers; using Perfolizer.Horology; // ReSharper disable UnusedMember.Global @@ -34,11 +35,11 @@ internal class HostEnvironmentInfoDto { public string BenchmarkDotNetCaption => HostEnvironmentInfo.BenchmarkDotNetCaption; public string BenchmarkDotNetVersion => hei.BenchmarkDotNetVersion; - public string OsVersion => hei.OsVersion.Value; - public string ProcessorName => ProcessorBrandStringHelper.Prettify(hei.CpuInfo.Value); - public string PhysicalProcessorCount => hei.CpuInfo.Value?.PhysicalProcessorCount?.ToString(); - public string PhysicalCoreCount => hei.CpuInfo.Value?.PhysicalCoreCount?.ToString(); - public string LogicalCoreCount => hei.CpuInfo.Value?.LogicalCoreCount?.ToString(); + public string OsVersion => hei.Os.Value.ToBrandString(); + public string ProcessorName => hei.Cpu.Value.ToShortBrandName(); + public string PhysicalProcessorCount => hei.Cpu.Value?.PhysicalProcessorCount?.ToString(); + public string PhysicalCoreCount => hei.Cpu.Value?.PhysicalCoreCount?.ToString(); + public string LogicalCoreCount => hei.Cpu.Value?.LogicalCoreCount?.ToString(); public string RuntimeVersion => hei.RuntimeVersion; public string Architecture => hei.Architecture; public bool HasAttachedDebugger => hei.HasAttachedDebugger; @@ -72,6 +73,7 @@ internal class BenchmarkReportDto public string Parameters => report.BenchmarkCase.Parameters.PrintInfo; public Statistics Statistics => report.ResultStatistics; public IEnumerable Metrics => report.Metrics.Values; + public GcStats Memory => new GcStats() { Gen0Collections = report.GcStats.Gen0Collections, @@ -80,6 +82,7 @@ internal class BenchmarkReportDto TotalOperations = report.GcStats.TotalOperations, BytesAllocatedPerOperation = report.GcStats.GetBytesAllocatedPerOperation(report.BenchmarkCase) }; + [PublicAPI] public IEnumerable Measurements { get; } private readonly BenchmarkReport report; @@ -105,4 +108,4 @@ internal struct GcStats public long TotalOperations { get; set; } public long? BytesAllocatedPerOperation { get; set; } } -} +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Extensions/MathExtensions.cs b/src/BenchmarkDotNet/Extensions/MathExtensions.cs new file mode 100644 index 0000000000..86a17f93f5 --- /dev/null +++ b/src/BenchmarkDotNet/Extensions/MathExtensions.cs @@ -0,0 +1,9 @@ +using System; + +namespace BenchmarkDotNet.Extensions; + +internal static class MathExtensions +{ + public static int RoundToInt(this double x) => (int)Math.Round(x); + public static long RoundToLong(this double x) => (long)Math.Round(x); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs index 912babeb39..dd6bb5b55b 100644 --- a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using BenchmarkDotNet.Characteristics; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; @@ -86,7 +87,7 @@ public static bool TrySetAffinity( if (logger == null) throw new ArgumentNullException(nameof(logger)); - if (!RuntimeInformation.IsWindows() && !RuntimeInformation.IsLinux()) + if (!OsDetector.IsWindows() && !OsDetector.IsLinux()) return false; try @@ -108,7 +109,7 @@ public static bool TrySetAffinity( if (process == null) throw new ArgumentNullException(nameof(process)); - if (!RuntimeInformation.IsWindows() && !RuntimeInformation.IsLinux()) + if (!OsDetector.IsWindows() && !OsDetector.IsLinux()) return null; try @@ -163,7 +164,7 @@ internal static void SetEnvironmentVariables(this ProcessStartInfo start, Benchm public static void KillTree(this Process process, TimeSpan timeout) { - if (RuntimeInformation.IsWindows()) + if (OsDetector.IsWindows()) { RunProcessAndIgnoreOutput("taskkill", $"/T /F /PID {process.Id}", timeout); } diff --git a/src/BenchmarkDotNet/Helpers/ArtifactFileNameHelper.cs b/src/BenchmarkDotNet/Helpers/ArtifactFileNameHelper.cs index 62b4d31d94..b85f9fd297 100644 --- a/src/BenchmarkDotNet/Helpers/ArtifactFileNameHelper.cs +++ b/src/BenchmarkDotNet/Helpers/ArtifactFileNameHelper.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Extensions; @@ -26,7 +27,7 @@ internal static string GetFilePath(DiagnoserActionParameters details, string? su // long paths can be enabled on Windows but it does not mean that everything is going to work fine.. // so we always use 260 as limit on Windows - int limit = RuntimeInformation.IsWindows() + int limit = OsDetector.IsWindows() ? WindowsOldPathLimit - reserve : CommonSenseLimit; diff --git a/src/BenchmarkDotNet/Helpers/ExternalToolsHelper.cs b/src/BenchmarkDotNet/Helpers/ExternalToolsHelper.cs index 3802608047..bd5274c6b3 100644 --- a/src/BenchmarkDotNet/Helpers/ExternalToolsHelper.cs +++ b/src/BenchmarkDotNet/Helpers/ExternalToolsHelper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Portability; namespace BenchmarkDotNet.Helpers @@ -11,7 +12,7 @@ public static class ExternalToolsHelper /// MacOSX only. /// public static readonly Lazy> MacSystemProfilerData = - LazyParse(RuntimeInformation.IsMacOS, "system_profiler", "SPSoftwareDataType", s => SectionsHelper.ParseSection(s, ':')); + LazyParse(OsDetector.IsMacOS, "system_profiler", "SPSoftwareDataType", s => SectionsHelper.ParseSection(s, ':')); private static Lazy LazyParse(Func isAvailable, string fileName, string arguments, Func parseFunc) { diff --git a/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs b/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs index f66e21cdf6..94afb75311 100644 --- a/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs +++ b/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using BenchmarkDotNet.Detectors; namespace BenchmarkDotNet.Helpers { @@ -15,7 +16,7 @@ internal enum TaskbarProgressState internal class TaskbarProgress : IDisposable { - private static readonly bool OsVersionIsSupported = Portability.RuntimeInformation.IsWindows() + private static readonly bool OsVersionIsSupported = OsDetector.IsWindows() // Must be windows 7 or greater && Environment.OSVersion.Version >= new Version(6, 1); diff --git a/src/BenchmarkDotNet/Helpers/UserInteractionHelper.cs b/src/BenchmarkDotNet/Helpers/UserInteractionHelper.cs index 7951d168dd..73e83d6ada 100644 --- a/src/BenchmarkDotNet/Helpers/UserInteractionHelper.cs +++ b/src/BenchmarkDotNet/Helpers/UserInteractionHelper.cs @@ -1,3 +1,4 @@ +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Portability; namespace BenchmarkDotNet.Helpers @@ -19,7 +20,7 @@ internal static class UserInteractionHelper /// public static string EscapeCommandExample(string input) { - return !RuntimeInformation.IsWindows() && input.IndexOf('*') >= 0 ? $"'{input}'" : input; + return !OsDetector.IsWindows() && input.IndexOf('*') >= 0 ? $"'{input}'" : input; } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Jobs/EnvironmentMode.cs b/src/BenchmarkDotNet/Jobs/EnvironmentMode.cs index 8fe04e5764..b7b1d15394 100644 --- a/src/BenchmarkDotNet/Jobs/EnvironmentMode.cs +++ b/src/BenchmarkDotNet/Jobs/EnvironmentMode.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using BenchmarkDotNet.Characteristics; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Phd; using BenchmarkDotNet.Portability; using JetBrains.Annotations; @@ -14,7 +16,10 @@ public sealed class EnvironmentMode : JobMode public static readonly Characteristic RuntimeCharacteristic = CreateCharacteristic(nameof(Runtime)); public static readonly Characteristic AffinityCharacteristic = CreateCharacteristic(nameof(Affinity)); public static readonly Characteristic GcCharacteristic = CreateCharacteristic(nameof(Gc)); - public static readonly Characteristic> EnvironmentVariablesCharacteristic = CreateCharacteristic>(nameof(EnvironmentVariables)); + + public static readonly Characteristic> EnvironmentVariablesCharacteristic = + CreateCharacteristic>(nameof(EnvironmentVariables)); + public static readonly Characteristic PowerPlanModeCharacteristic = CreateCharacteristic(nameof(PowerPlanMode)); public static readonly Characteristic LargeAddressAwareCharacteristic = CreateCharacteristic(nameof(LargeAddressAware)); @@ -60,7 +65,7 @@ public Jit Jit /// /// Runtime /// - public Runtime Runtime + public Runtime? Runtime { get { return RuntimeCharacteristic[this]; } set { RuntimeCharacteristic[this] = value; } @@ -107,7 +112,7 @@ public bool LargeAddressAware get => LargeAddressAwareCharacteristic[this]; set { - if (value && !RuntimeInformation.IsWindows()) + if (value && !OsDetector.IsWindows()) { throw new NotSupportedException("LargeAddressAware is a Windows-specific concept."); } @@ -133,5 +138,12 @@ public void SetEnvironmentVariable(EnvironmentVariable variable) } internal Runtime GetRuntime() => HasValue(RuntimeCharacteristic) ? Runtime : RuntimeInformation.GetCurrentRuntime(); + + public BdnEnvironment ToPhd() => new () + { + Jit = HasValue(JitCharacteristic) ? Jit : null, + Runtime = HasValue(RuntimeCharacteristic) ? Runtime?.RuntimeMoniker : null, + Affinity = HasValue(AffinityCharacteristic) ? (int)Affinity : null + }; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Jobs/RunMode.cs b/src/BenchmarkDotNet/Jobs/RunMode.cs index a76b5dc832..070c1753b0 100644 --- a/src/BenchmarkDotNet/Jobs/RunMode.cs +++ b/src/BenchmarkDotNet/Jobs/RunMode.cs @@ -2,6 +2,8 @@ using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Phd; using Perfolizer.Horology; namespace BenchmarkDotNet.Jobs @@ -9,7 +11,8 @@ namespace BenchmarkDotNet.Jobs [SuppressMessage("ReSharper", "UnusedMember.Global")] public sealed class RunMode : JobMode { - public static readonly Characteristic RunStrategyCharacteristic = Characteristic.Create(nameof(RunStrategy), RunStrategy.Throughput); + public static readonly Characteristic RunStrategyCharacteristic = + Characteristic.Create(nameof(RunStrategy), RunStrategy.Throughput); public static readonly Characteristic LaunchCountCharacteristic = CreateCharacteristic(nameof(LaunchCount)); public static readonly Characteristic InvocationCountCharacteristic = CreateCharacteristic(nameof(InvocationCount)); @@ -61,13 +64,9 @@ public sealed class RunMode : JobMode }.Freeze(); - public RunMode() : this(null) - { - } + public RunMode() : this(null) { } - private RunMode(string id) : base(id) - { - } + private RunMode(string id) : base(id) { } /// /// Available values: Throughput and ColdStart. @@ -191,5 +190,21 @@ public bool MemoryRandomization get => MemoryRandomizationCharacteristic[this]; set => MemoryRandomizationCharacteristic[this] = value; } + + public BdnExecution ToPhd() => new () + { + LaunchCount = HasValue(LaunchCountCharacteristic) ? LaunchCount : null, + WarmupCount = HasValue(WarmupCountCharacteristic) ? WarmupCount : null, + IterationCount = HasValue(IterationCountCharacteristic) ? IterationCount : null, + IterationTimeMs = HasValue(IterationTimeCharacteristic) ? IterationTime.ToMilliseconds().RoundToLong() : null, + InvocationCount = HasValue(InvocationCountCharacteristic) ? InvocationCount : null, + UnrollFactor = HasValue(UnrollFactorCharacteristic) ? UnrollFactor : null, + MinIterationCount = HasValue(MinIterationCountCharacteristic) ? MinIterationCount : null, + MaxIterationCount = HasValue(MaxIterationCountCharacteristic) ? MaxIterationCount : null, + MinWarmupIterationCount = HasValue(MinWarmupIterationCountCharacteristic) ? MinWarmupIterationCount : null, + MaxWarmupIterationCount = HasValue(MaxWarmupIterationCountCharacteristic) ? MaxWarmupIterationCount : null, + MemoryRandomization = HasValue(MemoryRandomizationCharacteristic) ? MemoryRandomization : null, + RunStrategy = HasValue(RunStrategyCharacteristic) ? RunStrategy : null, + }; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs b/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs index 20aa2f7a54..d201f5da75 100644 --- a/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs +++ b/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Portability; using JetBrains.Annotations; @@ -15,7 +16,7 @@ public sealed class ConsoleLogger : ILogger public static readonly ILogger Ascii = new ConsoleLogger(false); public static readonly ILogger Unicode = new ConsoleLogger(true); private static readonly bool ConsoleSupportsColors - = !(RuntimeInformation.IsAndroid() || RuntimeInformation.IsIOS() || RuntimeInformation.IsWasm || RuntimeInformation.IsTvOS()); + = !(OsDetector.IsAndroid() || OsDetector.IsIOS() || RuntimeInformation.IsWasm || OsDetector.IsTvOS()); private readonly bool unicodeSupport; private readonly Dictionary colorScheme; diff --git a/src/BenchmarkDotNet/Phd/BdnBenchmark.cs b/src/BenchmarkDotNet/Phd/BdnBenchmark.cs new file mode 100644 index 0000000000..b52a84d30e --- /dev/null +++ b/src/BenchmarkDotNet/Phd/BdnBenchmark.cs @@ -0,0 +1,38 @@ +using System.Text; +using BenchmarkDotNet.Extensions; +using JetBrains.Annotations; +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Phd; + +[PublicAPI] +public class BdnBenchmark : PhdBenchmark +{ + public string Namespace { get; set; } = ""; + public string Type { get; set; } = ""; + public string Method { get; set; } = ""; + public string Parameters { get; set; } = ""; + public string? HardwareIntrinsics { get; set; } = ""; + + // TODO: Improve + public override string? GetDisplay() + { + if (Display != null) return Display; + + var builder = new StringBuilder(); + builder.Append($"{Namespace}"); + if (Type.IsNotBlank()) + { + if (builder.Length > 0) + builder.Append('.'); + builder.Append(Type); + } + if (Method.IsNotBlank()) + { + if (builder.Length > 0) + builder.Append('.'); + builder.Append(Method); + } + return builder.ToString(); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Phd/BdnEnvironment.cs b/src/BenchmarkDotNet/Phd/BdnEnvironment.cs new file mode 100644 index 0000000000..c250c9faeb --- /dev/null +++ b/src/BenchmarkDotNet/Phd/BdnEnvironment.cs @@ -0,0 +1,12 @@ +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Phd; + +public class BdnEnvironment : PhdEnvironment +{ + public RuntimeMoniker? Runtime { get; set; } + public Jit? Jit { get; set; } + public int? Affinity { get; set; } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Phd/BdnExecution.cs b/src/BenchmarkDotNet/Phd/BdnExecution.cs new file mode 100644 index 0000000000..13cad143aa --- /dev/null +++ b/src/BenchmarkDotNet/Phd/BdnExecution.cs @@ -0,0 +1,84 @@ +using BenchmarkDotNet.Analysers; +using BenchmarkDotNet.Engines; +using Perfolizer.Horology; +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Phd; + +public class BdnExecution : PhdExecution +{ + /// + /// Available values: Throughput and ColdStart. + /// Throughput: default strategy which allows to get good precision level. + /// ColdStart: should be used only for measuring cold start of the application or testing purpose. + /// Monitoring: no overhead evaluating, with several target iterations. Perfect for macrobenchmarks without a steady state with high variance. + /// + public RunStrategy? RunStrategy { get; set; } + + /// + /// How many times we should launch process with target benchmark. + /// + public int? LaunchCount { get; set; } + + /// + /// How many warmup iterations should be performed. + /// + public int? WarmupCount { get; set; } + + /// + /// How many target iterations should be performed + /// If specified, will be ignored. + /// If specified, will be ignored. + /// + public int? IterationCount { get; set; } + + /// + /// Desired time of execution of an iteration. Used by Pilot stage to estimate the number of invocations per iteration. + /// The default value is 500 milliseconds. + /// + public long? IterationTimeMs { get; set; } + + /// + /// Invocation count in a single iteration. + /// If specified, will be ignored. + /// If specified, it must be a multiple of . + /// + public long? InvocationCount { get; set; } + + /// + /// How many times the benchmark method will be invoked per one iteration of a generated loop. + /// + public int? UnrollFactor { get; set; } + + /// + /// Minimum count of target iterations that should be performed + /// The default value is 15 + /// If you set this value to below 15, then is not going to work + /// + public int? MinIterationCount { get; set; } + + /// + /// Maximum count of target iterations that should be performed + /// The default value is 100 + /// If you set this value to below 15, then is not going to work + /// + public int? MaxIterationCount { get; set; } + + /// + /// Minimum count of warmup iterations that should be performed + /// The default value is 6 + /// + public int? MinWarmupIterationCount { get; set; } + + /// + /// Maximum count of warmup iterations that should be performed + /// The default value is 50 + /// + public int? MaxWarmupIterationCount { get; set; } + + /// + /// specifies whether Engine should allocate some random-sized memory between iterations + /// it makes [GlobalCleanup] and [GlobalSetup] methods to be executed after every iteration + /// + public bool? MemoryRandomization { get; set; } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Phd/BdnHost.cs b/src/BenchmarkDotNet/Phd/BdnHost.cs new file mode 100644 index 0000000000..3c66d3f5d2 --- /dev/null +++ b/src/BenchmarkDotNet/Phd/BdnHost.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using Perfolizer.Phd; +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Phd; + +[PublicAPI] +public class BdnHost : PhdHost +{ + public string RuntimeVersion { get; set; } = ""; + public bool HasAttachedDebugger { get; set; } + public bool HasRyuJit { get; set; } + public string Configuration { get; set; } = ""; + public string DotNetSdkVersion { get; set; } = ""; + public double ChronometerFrequency { get; set; } + public string HardwareTimerKind { get; set; } = ""; +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Phd/BdnInfo.cs b/src/BenchmarkDotNet/Phd/BdnInfo.cs new file mode 100644 index 0000000000..85c5956392 --- /dev/null +++ b/src/BenchmarkDotNet/Phd/BdnInfo.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using Perfolizer.Phd; +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Phd; + +[PublicAPI] +public class BdnInfo : PhdInfo +{ + +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Phd/BdnJob.cs b/src/BenchmarkDotNet/Phd/BdnJob.cs new file mode 100644 index 0000000000..0400faf3c0 --- /dev/null +++ b/src/BenchmarkDotNet/Phd/BdnJob.cs @@ -0,0 +1,8 @@ +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Phd; + +public class BdnJob : PhdJob +{ + +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Phd/BdnLifecycle.cs b/src/BenchmarkDotNet/Phd/BdnLifecycle.cs new file mode 100644 index 0000000000..dafc65a880 --- /dev/null +++ b/src/BenchmarkDotNet/Phd/BdnLifecycle.cs @@ -0,0 +1,57 @@ +using System; +using BenchmarkDotNet.Engines; +using JetBrains.Annotations; +using Perfolizer.Phd; +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Phd; + +[PublicAPI] +public class BdnLifecycle : PhdLifecycle, IEquatable, IComparable +{ + public int LaunchIndex { get; set; } + public IterationStage IterationStage { get; set; } = IterationStage.Unknown; + public IterationMode IterationMode { get; set; } = IterationMode.Unknown; + + public bool Equals(BdnLifecycle? other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + return LaunchIndex == other.LaunchIndex && + IterationMode == other.IterationMode && + IterationStage == other.IterationStage; + } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != GetType()) + return false; + return Equals((BdnLifecycle)obj); + } + + public override int GetHashCode() => HashCode.Combine(LaunchIndex, IterationMode, IterationStage); + + public static bool operator ==(BdnLifecycle? left, BdnLifecycle? right) => Equals(left, right); + public static bool operator !=(BdnLifecycle? left, BdnLifecycle? right) => !Equals(left, right); + + public int CompareTo(BdnLifecycle? other) + { + if (ReferenceEquals(this, other)) + return 0; + if (ReferenceEquals(null, other)) + return 1; + int launchIndexComparison = LaunchIndex.CompareTo(other.LaunchIndex); + if (launchIndexComparison != 0) + return launchIndexComparison; + int iterationStageComparison = IterationStage.CompareTo(other.IterationStage); + if (iterationStageComparison != 0) + return iterationStageComparison; + return IterationMode.CompareTo(other.IterationMode); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Phd/BdnSchema.cs b/src/BenchmarkDotNet/Phd/BdnSchema.cs new file mode 100644 index 0000000000..dd63db3e3c --- /dev/null +++ b/src/BenchmarkDotNet/Phd/BdnSchema.cs @@ -0,0 +1,19 @@ +using Perfolizer.Phd.Base; + +namespace BenchmarkDotNet.Phd; + +public class BdnSchema : PhdSchema +{ + public static readonly BdnSchema Instance = new (); + + private BdnSchema() : base("bdn") + { + Add(); + Add(); + Add(); + Add(); + Add(); + Add(); + Add(); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/CompositeCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/CompositeCpuInfoDetector.cs deleted file mode 100644 index d6919d938f..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/CompositeCpuInfoDetector.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Linq; -using BenchmarkDotNet.Extensions; -using BenchmarkDotNet.Portability.Cpu.Linux; -using BenchmarkDotNet.Portability.Cpu.macOS; -using BenchmarkDotNet.Portability.Cpu.Windows; - -namespace BenchmarkDotNet.Portability.Cpu; - -internal class CompositeCpuInfoDetector(params ICpuInfoDetector[] detectors) : ICpuInfoDetector -{ - public bool IsApplicable() => detectors.Any(loader => loader.IsApplicable()); - - public CpuInfo? Detect() => detectors - .Where(loader => loader.IsApplicable()) - .Select(loader => loader.Detect()) - .WhereNotNull() - .FirstOrDefault(); -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/CpuInfo.cs b/src/BenchmarkDotNet/Portability/Cpu/CpuInfo.cs deleted file mode 100644 index ee7cf310c7..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/CpuInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using BenchmarkDotNet.Portability.Cpu.Linux; -using BenchmarkDotNet.Portability.Cpu.macOS; -using BenchmarkDotNet.Portability.Cpu.Windows; -using Perfolizer.Horology; - -namespace BenchmarkDotNet.Portability.Cpu; - -public class CpuInfo( - string? processorName, - int? physicalProcessorCount, - int? physicalCoreCount, - int? logicalCoreCount, - Frequency? nominalFrequency, - Frequency? maxFrequency = null) -{ - public static readonly CpuInfo Empty = new (null, null, null, null, null, null); - public static CpuInfo FromName(string processorName) => new (processorName, null, null, null, null); - public static CpuInfo FromNameAndFrequency(string processorName, Frequency nominalFrequency) => new (processorName, null, null, null, nominalFrequency); - - private static readonly CompositeCpuInfoDetector Detector = new ( - new WindowsCpuInfoDetector(), - new LinuxCpuInfoDetector(), - new MacOsCpuInfoDetector()); - - public static CpuInfo? DetectCurrent() => Detector.Detect(); - - public string? ProcessorName { get; } = processorName; - public int? PhysicalProcessorCount { get; } = physicalProcessorCount; - public int? PhysicalCoreCount { get; } = physicalCoreCount; - public int? LogicalCoreCount { get; } = logicalCoreCount; - public Frequency? NominalFrequency { get; } = nominalFrequency ?? maxFrequency; - public Frequency? MaxFrequency { get; } = maxFrequency ?? nominalFrequency; -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/CpuInfoFormatter.cs b/src/BenchmarkDotNet/Portability/Cpu/CpuInfoFormatter.cs deleted file mode 100644 index 88301271e4..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/CpuInfoFormatter.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using BenchmarkDotNet.Environments; - -namespace BenchmarkDotNet.Portability.Cpu; - -public static class CpuInfoFormatter -{ - public static string Format(CpuInfo? cpuInfo) - { - if (cpuInfo == null) - return "Unknown processor"; - - var parts = new List - { - ProcessorBrandStringHelper.Prettify(cpuInfo, includeMaxFrequency: true) - }; - - if (cpuInfo.PhysicalProcessorCount > 0) - parts.Add($", {cpuInfo.PhysicalProcessorCount} CPU"); - - switch (cpuInfo.LogicalCoreCount) - { - case 1: - parts.Add(", 1 logical core"); - break; - case > 1: - parts.Add($", {cpuInfo.LogicalCoreCount} logical cores"); - break; - } - - if (cpuInfo.LogicalCoreCount > 0 && cpuInfo.PhysicalCoreCount > 0) - parts.Add(" and "); - else if (cpuInfo.PhysicalCoreCount > 0) - parts.Add(", "); - - switch (cpuInfo.PhysicalCoreCount) - { - case 1: - parts.Add("1 physical core"); - break; - case > 1: - parts.Add($"{cpuInfo.PhysicalCoreCount} physical cores"); - break; - } - - string result = string.Join("", parts); - // The line with ProcessorBrandString is one of the longest lines in the summary. - // When people past in on GitHub, it can be a reason of an ugly horizontal scrollbar. - // To avoid this, we are trying to minimize this line and use the minimum possible number of characters. - // Here we are removing the repetitive "cores" word. - if (result.Contains("logical cores") && result.Contains("physical cores")) - result = result.Replace("logical cores", "logical"); - - return result; - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/ICpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/ICpuInfoDetector.cs deleted file mode 100644 index 748f9ce186..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/ICpuInfoDetector.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BenchmarkDotNet.Portability.Cpu; - -/// -/// Loads the for the current hardware -/// -internal interface ICpuInfoDetector -{ - bool IsApplicable(); - CpuInfo? Detect(); -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/Cpu/Windows/WindowsCpuInfoDetector.cs b/src/BenchmarkDotNet/Portability/Cpu/Windows/WindowsCpuInfoDetector.cs deleted file mode 100644 index 9846c8b12a..0000000000 --- a/src/BenchmarkDotNet/Portability/Cpu/Windows/WindowsCpuInfoDetector.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace BenchmarkDotNet.Portability.Cpu.Windows; - -internal class WindowsCpuInfoDetector() : CompositeCpuInfoDetector(new MosCpuInfoDetector(), new WmicCpuInfoDetector()); \ No newline at end of file diff --git a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs index 95a3874d87..ed4d457caa 100644 --- a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs +++ b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs @@ -8,15 +8,16 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Text.RegularExpressions; +using BenchmarkDotNet.Detectors; +using BenchmarkDotNet.Detectors.Cpu; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; -using BenchmarkDotNet.Portability.Cpu; -using BenchmarkDotNet.Portability.Cpu.Linux; -using BenchmarkDotNet.Portability.Cpu.macOS; -using BenchmarkDotNet.Portability.Cpu.Windows; using JetBrains.Annotations; using Microsoft.Win32; +using Perfolizer.Helpers; +using Perfolizer.Phd; +using Perfolizer.Phd.Dto; using static System.Runtime.InteropServices.RuntimeInformation; using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment; @@ -31,7 +32,8 @@ internal static class RuntimeInformation /// /// returns true for both the old (implementation of .NET Framework) and new Mono (.NET 6+ flavour) /// - public static bool IsMono { get; } = Type.GetType("Mono.RuntimeStructs") != null; // it allocates a lot of memory, we need to check it once in order to keep Engine non-allocating! + public static bool IsMono { get; } = + Type.GetType("Mono.RuntimeStructs") != null; // it allocates a lot of memory, we need to check it once in order to keep Engine non-allocating! public static bool IsOldMono { get; } = Type.GetType("Mono.Runtime") != null; @@ -49,12 +51,12 @@ internal static class RuntimeInformation public static bool IsNetCore => ((Environment.Version.Major >= 5) || FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase)) - && !string.IsNullOrEmpty(typeof(object).Assembly.Location); + && !string.IsNullOrEmpty(typeof(object).Assembly.Location); public static bool IsNativeAOT => Environment.Version.Major >= 5 - && string.IsNullOrEmpty(typeof(object).Assembly.Location) // it's merged to a single .exe and .Location returns null - && !IsWasm; // Wasm also returns "" for assembly locations + && string.IsNullOrEmpty(typeof(object).Assembly.Location) // it's merged to a single .exe and .Location returns null + && !IsWasm; // Wasm also returns "" for assembly locations #if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatformGuard("browser")] @@ -90,160 +92,9 @@ private static bool IsAotMethod() public static bool IsRunningInContainer => string.Equals(Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER"), "true"); - internal static string ExecutableExtension => IsWindows() ? ".exe" : string.Empty; - - internal static string ScriptFileExtension => IsWindows() ? ".bat" : ".sh"; internal static string GetArchitecture() => GetCurrentPlatform().ToString(); -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatformGuard("windows")] -#endif - internal static bool IsWindows() => -#if NET6_0_OR_GREATER - OperatingSystem.IsWindows(); // prefer linker-friendly OperatingSystem APIs -#else - IsOSPlatform(OSPlatform.Windows); -#endif - -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatformGuard("linux")] -#endif - internal static bool IsLinux() => -#if NET6_0_OR_GREATER - OperatingSystem.IsLinux(); -#else - IsOSPlatform(OSPlatform.Linux); -#endif - -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatformGuard("macos")] -#endif - internal static bool IsMacOS() => -#if NET6_0_OR_GREATER - OperatingSystem.IsMacOS(); -#else - IsOSPlatform(OSPlatform.OSX); -#endif - -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatformGuard("android")] -#endif - internal static bool IsAndroid() => -#if NET6_0_OR_GREATER - OperatingSystem.IsAndroid(); -#else - Type.GetType("Java.Lang.Object, Mono.Android") != null; -#endif - -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatformGuard("ios")] -#endif - internal static bool IsIOS() => -#if NET6_0_OR_GREATER - OperatingSystem.IsIOS(); -#else - Type.GetType("Foundation.NSObject, Xamarin.iOS") != null; -#endif - -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatformGuard("tvos")] -#endif - internal static bool IsTvOS() => -#if NET6_0_OR_GREATER - OperatingSystem.IsTvOS(); -#else - IsOSPlatform(OSPlatform.Create("TVOS")); -#endif - - public static string GetOsVersion() - { - if (IsMacOS()) - { - string systemVersion = ExternalToolsHelper.MacSystemProfilerData.Value.GetValueOrDefault("System Version") ?? ""; - string kernelVersion = ExternalToolsHelper.MacSystemProfilerData.Value.GetValueOrDefault("Kernel Version") ?? ""; - if (!string.IsNullOrEmpty(systemVersion) && !string.IsNullOrEmpty(kernelVersion)) - return OsBrandStringHelper.PrettifyMacOSX(systemVersion, kernelVersion); - } - - if (IsLinux()) - { - string? version = GetLinuxOsVersion(); - if (version != null && version.Trim() != "") - return version; - } - - string operatingSystem = RuntimeEnvironment.OperatingSystem; - string operatingSystemVersion = RuntimeEnvironment.OperatingSystemVersion; - - return OsBrandStringHelper.Prettify( - operatingSystem, - operatingSystemVersion, - GetWindowsUbr()); - } - - private static string? GetLinuxOsVersion() - { - if (!IsLinux()) - return null; - try - { - string version = LinuxOsReleaseHelper.GetNameByOsRelease(File.ReadAllLines("/etc/os-release")); - bool wsl = IsUnderWsl(); - return wsl ? version + " WSL" : version; - } - catch (Exception) - { - return null; - } - } - - private static bool IsUnderWsl() - { - if (!IsLinux()) - return false; - try - { - return File.Exists("/proc/sys/fs/binfmt_misc/WSLInterop"); // https://superuser.com/a/1749811 - } - catch (Exception) - { - return false; - } - } - - // TODO: Introduce a common util API for registry calls, use it also in BenchmarkDotNet.Toolchains.CsProj.GetCurrentVersionBasedOnWindowsRegistry - /// - /// On Windows, this method returns UBR (Update Build Revision) based on Registry. - /// Returns null if the value is not available - /// - /// - private static int? GetWindowsUbr() - { - if (IsWindows()) - { - try - { - using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) - using (var ndpKey = baseKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")) - { - if (ndpKey == null) - return null; - - return Convert.ToInt32(ndpKey.GetValue("UBR")); - } - } - catch (Exception) - { - return null; - } - } - return null; - } - - private static readonly Lazy LazyCpuInfo = new (CpuInfo.DetectCurrent); - internal static CpuInfo? GetCpuInfo() => LazyCpuInfo.Value; - internal static string GetRuntimeVersion() { if (IsWasm) @@ -307,7 +158,7 @@ internal static string GetRuntimeVersion() private static string GetNetCoreVersion() { - if (IsAndroid()) + if (OsDetector.IsAndroid()) { return $".NET {Environment.Version}"; } @@ -415,7 +266,7 @@ internal static string GetJitInfo() if (IsNetCore || HasRyuJit()) // CoreCLR supports only RyuJIT return "RyuJIT"; if (IsFullFramework) - return "LegacyJIT"; + return "LegacyJIT"; return Unknown; } @@ -468,7 +319,7 @@ public JitModule(string name, string version) internal static ICollection GetAntivirusProducts() { var products = new List(); - if (IsWindows()) + if (OsDetector.IsWindows()) { try { @@ -476,7 +327,7 @@ internal static ICollection GetAntivirusProducts() using (var data = wmi.Get()) foreach (var o in data) { - var av = (ManagementObject) o; + var av = (ManagementObject)o; if (av != null) { string name = av["displayName"].ToString(); @@ -494,11 +345,11 @@ internal static ICollection GetAntivirusProducts() return products; } - internal static VirtualMachineHypervisor GetVirtualMachineHypervisor() + internal static VirtualMachineHypervisor? GetVirtualMachineHypervisor() { - VirtualMachineHypervisor[] hypervisors = { HyperV.Default, VirtualBox.Default, VMware.Default }; + VirtualMachineHypervisor[] hypervisors = [HyperV.Default, VirtualBox.Default, VMware.Default]; - if (IsWindows()) + if (OsDetector.IsWindows()) { try { @@ -524,4 +375,4 @@ internal static VirtualMachineHypervisor GetVirtualMachineHypervisor() return null; } } -} +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Properties/BenchmarkDotNetInfo.cs b/src/BenchmarkDotNet/Properties/BenchmarkDotNetInfo.cs index fcb01a0055..f371a95990 100644 --- a/src/BenchmarkDotNet/Properties/BenchmarkDotNetInfo.cs +++ b/src/BenchmarkDotNet/Properties/BenchmarkDotNetInfo.cs @@ -1,11 +1,15 @@ using System; using System.Reflection; using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Phd; +using Perfolizer.Phd.Dto; namespace BenchmarkDotNet.Properties { public class BenchmarkDotNetInfo { + public const string BenchmarkDotNetCaption = "BenchmarkDotNet"; + private static readonly Lazy LazyInstance = new (() => { var assembly = typeof(BenchmarkDotNetInfo).GetTypeInfo().Assembly; @@ -16,6 +20,12 @@ public class BenchmarkDotNetInfo public static BenchmarkDotNetInfo Instance { get; } = LazyInstance.Value; + public PhdEngine GetBdnEngine() => new () + { + Name = BenchmarkDotNetCaption, + Version = BrandVersion + }; + public Version AssemblyVersion { get; } public string FullVersion { get; } diff --git a/src/BenchmarkDotNet/Reports/BenchmarkReport.cs b/src/BenchmarkDotNet/Reports/BenchmarkReport.cs index 744793c063..8a896a84bf 100644 --- a/src/BenchmarkDotNet/Reports/BenchmarkReport.cs +++ b/src/BenchmarkDotNet/Reports/BenchmarkReport.cs @@ -3,10 +3,18 @@ using System.Collections.Immutable; using System.Linq; using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Mathematics; +using BenchmarkDotNet.Phd; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.Results; using JetBrains.Annotations; +using Perfolizer.Horology; +using Perfolizer.Phd; +using Perfolizer.Phd.Base; +using Perfolizer.Phd.Dto; namespace BenchmarkDotNet.Reports { @@ -51,5 +59,54 @@ public BenchmarkReport( public override string ToString() => $"{BenchmarkCase.DisplayInfo}, {AllMeasurements.Count} runs"; public IReadOnlyList GetResultRuns() => AllMeasurements.Where(r => r.Is(IterationMode.Workload, IterationStage.Result)).ToList(); + + public PhdEntry ToPhd() + { + var entry = new PhdEntry + { + Benchmark = new BdnBenchmark + { + Display = BenchmarkCase.DisplayInfo, + Namespace = BenchmarkCase.Descriptor.Type.Namespace ?? "", + Type = FullNameProvider.GetTypeName(BenchmarkCase.Descriptor.Type), + Method = BenchmarkCase.Descriptor.WorkloadMethod.Name, + Parameters = BenchmarkCase.Parameters.PrintInfo, + HardwareIntrinsics = this.GetHardwareIntrinsicsInfo() + }, + Job = new BdnJob + { + Environment = BenchmarkCase.Job.Environment.ToPhd(), + Execution = BenchmarkCase.Job.Run.ToPhd() + } + }; + + var lifecycles = AllMeasurements.GroupBy(m => new BdnLifecycle + { + LaunchIndex = m.LaunchIndex, + IterationMode = m.IterationMode, + IterationStage = m.IterationStage + }).OrderBy(x => x.Key).ToList(); + foreach (var lifecycleGroup in lifecycles) + { + var measurementsEntry = new PhdEntry + { + Lifecycle = lifecycleGroup.Key + }; + + foreach (var measurement in lifecycleGroup.ToList()) + { + measurementsEntry.Add(new PhdEntry + { + IterationIndex = measurement.IterationIndex, + InvocationCount = measurement.Operations, + Value = measurement.Nanoseconds / measurement.Operations, + Unit = TimeUnit.Nanosecond + }); + } + entry.Add(measurementsEntry); + } + + return entry; + } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Reports/Summary.cs b/src/BenchmarkDotNet/Reports/Summary.cs index 3822ed8acb..2ccfd4628d 100644 --- a/src/BenchmarkDotNet/Reports/Summary.cs +++ b/src/BenchmarkDotNet/Reports/Summary.cs @@ -8,10 +8,15 @@ using BenchmarkDotNet.Environments; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Order; +using BenchmarkDotNet.Phd; using BenchmarkDotNet.Running; using BenchmarkDotNet.Validators; using JetBrains.Annotations; using Perfolizer.Horology; +using Perfolizer.Phd; +using Perfolizer.Phd.Base; +using Perfolizer.Phd.Dto; +using Perfolizer.Phd.Tables; namespace BenchmarkDotNet.Reports { @@ -62,7 +67,8 @@ public Summary( DisplayPrecisionManager = new DisplayPrecisionManager(this); Orderer = GetConfiguredOrdererOrDefaultOne(reports.Select(report => report.BenchmarkCase.Config)); - BenchmarksCases = Orderer.GetSummaryOrder(reports.Select(report => report.BenchmarkCase).ToImmutableArray(), this).ToImmutableArray(); // we sort it first + BenchmarksCases = + Orderer.GetSummaryOrder(reports.Select(report => report.BenchmarkCase).ToImmutableArray(), this).ToImmutableArray(); // we sort it first Reports = BenchmarksCases.Select(b => ReportMap[b]).ToImmutableArray(); // we use sorted collection to re-create reports list BaseliningStrategy = BaseliningStrategy.Create(BenchmarksCases); Style = (summaryStyle ?? GetConfiguredSummaryStyleOrDefaultOne(BenchmarksCases)).WithCultureInfo(cultureInfo); @@ -84,8 +90,10 @@ public Summary( 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? validationErrors = null) - => new Summary(title, ImmutableArray.Empty, HostEnvironmentInfo.GetCurrent(), resultsDirectoryPath, logFilePath, TimeSpan.Zero, DefaultCultureInfo.Instance, validationErrors ?? ImmutableArray.Empty, ImmutableArray.Empty); + internal static Summary ValidationFailed(string title, string resultsDirectoryPath, string logFilePath, + ImmutableArray? validationErrors = null) + => new Summary(title, ImmutableArray.Empty, HostEnvironmentInfo.GetCurrent(), resultsDirectoryPath, logFilePath, TimeSpan.Zero, + DefaultCultureInfo.Instance, validationErrors ?? ImmutableArray.Empty, ImmutableArray.Empty); internal static Summary Join(List summaries, ClockSpan clockSpan) => new Summary( @@ -167,5 +175,37 @@ private static SummaryStyle GetConfiguredSummaryStyleOrDefaultOne(ImmutableArray .Distinct() .SingleOrDefault() ?? SummaryStyle.Default; + + // TODO: GcStats + public PhdEntry ToPhd() + { + var tableConfig = new PhdTableConfig + { + ColumnDefinitions = + [ + new PhdColumnDefinition(".engine") { Cloud = "primary", IsSelfExplanatory = true, IsAtomic = true }, + new PhdColumnDefinition(".host.os") { Cloud = "primary", IsSelfExplanatory = true, IsAtomic = true }, + new PhdColumnDefinition(".host.cpu") { Cloud = "primary", IsSelfExplanatory = true, IsAtomic = true }, + new PhdColumnDefinition(".benchmark") { Cloud = "secondary" }, + new PhdColumnDefinition(".job") { Cloud = "secondary", Compressed = true }, + new PhdColumnDefinition("=center"), + new PhdColumnDefinition("=spread") + ] + }; + + var root = new PhdEntry + { + Engine = new PhdEngine + { + Name = HostEnvironmentInfo.BenchmarkDotNetCaption, + Version = HostEnvironmentInfo.BenchmarkDotNetVersion, + }, + Host = HostEnvironmentInfo.ToPhd(), + Meta = new PhdMeta { Table = tableConfig } + }; + foreach (var benchmarkReport in Reports) + root.Add(benchmarkReport.ToPhd()); + return root; + } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs index 4a1e85eee9..e8088702f1 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Environments; @@ -43,7 +44,7 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos) var artifactsToCleanup = new List(); var rootArtifactsFolderPath = GetRootArtifactsFolderPath(benchmarkRunInfos); - var maxTitleLength = RuntimeInformation.IsWindows() + var maxTitleLength = OsDetector.IsWindows() ? 254 - rootArtifactsFolderPath.Length : int.MaxValue; var title = GetTitle(benchmarkRunInfos, maxTitleLength); diff --git a/src/BenchmarkDotNet/Running/ConsoleTitler.cs b/src/BenchmarkDotNet/Running/ConsoleTitler.cs index 9f8e21a1fe..7458388c42 100644 --- a/src/BenchmarkDotNet/Running/ConsoleTitler.cs +++ b/src/BenchmarkDotNet/Running/ConsoleTitler.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Portability; namespace BenchmarkDotNet.Running @@ -62,7 +63,7 @@ public ConsoleTitler(string initialTitle) #if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatformGuard("windows")] #endif - private static bool PlatformSupportsTitleRead() => RuntimeInformation.IsWindows(); + private static bool PlatformSupportsTitleRead() => OsDetector.IsWindows(); /// /// Updates Console.Title if enabled. diff --git a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs index fdfa0239f5..f240c1a209 100644 --- a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs +++ b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Loggers; @@ -33,7 +34,7 @@ internal class PowerManagementApplier : IDisposable internal void ApplyPerformancePlan(Guid id) { - if (!RuntimeInformation.IsWindows() || id == Guid.Empty) + if (!OsDetector.IsWindows() || id == Guid.Empty) return; if (id != UserPowerPlan) @@ -44,7 +45,7 @@ internal void ApplyPerformancePlan(Guid id) private void ApplyUserPowerPlan() { - if (powerPlanChanged && RuntimeInformation.IsWindows()) + if (powerPlanChanged && OsDetector.IsWindows()) { try { diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs index 187458be17..2761bd7c2c 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using BenchmarkDotNet.Characteristics; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Portability; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.DotNetCli; @@ -44,7 +45,7 @@ public override IEnumerable Validate(BenchmarkCase benchmarkCas yield return validationError; } - if (!RuntimeInformation.IsWindows()) + if (!OsDetector.IsWindows()) { yield return new ValidationError(true, $"Classic .NET toolchain is supported only for Windows, benchmark '{benchmarkCase.DisplayInfo}' will not be executed", diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/CustomDotNetCliToolchainBuilder.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/CustomDotNetCliToolchainBuilder.cs index dc48965f1e..3e56c6be24 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/CustomDotNetCliToolchainBuilder.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/CustomDotNetCliToolchainBuilder.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Portability; using JetBrains.Annotations; @@ -127,7 +128,7 @@ internal static string GetPortableRuntimeIdentifier() // Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier() // returns win10-x64, we want the simpler form win-x64 // the values taken from https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#macos-rids - string osPart = RuntimeInformation.IsWindows() ? "win" : (RuntimeInformation.IsMacOS() ? "osx" : "linux"); + string osPart = OsDetector.IsWindows() ? "win" : (OsDetector.IsMacOS() ? "osx" : "linux"); string architecture = #if NETSTANDARD diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs index 10ce4ef15d..5cbff89b47 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Jobs; @@ -137,7 +138,7 @@ internal static ProcessStartInfo BuildStartInfo(string? customDotNetCliPath, str private static string GetDefaultDotNetCliPath() { - if (!Portability.RuntimeInformation.IsLinux()) + if (!OsDetector.IsLinux()) return "dotnet"; using (var parentProcess = Process.GetProcessById(libc.getppid())) diff --git a/src/BenchmarkDotNet/Toolchains/GeneratorBase.cs b/src/BenchmarkDotNet/Toolchains/GeneratorBase.cs index cab9d811c4..020788cc56 100644 --- a/src/BenchmarkDotNet/Toolchains/GeneratorBase.cs +++ b/src/BenchmarkDotNet/Toolchains/GeneratorBase.cs @@ -2,6 +2,7 @@ using System.IO; using System.Text; using BenchmarkDotNet.Code; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Portability; @@ -60,7 +61,7 @@ protected virtual string GetIntermediateDirectoryPath(string buildArtifactsDirec /// returns OS-specific executable extension /// [PublicAPI] protected virtual string GetExecutableExtension() - => RuntimeInformation.ExecutableExtension; + => OsDetector.ExecutableExtension; /// /// returns a path to the auto-generated .csproj file @@ -142,7 +143,7 @@ private ArtifactsPaths GetArtifactsPaths(BuildPartition buildPartition, string r appConfigPath: $"{executablePath}.config", nuGetConfigPath: Path.Combine(buildArtifactsDirectoryPath, "NuGet.config"), projectFilePath: GetProjectFilePath(buildArtifactsDirectoryPath), - buildScriptFilePath: Path.Combine(buildArtifactsDirectoryPath, $"{programName}{RuntimeInformation.ScriptFileExtension}"), + buildScriptFilePath: Path.Combine(buildArtifactsDirectoryPath, $"{programName}{OsDetector.ScriptFileExtension}"), executablePath: executablePath, programName: programName, packagesDirectoryName: GetPackagesDirectoryPath(buildArtifactsDirectoryPath)); diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs index 64d2019a75..60d6f8a6ae 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Threading; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Environments; @@ -59,7 +60,7 @@ public ExecuteResult Execute(ExecuteParameters executeParameters) if (executeParameters.BenchmarkCase.Descriptor.WorkloadMethod .GetCustomAttributes(false) .Any() && - Portability.RuntimeInformation.IsWindows()) + OsDetector.IsWindows()) { runThread.SetApartmentState(ApartmentState.STA); } diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs index 49cd24e3d4..b50ba77c1c 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Threading; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Extensions; @@ -59,7 +60,7 @@ public ExecuteResult Execute(ExecuteParameters executeParameters) var runThread = new Thread(() => exitCode = ExecuteCore(host, executeParameters)); if (executeParameters.BenchmarkCase.Descriptor.WorkloadMethod.GetCustomAttributes(false).Any() && - Portability.RuntimeInformation.IsWindows()) + OsDetector.IsWindows()) { runThread.SetApartmentState(ApartmentState.STA); } diff --git a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs index 833c113d84..de3cef53d3 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs @@ -1,6 +1,7 @@ using System.IO; using System.Text; using System.Xml; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Loggers; @@ -54,7 +55,7 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts } protected override string GetExecutablePath(string binariesDirectoryPath, string programName) - => Portability.RuntimeInformation.IsWindows() + => OsDetector.IsWindows() ? Path.Combine(binariesDirectoryPath, "publish", $"{programName}.exe") : Path.Combine(binariesDirectoryPath, "publish", programName); diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs index 42e5c428d2..71ad2ade2b 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using BenchmarkDotNet.Characteristics; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Portability; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.DotNetCli; @@ -26,7 +27,7 @@ public override IEnumerable Validate(BenchmarkCase benchmarkCas yield return validationError; } - if (RuntimeInformation.IsWindows()) + if (OsDetector.IsWindows()) { yield return new ValidationError(true, $"{nameof(WasmToolchain)} is supported only on Unix, benchmark '{benchmarkCase.DisplayInfo}' might not work correctly", diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs index 2f2a6d80f3..c9c964d7eb 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs @@ -4,12 +4,13 @@ using System.Linq; using System.Text; using BenchmarkDotNet.ConsoleArguments; +using BenchmarkDotNet.Detectors; +using BenchmarkDotNet.Detectors.Cpu; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Portability; -using BenchmarkDotNet.Portability.Cpu; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.CsProj; using BenchmarkDotNet.Toolchains.DotNetCli; @@ -57,7 +58,7 @@ internal Generator(string ilCompilerVersion, private readonly string ilcOptimizationPreference; private readonly string ilcInstructionSet; - protected override string GetExecutableExtension() => RuntimeInformation.ExecutableExtension; + protected override string GetExecutableExtension() => OsDetector.ExecutableExtension; protected override string GetBuildArtifactsDirectoryPath(BuildPartition buildPartition, string programName) => useTempFolderForRestore diff --git a/src/BenchmarkDotNet/Toolchains/Roslyn/Generator.cs b/src/BenchmarkDotNet/Toolchains/Roslyn/Generator.cs index 0718d6b8cb..3ba7054f7a 100644 --- a/src/BenchmarkDotNet/Toolchains/Roslyn/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/Roslyn/Generator.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Reflection; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Portability; using BenchmarkDotNet.Running; @@ -27,9 +28,9 @@ protected override string[] GetArtifactsToCleanup(ArtifactsPaths artifactsPaths) protected override void GenerateBuildScript(BuildPartition buildPartition, ArtifactsPaths artifactsPaths) { - string prefix = RuntimeInformation.IsWindows() ? "" : "#!/bin/bash\n"; + string prefix = OsDetector.IsWindows() ? "" : "#!/bin/bash\n"; var list = new List(); - if (!RuntimeInformation.IsWindows()) + if (!OsDetector.IsWindows()) list.Add("mono"); list.Add("csc"); list.Add("/noconfig"); diff --git a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs index c2872810fc..499d5d8f40 100644 --- a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs +++ b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs @@ -1,4 +1,5 @@ using System; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; @@ -46,9 +47,9 @@ internal static IToolchain GetToolchain(this Runtime runtime, Descriptor? descri : CsProjClassicNetToolchain.From(clrRuntime.MsBuildMoniker); case MonoRuntime mono: - if (RuntimeInformation.IsAndroid()) + if (OsDetector.IsAndroid()) return InProcessEmitToolchain.Instance; - if (RuntimeInformation.IsIOS()) + if (OsDetector.IsIOS()) return InProcessNoEmitToolchain.Instance; if (!string.IsNullOrEmpty(mono.AotArgs)) return MonoAotToolchain.Instance; diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs index b1309efdf0..29bf8275dd 100644 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/ExpectedBenchmarkResultsTests.cs @@ -4,6 +4,7 @@ using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Extensions; @@ -110,7 +111,7 @@ private void AssertZeroResults(Type benchmarkType, IConfig config) .AddDiagnoser(new MemoryDiagnoser(new MemoryDiagnoserConfig(false))) ); - var cpuResolution = RuntimeInformation.GetCpuInfo()?.MaxFrequency?.ToResolution() ?? FallbackCpuResolutionValue; + var cpuResolution = CpuDetector.Cpu?.MaxFrequency()?.ToResolution() ?? FallbackCpuResolutionValue; var threshold = new NumberValue(cpuResolution.Nanoseconds).ToThreshold(); foreach (var report in summary.Reports) @@ -163,7 +164,7 @@ private void AssertDifferentSizedStructsResults(IConfig config) .AddDiagnoser(new MemoryDiagnoser(new MemoryDiagnoserConfig(false))) ); - var cpuResolution = RuntimeInformation.GetCpuInfo().MaxFrequency?.ToResolution() ?? FallbackCpuResolutionValue; + var cpuResolution = CpuDetector.Cpu?.MaxFrequency()?.ToResolution() ?? FallbackCpuResolutionValue; var threshold = (cpuResolution / 2).ToThreshold(); foreach (var report in summary.Reports) diff --git a/tests/BenchmarkDotNet.IntegrationTests/BuildTimeoutTests.cs b/tests/BenchmarkDotNet.IntegrationTests/BuildTimeoutTests.cs index e672bcc906..101fa50139 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BuildTimeoutTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/BuildTimeoutTests.cs @@ -1,6 +1,7 @@ using System; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; @@ -19,7 +20,7 @@ public void WhenBuildTakesMoreTimeThanTheTimeoutTheBuildIsCancelled() { if (!RuntimeInformation.Is64BitPlatform()) // NativeAOT does not support 32bit yet return; - if (RuntimeInformation.IsMacOS()) + if (OsDetector.IsMacOS()) return; // currently not supported // we use NativeAOT on purpose because it takes a LOT of time to build it diff --git a/tests/BenchmarkDotNet.IntegrationTests/ContinuousIntegration.cs b/tests/BenchmarkDotNet.IntegrationTests/ContinuousIntegration.cs index 0f112aa328..8b8710efe5 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/ContinuousIntegration.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/ContinuousIntegration.cs @@ -1,5 +1,6 @@ using BenchmarkDotNet.Portability; using System; +using BenchmarkDotNet.Detectors; namespace BenchmarkDotNet.IntegrationTests { @@ -8,7 +9,7 @@ internal static class ContinuousIntegration private static bool IsGitHubActions() => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTION")); internal static bool IsGitHubActionsOnWindows() - => RuntimeInformation.IsWindows() && IsGitHubActions(); + => OsDetector.IsWindows() && IsGitHubActions(); internal static bool IsLocalRun() => !IsGitHubActions(); } diff --git a/tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs b/tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs index bd2f6d48c2..26c17eed04 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs @@ -5,6 +5,7 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Disassemblers; using BenchmarkDotNet.Engines; @@ -36,12 +37,12 @@ public static IEnumerable GetAllJits() { yield return new object[] { Jit.RyuJit, Platform.X64, CoreRuntime.Core80 }; // .NET Core x64 } - else if (RuntimeInformation.GetCurrentPlatform() is Platform.Arm64 && RuntimeInformation.IsLinux()) + else if (RuntimeInformation.GetCurrentPlatform() is Platform.Arm64 && OsDetector.IsLinux()) { yield return new object[] { Jit.RyuJit, Platform.Arm64, CoreRuntime.Core80 }; // .NET Core arm64 } } - if (RuntimeInformation.IsMacOS()) + if (OsDetector.IsMacOS()) { // This scope of tests is not supported on macOS // However, when the MemberData method provides no data, xUnit throws an "No data found" InvalidOperationException @@ -91,7 +92,7 @@ public void Recursive() [Trait(Constants.Category, Constants.BackwardCompatibilityCategory)] public void CanDisassembleAllMethodCalls(Jit jit, Platform platform, Runtime runtime) { - if (RuntimeInformation.IsMacOS()) return; // currently not supported + if (OsDetector.IsMacOS()) return; // currently not supported var disassemblyDiagnoser = new DisassemblyDiagnoser( new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3)); @@ -110,7 +111,7 @@ public void CanDisassembleAllMethodCalls(Jit jit, Platform platform, Runtime run [Trait(Constants.Category, Constants.BackwardCompatibilityCategory)] public void CanDisassembleAllMethodCallsUsingFilters(Jit jit, Platform platform, Runtime runtime) { - if (RuntimeInformation.IsMacOS()) return; // currently not supported + if (OsDetector.IsMacOS()) return; // currently not supported var disassemblyDiagnoser = new DisassemblyDiagnoser( new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 1, filters: new[] { "*WithCalls*" })); @@ -135,7 +136,7 @@ public void CanDisassembleAllMethodCallsUsingFilters(Jit jit, Platform platform, [Trait(Constants.Category, Constants.BackwardCompatibilityCategory)] public void CanDisassembleGenericTypes(Jit jit, Platform platform, Runtime runtime) { - if (RuntimeInformation.IsMacOS()) return; // currently not supported + if (OsDetector.IsMacOS()) return; // currently not supported var disassemblyDiagnoser = new DisassemblyDiagnoser( new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3)); @@ -157,7 +158,7 @@ [Benchmark] public void JustReturn() { } [Trait(Constants.Category, Constants.BackwardCompatibilityCategory)] public void CanDisassembleInlinableBenchmarks(Jit jit, Platform platform, Runtime runtime) { - if (RuntimeInformation.IsMacOS()) return; // currently not supported + if (OsDetector.IsMacOS()) return; // currently not supported var disassemblyDiagnoser = new DisassemblyDiagnoser( new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3)); diff --git a/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs b/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs index 1e8dc79d00..2f21fb9f3c 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs @@ -4,6 +4,7 @@ using System.Linq; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnostics.dotMemory; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; @@ -20,7 +21,7 @@ public DotMemoryTests(ITestOutputHelper output) : base(output) { } [Fact] public void DotMemorySmokeTest() { - if (!RuntimeInformation.IsWindows() && RuntimeInformation.IsMono) + if (!OsDetector.IsWindows() && RuntimeInformation.IsMono) { Output.WriteLine("Skip Mono on non-Windows"); return; diff --git a/tests/BenchmarkDotNet.IntegrationTests/DotTraceTests.cs b/tests/BenchmarkDotNet.IntegrationTests/DotTraceTests.cs index 80f11519e8..3c40ee31a0 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/DotTraceTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/DotTraceTests.cs @@ -4,6 +4,7 @@ using System.Linq; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnostics.dotTrace; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; @@ -20,7 +21,7 @@ public DotTraceTests(ITestOutputHelper output) : base(output) { } [Fact] public void DotTraceSmokeTest() { - if (!RuntimeInformation.IsWindows() && RuntimeInformation.IsMono) + if (!OsDetector.IsWindows() && RuntimeInformation.IsMono) { Output.WriteLine("Skip Mono on non-Windows"); return; diff --git a/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs b/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs index fc6bc14362..db3dddfe2e 100755 --- a/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.IntegrationTests.Xunit; @@ -69,7 +70,7 @@ public void MemoryDiagnoserIsAccurate(IToolchain toolchain) [FactEnvSpecific("We don't want to test NativeAOT twice (for .NET Framework 4.6.2 and .NET 7.0)", EnvRequirement.DotNetCoreOnly)] public void MemoryDiagnoserSupportsNativeAOT() { - if (RuntimeInformation.IsMacOS()) + if (OsDetector.IsMacOS()) return; // currently not supported MemoryDiagnoserIsAccurate(NativeAotToolchain.Net80); diff --git a/tests/BenchmarkDotNet.IntegrationTests/NativeAotTests.cs b/tests/BenchmarkDotNet.IntegrationTests/NativeAotTests.cs index ce39b0572e..169fb3a0b3 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/NativeAotTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/NativeAotTests.cs @@ -1,6 +1,7 @@ using System; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Environments; using BenchmarkDotNet.IntegrationTests.Xunit; using BenchmarkDotNet.Jobs; @@ -22,7 +23,7 @@ public void LatestNativeAotVersionIsSupported() return; if (ContinuousIntegration.IsGitHubActionsOnWindows()) // no native dependencies installed return; - if (RuntimeInformation.IsMacOS()) + if (OsDetector.IsMacOS()) return; // currently not supported var toolchain = NativeAotToolchain.CreateBuilder().UseNuGet().IlcInstructionSet(IsAvx2Supported() ? "avx2" : "").ToToolchain(); diff --git a/tests/BenchmarkDotNet.IntegrationTests/ThreadingDiagnoserTests.cs b/tests/BenchmarkDotNet.IntegrationTests/ThreadingDiagnoserTests.cs index 213bc3af02..724a882e22 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/ThreadingDiagnoserTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/ThreadingDiagnoserTests.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.IntegrationTests.Xunit; using BenchmarkDotNet.Portability; using Xunit; @@ -32,7 +33,7 @@ public static IEnumerable GetToolchains() yield return new object[] { Job.Default.GetToolchain() }; if (!ContinuousIntegration.IsGitHubActionsOnWindows() // no native dependencies - && !RuntimeInformation.IsMacOS()) // currently not supported + && !OsDetector.IsMacOS()) // currently not supported { yield return new object[]{ NativeAotToolchain.Net80 }; } diff --git a/tests/BenchmarkDotNet.Tests/Attributes/ParamsAllValuesVerifyTests.cs b/tests/BenchmarkDotNet.Tests/Attributes/ParamsAllValuesVerifyTests.cs index a1086da1de..f766f0eba3 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/ParamsAllValuesVerifyTests.cs +++ b/tests/BenchmarkDotNet.Tests/Attributes/ParamsAllValuesVerifyTests.cs @@ -9,6 +9,7 @@ using BenchmarkDotNet.Tests.Mocks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Tests.Builders; +using BenchmarkDotNet.Tests.Infra; using BenchmarkDotNet.Validators; using JetBrains.Annotations; using VerifyXunit; @@ -53,7 +54,7 @@ public Task BenchmarkShouldProduceSummary(Type benchmarkType) foreach (var error in errors) logger.WriteLineError("* " + error.Message); - var settings = VerifySettingsFactory.Create(); + var settings = VerifyHelper.Create(); settings.UseTextForParameters(benchmarkType.Name); return Verifier.Verify(logger.GetLog(), settings); } diff --git a/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj b/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj index fcdc416bbf..d0011e1d64 100755 --- a/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj +++ b/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj @@ -1,5 +1,5 @@  - + BenchmarkDotNet.Tests net8.0;net462 @@ -15,10 +15,10 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -40,9 +40,19 @@ - + - + + + + + CpuInfoFormatterTests + CpuInfoFormatterTests.cs + + + + + diff --git a/tests/BenchmarkDotNet.Tests/Builders/HostEnvironmentInfoBuilder.cs b/tests/BenchmarkDotNet.Tests/Builders/HostEnvironmentInfoBuilder.cs index aadebcab6d..0c0737ecbb 100644 --- a/tests/BenchmarkDotNet.Tests/Builders/HostEnvironmentInfoBuilder.cs +++ b/tests/BenchmarkDotNet.Tests/Builders/HostEnvironmentInfoBuilder.cs @@ -1,11 +1,14 @@ using System; +using System.Diagnostics.CodeAnalysis; using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Portability; -using BenchmarkDotNet.Portability.Cpu; using Perfolizer.Horology; +using Perfolizer.Phd.Dto; namespace BenchmarkDotNet.Tests.Builders { + [SuppressMessage("ReSharper", "InconsistentNaming")] public class HostEnvironmentInfoBuilder { private string architecture = "64mock"; @@ -20,16 +23,18 @@ public class HostEnvironmentInfoBuilder private bool isServerGC = false; private string jitInfo = "RyuJIT-v4.6.x.mock"; private string jitModules = "clrjit-v4.6.x.mock"; - private string osVersion = "Microsoft Windows NT 10.0.x.mock"; + private PhdOs os = new () { Display = "Microsoft Windows NT 10.0.x.mock" }; private string runtimeVersion = "Clr 4.0.x.mock"; - private CpuInfo cpuInfo = - new ("MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", - physicalProcessorCount: 1, - physicalCoreCount: 4, - logicalCoreCount: 8, - nominalFrequency: Frequency.FromMHz(3100), - maxFrequency: Frequency.FromMHz(3100)); + private readonly PhdCpu cpu = new () + { + ProcessorName = "MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", + PhysicalProcessorCount = 1, + PhysicalCoreCount = 4, + LogicalCoreCount = 8, + NominalFrequencyHz = Frequency.FromMHz(3100).Hertz.RoundToLong(), + MaxFrequencyHz = Frequency.FromMHz(3100).Hertz.RoundToLong() + }; private VirtualMachineHypervisor? virtualMachineHypervisor = HyperV.Default; @@ -55,7 +60,7 @@ public HostEnvironmentInfo Build() { return new MockHostEnvironmentInfo(architecture, benchmarkDotNetVersion, chronometerFrequency, configuration, dotNetSdkVersion, hardwareTimerKind, hasAttachedDebugger, hasRyuJit, isConcurrentGC, isServerGC, - jitInfo, jitModules, osVersion, cpuInfo, runtimeVersion, virtualMachineHypervisor); + jitInfo, jitModules, os, cpu, runtimeVersion, virtualMachineHypervisor); } } @@ -64,7 +69,7 @@ internal class MockHostEnvironmentInfo : HostEnvironmentInfo public MockHostEnvironmentInfo( string architecture, string benchmarkDotNetVersion, Frequency chronometerFrequency, string configuration, string dotNetSdkVersion, HardwareTimerKind hardwareTimerKind, bool hasAttachedDebugger, bool hasRyuJit, bool isConcurrentGC, bool isServerGC, - string jitInfo, string jitModules, string osVersion, CpuInfo cpuInfo, + string jitInfo, string jitModules, PhdOs os, PhdCpu cpu, string runtimeVersion, VirtualMachineHypervisor virtualMachineHypervisor) { Architecture = architecture; @@ -79,8 +84,8 @@ public MockHostEnvironmentInfo( IsServerGC = isServerGC; JitInfo = jitInfo; HardwareIntrinsicsShort = ""; - OsVersion = new Lazy(() => osVersion); - CpuInfo = new Lazy(() => cpuInfo); + Os = new Lazy(() => os); + Cpu = new Lazy(() => cpu); RuntimeVersion = runtimeVersion; VirtualMachineHypervisor = new Lazy(() => virtualMachineHypervisor); } diff --git a/tests/BenchmarkDotNet.Tests/Builders/VerifySettingsFactory.cs b/tests/BenchmarkDotNet.Tests/Builders/VerifySettingsFactory.cs deleted file mode 100644 index 027b5272a7..0000000000 --- a/tests/BenchmarkDotNet.Tests/Builders/VerifySettingsFactory.cs +++ /dev/null @@ -1,15 +0,0 @@ -using VerifyTests; - -namespace BenchmarkDotNet.Tests.Builders -{ - public static class VerifySettingsFactory - { - public static VerifySettings Create() - { - var result = new VerifySettings(); - result.UseDirectory("VerifiedFiles"); - result.DisableDiff(); - return result; - } - } -} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Detectors/Cpu/CpuInfoFormatterTests.cs b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/CpuInfoFormatterTests.cs new file mode 100644 index 0000000000..342c38a1e8 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/CpuInfoFormatterTests.cs @@ -0,0 +1,39 @@ +using System.Text; +using System.Threading.Tasks; +using BenchmarkDotNet.Tests.Builders; +using BenchmarkDotNet.Tests.Infra; +using Perfolizer.Helpers; +using Perfolizer.Phd.Dto; +using VerifyXunit; +using Xunit; + +namespace BenchmarkDotNet.Tests.Detectors.Cpu; + +[Collection("VerifyTests")] +[UsesVerify] +public class CpuInfoFormatterTests +{ + [Fact] + public Task FormatTest() + { + var captions = new StringBuilder(); + foreach (var processorName in new[] { null, "", "Intel" }) + foreach (var physicalProcessorCount in new int?[] { null, 0, 1, 2 }) + foreach (var physicalCoreCount in new int?[] { null, 0, 1, 2 }) + foreach (var logicalCoreCount in new int?[] { null, 0, 1, 2 }) + { + var cpu = new PhdCpu + { + ProcessorName = processorName, + PhysicalProcessorCount = physicalProcessorCount, + PhysicalCoreCount = physicalCoreCount, + LogicalCoreCount = logicalCoreCount, + }; + + captions.AppendLine(cpu.ToFullBrandName()); + } + + var settings = VerifyHelper.Create(); + return Verifier.Verify(captions.ToString(), settings); + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/LinuxCpuInfoParserTests.cs similarity index 78% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/LinuxCpuInfoParserTests.cs index ab9a4ee76c..596f54aba1 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/LinuxCpuInfoParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/LinuxCpuInfoParserTests.cs @@ -1,10 +1,10 @@ -using BenchmarkDotNet.Portability.Cpu; -using BenchmarkDotNet.Portability.Cpu.Linux; +using BenchmarkDotNet.Detectors.Cpu.Linux; +using Perfolizer.Phd.Dto; using Xunit; using Xunit.Abstractions; using static Perfolizer.Horology.Frequency; -namespace BenchmarkDotNet.Tests.Portability.Cpu; +namespace BenchmarkDotNet.Tests.Detectors.Cpu; // ReSharper disable StringLiteralTypo public class LinuxCpuInfoParserTests(ITestOutputHelper output) @@ -15,7 +15,7 @@ public class LinuxCpuInfoParserTests(ITestOutputHelper output) public void EmptyTest() { var actual = LinuxCpuInfoParser.Parse("", ""); - var expected = CpuInfo.Empty; + var expected = new PhdCpu(); Output.AssertEqual(expected, actual); } @@ -23,7 +23,7 @@ public void EmptyTest() public void MalformedTest() { var actual = LinuxCpuInfoParser.Parse("malformedkey: malformedvalue\n\nmalformedkey2: malformedvalue2", null); - var expected = CpuInfo.Empty; + var expected = new PhdCpu(); Output.AssertEqual(expected, actual); } @@ -32,9 +32,15 @@ public void TwoProcessorWithDifferentCoresCountTest() { string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoProcessorWithDifferentCoresCount.txt"); var actual = LinuxCpuInfoParser.Parse(cpuInfo, null); - var expected = new CpuInfo( - "Unknown processor with 2 cores and hyper threading, Unknown processor with 4 cores", - 2, 6, 8, 2.5 * GHz, 2.5 * GHz); + var expected = new PhdCpu + { + ProcessorName = "Unknown processor with 2 cores and hyper threading, Unknown processor with 4 cores", + PhysicalProcessorCount = 2, + PhysicalCoreCount = 6, + LogicalCoreCount = 8, + NominalFrequencyHz = 2_500_000_000, + MaxFrequencyHz = 2_500_000_000 + }; Output.AssertEqual(expected, actual); } @@ -44,9 +50,15 @@ public void RealOneProcessorTwoCoresTest() { string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoRealOneProcessorTwoCores.txt"); var actual = LinuxCpuInfoParser.Parse(cpuInfo, null); - var expected = new CpuInfo( - "Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz", - 1, 2, 4, 2.3 * GHz, 2.3 * GHz); + var expected = new PhdCpu + { + ProcessorName = "Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz", + PhysicalProcessorCount = 1, + PhysicalCoreCount = 2, + LogicalCoreCount = 4, + NominalFrequencyHz = 2_300_000_000, + MaxFrequencyHz = 2_300_000_000 + }; Output.AssertEqual(expected, actual); } @@ -55,9 +67,15 @@ public void RealOneProcessorFourCoresTest() { string cpuInfo = TestHelper.ReadTestFile("ProcCpuInfoRealOneProcessorFourCores.txt"); var actual = LinuxCpuInfoParser.Parse(cpuInfo, null); - var expected = new CpuInfo( - "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", - 1, 4, 8, 2.5 * GHz, 2.5 * GHz); + var expected = new PhdCpu + { + ProcessorName = "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", + PhysicalProcessorCount = 1, + PhysicalCoreCount = 4, + LogicalCoreCount = 8, + NominalFrequencyHz = 2_500_000_000, + MaxFrequencyHz = 2_500_000_000 + }; Output.AssertEqual(expected, actual); } @@ -94,7 +112,7 @@ public void Issue2577Test() Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp """; var actual = LinuxCpuInfoParser.Parse(cpuInfo, lscpu); - var expected = new CpuInfo("Neoverse-N1", null, 16, null, null, null); + var expected = new PhdCpu { ProcessorName = "Neoverse-N1", PhysicalCoreCount = 16 }; Output.AssertEqual(expected, actual); } @@ -130,7 +148,15 @@ flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2av r smca fsrm flush_l1d """; var actual = LinuxCpuInfoParser.Parse(cpuInfo, lscpu); - var expected = new CpuInfo("AMD Ryzen 9 7950X 16-Core Processor", 1, 16, 32, 5881 * MHz, 5881 * MHz); + var expected = new PhdCpu + { + ProcessorName = "AMD Ryzen 9 7950X 16-Core Processor", + PhysicalProcessorCount = 1, + PhysicalCoreCount = 16, + LogicalCoreCount = 32, + NominalFrequencyHz = 5_881_000_000, + MaxFrequencyHz = 5_881_000_000 + }; Output.AssertEqual(expected, actual); } diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/SysctlCpuInfoParserTests.cs b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/SysctlCpuInfoParserTests.cs similarity index 61% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/SysctlCpuInfoParserTests.cs rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/SysctlCpuInfoParserTests.cs index 2aca9acb89..208a7c8373 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/SysctlCpuInfoParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/SysctlCpuInfoParserTests.cs @@ -1,10 +1,9 @@ -using BenchmarkDotNet.Portability.Cpu; -using BenchmarkDotNet.Portability.Cpu.macOS; +using BenchmarkDotNet.Detectors.Cpu.macOS; +using Perfolizer.Phd.Dto; using Xunit; using Xunit.Abstractions; -using static Perfolizer.Horology.Frequency; -namespace BenchmarkDotNet.Tests.Portability.Cpu; +namespace BenchmarkDotNet.Tests.Detectors.Cpu; // ReSharper disable StringLiteralTypo public class SysctlCpuInfoParserTests(ITestOutputHelper output) @@ -15,7 +14,7 @@ public class SysctlCpuInfoParserTests(ITestOutputHelper output) public void EmptyTest() { var actual = SysctlCpuInfoParser.Parse(string.Empty); - var expected = CpuInfo.Empty; + var expected = new PhdCpu(); Output.AssertEqual(expected, actual); } @@ -23,7 +22,7 @@ public void EmptyTest() public void MalformedTest() { var actual = SysctlCpuInfoParser.Parse("malformedkey=malformedvalue\n\nmalformedkey2=malformedvalue2"); - var expected = CpuInfo.Empty; + var expected = new PhdCpu(); Output.AssertEqual(expected, actual); } @@ -32,9 +31,15 @@ public void RealOneProcessorFourCoresTest() { string cpuInfo = TestHelper.ReadTestFile("SysctlRealOneProcessorFourCores.txt"); var actual = SysctlCpuInfoParser.Parse(cpuInfo); - var expected = new CpuInfo( - "Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz", - 1, 4, 8, 2200 * MHz, 2200 * MHz); + var expected = new PhdCpu + { + ProcessorName = "Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz", + PhysicalProcessorCount = 1, + PhysicalCoreCount = 4, + LogicalCoreCount = 8, + NominalFrequencyHz = 2_200_000_000, + MaxFrequencyHz = 2_200_000_000 + }; Output.AssertEqual(expected, actual); } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ProcCpuInfoProcessorWithDifferentCoresCount.txt b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/ProcCpuInfoProcessorWithDifferentCoresCount.txt similarity index 100% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ProcCpuInfoProcessorWithDifferentCoresCount.txt rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/ProcCpuInfoProcessorWithDifferentCoresCount.txt diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ProcCpuInfoRealOneProcessorFourCores.txt b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/ProcCpuInfoRealOneProcessorFourCores.txt similarity index 100% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ProcCpuInfoRealOneProcessorFourCores.txt rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/ProcCpuInfoRealOneProcessorFourCores.txt diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ProcCpuInfoRealOneProcessorTwoCores.txt b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/ProcCpuInfoRealOneProcessorTwoCores.txt similarity index 100% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ProcCpuInfoRealOneProcessorTwoCores.txt rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/ProcCpuInfoRealOneProcessorTwoCores.txt diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/SysctlRealOneProcessorFourCores.txt b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/SysctlRealOneProcessorFourCores.txt similarity index 100% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/SysctlRealOneProcessorFourCores.txt rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/SysctlRealOneProcessorFourCores.txt diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ryzen9-cpuinfo.txt b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/ryzen9-cpuinfo.txt similarity index 100% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/TestFiles/ryzen9-cpuinfo.txt rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestFiles/ryzen9-cpuinfo.txt diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestHelper.cs b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestHelper.cs similarity index 71% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/TestHelper.cs rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestHelper.cs index d95a8b5dfb..806035da29 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/TestHelper.cs +++ b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/TestHelper.cs @@ -1,11 +1,12 @@ using System.IO; using System.Reflection; -using BenchmarkDotNet.Portability.Cpu; using JetBrains.Annotations; +using Perfolizer.Helpers; +using Perfolizer.Phd.Dto; using Xunit; using Xunit.Abstractions; -namespace BenchmarkDotNet.Tests.Portability.Cpu; +namespace BenchmarkDotNet.Tests.Detectors.Cpu; public static class TestHelper { @@ -23,15 +24,15 @@ public static string ReadTestFile(string name) } [AssertionMethod] - public static void AssertEqual(this ITestOutputHelper output, CpuInfo expected, CpuInfo actual) + public static void AssertEqual(this ITestOutputHelper output, PhdCpu expected, PhdCpu actual) { - output.WriteLine($"Expected : {CpuInfoFormatter.Format(expected)}"); - output.WriteLine($"Actual : {CpuInfoFormatter.Format(actual)}"); + output.WriteLine($"Expected : {expected.ToFullBrandName()}"); + output.WriteLine($"Actual : {actual.ToFullBrandName()}"); Assert.Equal(expected.ProcessorName, actual.ProcessorName); Assert.Equal(expected.PhysicalProcessorCount, actual.PhysicalProcessorCount); Assert.Equal(expected.PhysicalCoreCount, actual.PhysicalCoreCount); Assert.Equal(expected.LogicalCoreCount, actual.LogicalCoreCount); - Assert.Equal(expected.NominalFrequency, actual.NominalFrequency); - Assert.Equal(expected.MaxFrequency, actual.MaxFrequency); + Assert.Equal(expected.NominalFrequency(), actual.NominalFrequency()); + Assert.Equal(expected.MaxFrequency(), actual.MaxFrequency()); } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/VerifiedFiles/CpuInfoFormatterTests.FormatTest.verified.txt b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/VerifiedFiles/CpuInfoFormatterTests.FormatTest.verified.txt similarity index 100% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/VerifiedFiles/CpuInfoFormatterTests.FormatTest.verified.txt rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/VerifiedFiles/CpuInfoFormatterTests.FormatTest.verified.txt diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/WmicCpuInfoParserTests.cs b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/WmicCpuInfoParserTests.cs similarity index 64% rename from tests/BenchmarkDotNet.Tests/Portability/Cpu/WmicCpuInfoParserTests.cs rename to tests/BenchmarkDotNet.Tests/Detectors/Cpu/WmicCpuInfoParserTests.cs index 98f170ed03..3ee84a8e44 100644 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/WmicCpuInfoParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/Detectors/Cpu/WmicCpuInfoParserTests.cs @@ -1,10 +1,10 @@ -using BenchmarkDotNet.Portability.Cpu; -using BenchmarkDotNet.Portability.Cpu.Windows; +using BenchmarkDotNet.Detectors.Cpu.Windows; +using Perfolizer.Phd.Dto; using Xunit; using Xunit.Abstractions; using static Perfolizer.Horology.Frequency; -namespace BenchmarkDotNet.Tests.Portability.Cpu; +namespace BenchmarkDotNet.Tests.Detectors.Cpu; // ReSharper disable StringLiteralTypo public class WmicCpuInfoParserTests(ITestOutputHelper output) @@ -15,7 +15,7 @@ public class WmicCpuInfoParserTests(ITestOutputHelper output) public void EmptyTest() { var actual = WmicCpuInfoParser.Parse(string.Empty); - var expected = CpuInfo.Empty; + var expected = new PhdCpu(); Output.AssertEqual(expected, actual); } @@ -23,7 +23,7 @@ public void EmptyTest() public void MalformedTest() { var actual = WmicCpuInfoParser.Parse("malformedkey=malformedvalue\n\nmalformedkey2=malformedvalue2"); - var expected = CpuInfo.Empty; + var expected = new PhdCpu(); Output.AssertEqual(expected, actual); } @@ -45,9 +45,15 @@ public void RealTwoProcessorEightCoresTest() "; var actual = WmicCpuInfoParser.Parse(cpuInfo); - var expected = new CpuInfo( - "Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz", - 2, 16, 32, 2400 * MHz, 2400 * MHz); + var expected = new PhdCpu + { + ProcessorName = "Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz", + PhysicalProcessorCount = 2, + PhysicalCoreCount = 16, + LogicalCoreCount = 32, + NominalFrequencyHz = 2_400_000_000, + MaxFrequencyHz = 2_400_000_000, + }; Output.AssertEqual(expected, actual); } @@ -71,9 +77,15 @@ public void RealTwoProcessorEightCoresWithWmicBugTest() "\r\r\n" + "\r\r\n"; var actual = WmicCpuInfoParser.Parse(cpuInfo); - var expected = new CpuInfo( - "Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz", - 2, 16, 32, 3111 * MHz, 3111 * MHz); + var expected = new PhdCpu + { + ProcessorName = "Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz", + PhysicalProcessorCount = 2, + PhysicalCoreCount = 16, + LogicalCoreCount = 32, + NominalFrequencyHz = 3_111_000_000, + MaxFrequencyHz = 3_111_000_000, + }; Output.AssertEqual(expected, actual); } @@ -90,9 +102,15 @@ public void RealOneProcessorFourCoresTest() "; var actual = WmicCpuInfoParser.Parse(cpuInfo); - var expected = new CpuInfo( - "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", - 1, 4, 8, 2500 * MHz, 2500 * MHz); + var expected = new PhdCpu + { + ProcessorName = "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz", + PhysicalProcessorCount = 1, + PhysicalCoreCount = 4, + LogicalCoreCount = 8, + NominalFrequencyHz = 2_500_000_000, + MaxFrequencyHz = 2_500_000_000, + }; Output.AssertEqual(expected, actual); } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Environments/HostEnvironmentInfoTests.cs b/tests/BenchmarkDotNet.Tests/Environments/HostEnvironmentInfoTests.cs index 977be28869..a49d9ea2e7 100644 --- a/tests/BenchmarkDotNet.Tests/Environments/HostEnvironmentInfoTests.cs +++ b/tests/BenchmarkDotNet.Tests/Environments/HostEnvironmentInfoTests.cs @@ -4,6 +4,7 @@ using BenchmarkDotNet.Portability; using BenchmarkDotNet.Tests.Builders; using JetBrains.Annotations; +using Perfolizer.Helpers; using Xunit; namespace BenchmarkDotNet.Tests.Environments @@ -22,7 +23,7 @@ public void ReturnsHypervisorNameWhenItsDetected(string hypervisorName) string line = info.ToFormattedString().First(); string expected = $"{HostEnvironmentInfo.BenchmarkDotNetCaption} v{info.BenchmarkDotNetVersion}, " + - $"{info.OsVersion.Value} ({hypervisor.Name})"; + $"{info.Os.Value.ToBrandString()} ({hypervisor.Name})"; Assert.Equal(expected, line); } @@ -46,7 +47,7 @@ public void DoesntReturnHypervisorNameWhenItsNotDetected() string line = info.ToFormattedString().First(); string expected = $"{HostEnvironmentInfo.BenchmarkDotNetCaption} v{info.BenchmarkDotNetVersion}, " + - $"{info.OsVersion.Value}"; + $"{info.Os.Value.ToBrandString()}"; Assert.Equal(expected, line); } } diff --git a/tests/BenchmarkDotNet.Tests/Environments/OsBrandStringTests.cs b/tests/BenchmarkDotNet.Tests/Environments/OsBrandStringTests.cs deleted file mode 100644 index 5690fbe6b7..0000000000 --- a/tests/BenchmarkDotNet.Tests/Environments/OsBrandStringTests.cs +++ /dev/null @@ -1,70 +0,0 @@ -using BenchmarkDotNet.Environments; -using Xunit; -using Xunit.Abstractions; - -namespace BenchmarkDotNet.Tests.Environments -{ - public class OsBrandStringTests - { - private readonly ITestOutputHelper output; - - public OsBrandStringTests(ITestOutputHelper output) => this.output = output; - - private void Check(string actual, string expected) - { - output.WriteLine("LENGTH : " + actual.Length); - output.WriteLine("ACTUAL : " + actual); - output.WriteLine("EXPECTED : " + expected); - Assert.Equal(expected, actual); - - // The line with OsBrandString is one of the longest lines in the summary. - // When people past in on GitHub, it can be a reason of an ugly horizontal scrollbar. - // To avoid this, we are trying to minimize this line and use the minimum possible number of characters. - // In this test, we check that the length of the OS brand string for typical Windows versions - // is less than 61 characters. - const int maxLength = 61; - Assert.True(actual.Length <= maxLength, $"The brand string name length should be <= {maxLength}"); - } - - [Theory] - [InlineData("6.3.9600", "Windows 8.1 (6.3.9600)")] - [InlineData("10.0.14393", "Windows 10 (10.0.14393/1607/AnniversaryUpdate/Redstone1)")] - public void WindowsIsPrettified(string originalVersion, string prettifiedName) - => Check(OsBrandStringHelper.Prettify("Windows", originalVersion), prettifiedName); - - [Theory] - [InlineData("10.0.10240", 17797, "Windows 10 (10.0.10240.17797/1507/RTM/Threshold1)")] - [InlineData("10.0.10586", 1478, "Windows 10 (10.0.10586.1478/1511/NovemberUpdate/Threshold2)")] - [InlineData("10.0.14393", 2156, "Windows 10 (10.0.14393.2156/1607/AnniversaryUpdate/Redstone1)")] - [InlineData("10.0.15063", 997, "Windows 10 (10.0.15063.997/1703/CreatorsUpdate/Redstone2)")] - [InlineData("10.0.16299", 334, "Windows 10 (10.0.16299.334/1709/FallCreatorsUpdate/Redstone3)")] - [InlineData("10.0.17134", 48, "Windows 10 (10.0.17134.48/1803/April2018Update/Redstone4)")] - [InlineData("10.0.17763", 1, "Windows 10 (10.0.17763.1/1809/October2018Update/Redstone5)")] - [InlineData("10.0.18362", 693, "Windows 10 (10.0.18362.693/1903/May2019Update/19H1)")] - [InlineData("10.0.18363", 657, "Windows 10 (10.0.18363.657/1909/November2019Update/19H2)")] - [InlineData("10.0.19041", 1, "Windows 10 (10.0.19041.1/2004/May2020Update/20H1)")] - [InlineData("10.0.19042", 746, "Windows 10 (10.0.19042.746/20H2/October2020Update)")] - [InlineData("10.0.19043", 964, "Windows 10 (10.0.19043.964/21H1/May2021Update)")] - [InlineData("10.0.19044", 1147, "Windows 10 (10.0.19044.1147/21H2/November2021Update)")] - [InlineData("10.0.19099", 1729, "Windows 10 (10.0.19099.1729)")] - [InlineData("10.0.19045", 0, "Windows 10 (10.0.19045.0/22H2/2022Update)")] - [InlineData("10.0.22000", 348, "Windows 11 (10.0.22000.348/21H2/SunValley)")] - [InlineData("10.0.22518", 1012, "Windows 11 (10.0.22518.1012)")] - [InlineData("10.0.22621", 0, "Windows 11 (10.0.22621.0/22H2/2022Update/SunValley2)")] - [InlineData("10.0.22631", 2428, "Windows 11 (10.0.22631.2428/23H2/2023Update/SunValley3)")] - public void WindowsWithUbrIsPrettified(string originalVersion, int ubr, string prettifiedName) - => Check(OsBrandStringHelper.Prettify("Windows", originalVersion, ubr), prettifiedName); - - [Theory] - [InlineData("macOS 10.12.6 (16G29)", "Darwin 16.7.0", "macOS Sierra 10.12.6 (16G29) [Darwin 16.7.0]")] - [InlineData("macOS 10.13.4 (17E199)", "Darwin 17.5.0", "macOS High Sierra 10.13.4 (17E199) [Darwin 17.5.0]")] - [InlineData("macOS 10.15.4 (19E266)", "Darwin 19.4.0", "macOS Catalina 10.15.4 (19E266) [Darwin 19.4.0]")] - [InlineData("macOS 11.1 (20C69)", "Darwin 20.2.0", "macOS Big Sur 11.1 (20C69) [Darwin 20.2.0]")] - [InlineData("macOS 11.3.1 (20E241)", "Darwin 20.4.0", "macOS Big Sur 11.3.1 (20E241) [Darwin 20.4.0]")] - [InlineData("macOS 12.1 (21C52)", "Darwin 21.2.0", "macOS Monterey 12.1 (21C52) [Darwin 21.2.0]")] - [InlineData("macOS 13.0.1 (22A400)", "Darwin 22.1.0", "macOS Ventura 13.0.1 (22A400) [Darwin 22.1.0]")] - [InlineData("macOS 14.0.0", "Darwin 23.0.0", "macOS Sonoma 14.0.0 [Darwin 23.0.0]")] - public void MacOSXIsPrettified(string systemVersion, string kernelVersion, string prettifiedName) - => Check(OsBrandStringHelper.PrettifyMacOSX(systemVersion, kernelVersion), prettifiedName); - } -} diff --git a/tests/BenchmarkDotNet.Tests/Environments/ProcessorBrandStringTests.cs b/tests/BenchmarkDotNet.Tests/Environments/ProcessorBrandStringTests.cs deleted file mode 100644 index 127c96ca1d..0000000000 --- a/tests/BenchmarkDotNet.Tests/Environments/ProcessorBrandStringTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -using BenchmarkDotNet.Environments; -using BenchmarkDotNet.Portability.Cpu; -using Perfolizer.Horology; -using Xunit; - -namespace BenchmarkDotNet.Tests.Environments -{ - public class ProcessorBrandStringTests - { - [Theory] - [InlineData("Intel(R) Pentium(TM) G4560 CPU @ 3.50GHz", "Intel Pentium G4560 CPU 3.50GHz")] - [InlineData("Intel(R) Core(TM) i7 CPU 970 @ 3.20GHz", "Intel Core i7 CPU 970 3.20GHz (Nehalem)")] // Nehalem/Westmere/Gulftown - [InlineData("Intel(R) Core(TM) i7-920 CPU @ 2.67GHz", "Intel Core i7-920 CPU 2.67GHz (Nehalem)")] - [InlineData("Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz", "Intel Core i7-2600 CPU 3.40GHz (Sandy Bridge)")] - [InlineData("Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz", "Intel Core i7-3770 CPU 3.40GHz (Ivy Bridge)")] - [InlineData("Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz", "Intel Core i7-4770K CPU 3.50GHz (Haswell)")] - [InlineData("Intel(R) Core(TM) i7-5775R CPU @ 3.30GHz", "Intel Core i7-5775R CPU 3.30GHz (Broadwell)")] - [InlineData("Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz", "Intel Core i7-6700HQ CPU 2.60GHz (Skylake)")] - [InlineData("Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz", "Intel Core i7-7700 CPU 3.60GHz (Kaby Lake)")] - [InlineData("Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz ", "Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R)")] - [InlineData("Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz", "Intel Core i7-8700K CPU 3.70GHz (Coffee Lake)")] - public void IntelCoreIsPrettified(string originalName, string prettifiedName) - { - var actual = CpuInfo.FromName(originalName); - Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(actual)); - } - - [Theory] - [InlineData("Intel(R) Pentium(TM) G4560 CPU @ 3.50GHz", "Intel Pentium G4560 CPU 3.50GHz (Max: 3.70GHz)", 3.7)] - [InlineData("Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz", "Intel Core i5-2500 CPU 3.30GHz (Max: 3.70GHz) (Sandy Bridge)", 3.7)] - [InlineData("Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz", "Intel Core i7-2600 CPU 3.40GHz (Max: 3.70GHz) (Sandy Bridge)", 3.7)] - [InlineData("Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz", "Intel Core i7-3770 CPU 3.40GHz (Max: 3.50GHz) (Ivy Bridge)", 3.5)] - [InlineData("Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz", "Intel Core i7-4770K CPU 3.50GHz (Max: 3.60GHz) (Haswell)", 3.6)] - [InlineData("Intel(R) Core(TM) i7-5775R CPU @ 3.30GHz", "Intel Core i7-5775R CPU 3.30GHz (Max: 3.40GHz) (Broadwell)", 3.4)] - public void CoreIsPrettifiedWithDiffFrequencies(string originalName, string prettifiedName, double actualFrequency) - { - var actual = CpuInfo.FromNameAndFrequency(originalName, Frequency.FromGHz(actualFrequency)); - Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(actual, includeMaxFrequency: true)); - } - - [Theory] - [InlineData("AMD Ryzen 7 2700X Eight-Core Processor", "AMD Ryzen 7 2700X 4.10GHz", 4.1, 8, 16)] - [InlineData("AMD Ryzen 7 2700X Eight-Core Processor", "AMD Ryzen 7 2700X Eight-Core Processor 4.10GHz", 4.1, null, null)] - public void AmdIsPrettifiedWithDiffFrequencies(string originalName, string prettifiedName, double actualFrequency, int? physicalCoreCount, int? logicalCoreCount) - { - var cpuInfo = new CpuInfo( - originalName, - physicalProcessorCount: null, - physicalCoreCount, - logicalCoreCount, - Frequency.FromGHz(actualFrequency), - maxFrequency: null); - - Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(cpuInfo, includeMaxFrequency: true)); - } - - [Theory] - [InlineData("8130U", "Kaby Lake")] - [InlineData("9900K", "Coffee Lake")] - [InlineData("8809G", "Kaby Lake G")] - public void IntelCoreMicroarchitecture(string modelNumber, string microarchitecture) - { - Assert.Equal(microarchitecture, ProcessorBrandStringHelper.ParseIntelCoreMicroarchitecture(modelNumber)); - } - - [Theory] - [InlineData("", "Unknown processor")] - [InlineData(null, "Unknown processor")] - public void UnknownProcessorDoesNotThrow(string? originalName, string prettifiedName) - { - var cpuInfo = new CpuInfo(originalName, null, null, null, null); - - Assert.Equal(prettifiedName, ProcessorBrandStringHelper.Prettify(cpuInfo, includeMaxFrequency: true)); - } - } -} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Exporters/CommonExporterVerifyTests.cs b/tests/BenchmarkDotNet.Tests/Exporters/CommonExporterVerifyTests.cs index 074bbf50ab..5ab48b7a30 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/CommonExporterVerifyTests.cs +++ b/tests/BenchmarkDotNet.Tests/Exporters/CommonExporterVerifyTests.cs @@ -12,6 +12,7 @@ using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Tests.Builders; +using BenchmarkDotNet.Tests.Infra; using BenchmarkDotNet.Tests.Mocks; using BenchmarkDotNet.Tests.Reports; using JetBrains.Annotations; @@ -62,7 +63,7 @@ [new Metric(new FakeMetricDescriptor("CacheMisses", "Hardware counter 'CacheMiss logger); } - var settings = VerifySettingsFactory.Create(); + var settings = VerifyHelper.Create(); settings.UseTextForParameters(GetName(cultureInfo)); return Verifier.Verify(logger.GetLog(), settings); } diff --git a/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs b/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs index fca08d0c0e..8da0a0e24e 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs +++ b/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs @@ -10,12 +10,11 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Tests.Builders; +using BenchmarkDotNet.Tests.Infra; using BenchmarkDotNet.Validators; using JetBrains.Annotations; using VerifyXunit; using Xunit; -using BenchmarkDotNet.Columns; -using System.Reflection; namespace BenchmarkDotNet.Tests.Exporters { @@ -42,8 +41,9 @@ public Task GroupExporterTest(Type benchmarkType) var logger = new AccumulationLogger(); logger.WriteLine("=== " + benchmarkType.Name + " ==="); + var exporter = MarkdownExporter.Mock; - var summary = MockFactory.CreateSummary(benchmarkType, benchmarkType.GetCustomAttribute()?.Config.GetColumnHidingRules().ToArray() ?? []); + var summary = MockFactory.CreateSummary(benchmarkType); exporter.ExportToLog(summary, logger); var validator = BaselineValidator.FailOnError; @@ -53,7 +53,7 @@ public Task GroupExporterTest(Type benchmarkType) foreach (var error in errors) logger.WriteLineError("* " + error.Message); - var settings = VerifySettingsFactory.Create(); + var settings = VerifyHelper.Create(); settings.UseTextForParameters(benchmarkType.Name); return Verifier.Verify(logger.GetLog(), settings); } @@ -220,8 +220,8 @@ [Benchmark] public void Bar() { } [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2")] public class MethodJobBaseline_MethodsJobs { - [Benchmark(Baseline = true)] public void Foo() { } - [Benchmark] public void Bar() { } + [Benchmark(Baseline = true)] public void Foo() {} + [Benchmark] public void Bar() {} } [RankColumn, LogicalGroupColumn, BaselineColumn] @@ -230,8 +230,8 @@ public class MethodJobBaseline_MethodsJobsParams { [Params(2, 10), UsedImplicitly] public int Param; - [Benchmark(Baseline = true)] public void Foo() { } - [Benchmark] public void Bar() { } + [Benchmark(Baseline = true)] public void Foo() {} + [Benchmark] public void Bar() {} } /* Invalid */ @@ -239,16 +239,16 @@ [Benchmark] public void Bar() { } [RankColumn, LogicalGroupColumn, BaselineColumn] public class Invalid_TwoMethodBaselines { - [Benchmark(Baseline = true)] public void Foo() { } - [Benchmark(Baseline = true)] public void Bar() { } + [Benchmark(Baseline = true)] public void Foo() {} + [Benchmark(Baseline = true)] public void Bar() {} } [RankColumn, LogicalGroupColumn, BaselineColumn] [SimpleJob(id: "Job1", baseline: true), SimpleJob(id: "Job2", baseline: true)] public class Invalid_TwoJobBaselines { - [Benchmark] public void Foo() { } - [Benchmark] public void Bar() { } + [Benchmark] public void Foo() {} + [Benchmark] public void Bar() {} } /* Escape Params */ @@ -261,13 +261,6 @@ public class Escape_ParamsAndArguments [Benchmark] public void Foo(char charArg) {} [Benchmark] public void Bar() {} } - - /* Hide Column */ - [HideColumns(Column.StdDev)] - public class HideColumns_TableMarkDown - { - [Benchmark] public void Foo() { } - } } } } diff --git a/tests/BenchmarkDotNet.Tests/Infra/TestOutputPresenter.cs b/tests/BenchmarkDotNet.Tests/Infra/TestOutputPresenter.cs new file mode 100644 index 0000000000..7ed360c266 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Infra/TestOutputPresenter.cs @@ -0,0 +1,9 @@ +using Perfolizer.Presenting; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.Tests.Infra; + +public class TestOutputPresenter(ITestOutputHelper output) : BufferedPresenter +{ + protected override void Flush(string text) => output.WriteLine(text.TrimEnd('\n', '\r')); +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Infra/VerifyHelper.cs b/tests/BenchmarkDotNet.Tests/Infra/VerifyHelper.cs new file mode 100644 index 0000000000..9d563d8ca6 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Infra/VerifyHelper.cs @@ -0,0 +1,16 @@ +using VerifyTests; + +namespace BenchmarkDotNet.Tests.Infra; + +public static class VerifyHelper +{ + public static VerifySettings Create(string? typeName = null) + { + var result = new VerifySettings(); + result.UseDirectory("VerifiedFiles"); + result.DisableDiff(); + if (typeName != null) + result.UseTypeName(typeName); + return result; + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/LightJsonSerializerTests.cs b/tests/BenchmarkDotNet.Tests/LightJsonSerializerTests.cs new file mode 100644 index 0000000000..16ef22efed --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/LightJsonSerializerTests.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using SimpleJson; +using Xunit; + +namespace BenchmarkDotNet.Tests +{ + public class LightJsonSerializerTests + { + [Theory] + [InlineData(10.0, "test", double.NaN)] + public void SimpleJson_ReplaceUnsupportedNumericValues_Smoke(object val1, object val2, object val3) + { + //Arrange + var data = new List() + { + val1, val2, val3 + }; + + //Act + for (int i = 0; i < data.Count; i++) + { + data[i] = SimpleJsonSerializer.ReplaceUnsupportedNumericValues(data[i]); + } + + //Assert + Assert.Equal(val1, data[0]); + Assert.Equal(val2, data[1]); + Assert.Equal(SimpleJsonSerializer.JSON_EMPTY_STRING, data[2]); + } + + + [Fact] + public void SimpleJson_SerializeObjectWithUnsupportedNumericValues_ReturnsValidJson() + { + //Arrange + var data = new Dictionary + { + { "Statistic1", float.NaN}, + { "Statistic2", float.NegativeInfinity }, + { "Statistic3", float.PositiveInfinity }, + { "Statistic4", double.NaN}, + { "Statistic5", double.NegativeInfinity }, + { "Statistic6", double.PositiveInfinity } + }; + + //Act + string json = SimpleJsonSerializer.SerializeObject(data); + + //Assert + Assert.True(SimpleJsonSerializer.TryDeserializeObject(json, out var obj)); + var values = (obj as SimpleJson.JsonObject).Select(x => x.Value); + Assert.True(values.All(x => x.Equals(string.Empty))); + } + } +} diff --git a/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs b/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs index edbaba0e3a..bbe715d660 100644 --- a/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs +++ b/tests/BenchmarkDotNet.Tests/Mocks/MockFactory.cs @@ -18,6 +18,7 @@ using Perfolizer.Horology; using Perfolizer.Metrology; using static BenchmarkDotNet.Reports.SummaryTable.SummaryTableColumn; +using Measurement = BenchmarkDotNet.Reports.Measurement; using MethodInfo = System.Reflection.MethodInfo; namespace BenchmarkDotNet.Tests.Mocks @@ -37,8 +38,8 @@ public static Summary CreateSummary(Type benchmarkType, params IColumnHidingRule TestCultureInfo.Instance, ImmutableArray.Empty, ImmutableArray.Create(columHidingRules)); - } - + } + public static Summary CreateSummaryWithBiasedDistribution(Type benchmarkType, int min, int median, int max, int n) { var runInfo = BenchmarkConverter.TypeToBenchmarks(benchmarkType); @@ -145,7 +146,7 @@ private static BenchmarkReport CreateReportWithBiasedDistribution(BenchmarkCase { var buildResult = BuildResult.Success(GenerateResult.Success(ArtifactsPaths.Empty, Array.Empty())); bool isFoo = benchmarkCase.Descriptor.WorkloadMethodDisplayInfo == "Foo"; - bool isBar = benchmarkCase.Descriptor.WorkloadMethodDisplayInfo == "Bar"; + bool isBar = benchmarkCase.Descriptor.WorkloadMethodDisplayInfo == "Bar"; var measurements = from i in Enumerable.Range(0, Math.Max(1, n / 9)) from m in isFoo ? new[] { diff --git a/tests/BenchmarkDotNet.Tests/Phd/Infra/PhdMock.cs b/tests/BenchmarkDotNet.Tests/Phd/Infra/PhdMock.cs new file mode 100644 index 0000000000..a7a6d8e812 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/Infra/PhdMock.cs @@ -0,0 +1,13 @@ +using BenchmarkDotNet.Properties; +using Perfolizer.Phd.Dto; + +namespace BenchmarkDotNet.Tests.Phd.Infra; + +public static class PhdMock +{ + public static readonly PhdEngine Engine = new () + { + Name = BenchmarkDotNetInfo.BenchmarkDotNetCaption, + Version = "0.1729.0-mock" + }; +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/Infra/PhdTestExtensions.cs b/tests/BenchmarkDotNet.Tests/Phd/Infra/PhdTestExtensions.cs new file mode 100644 index 0000000000..a3db74c376 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/Infra/PhdTestExtensions.cs @@ -0,0 +1,22 @@ +using Perfolizer.Metrology; +using Perfolizer.Phd.Base; + +namespace BenchmarkDotNet.Tests.Phd.Infra; + +public static class PhdTestExtensions +{ + public static PhdEntry AddMetrics(this PhdEntry entry, params string[] metrics) + { + for (int i = 0; i < metrics.Length; i++) + { + var measurement = Measurement.Parse(metrics[i]); + entry.Add(new PhdEntry + { + IterationIndex = i, + Value = measurement.NominalValue, + Unit = measurement.Unit + }); + } + return entry; + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/PhdTests.cs b/tests/BenchmarkDotNet.Tests/Phd/PhdTests.cs new file mode 100644 index 0000000000..1772f54789 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/PhdTests.cs @@ -0,0 +1,173 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Phd; +using BenchmarkDotNet.Tests.Builders; +using BenchmarkDotNet.Tests.Infra; +using BenchmarkDotNet.Tests.Phd.Infra; +using JetBrains.Annotations; +using Perfolizer.Json; +using Perfolizer.Phd.Base; +using Perfolizer.Phd.Dto; +using Perfolizer.Phd.Presenting; +using Perfolizer.Phd.Tables; +using Perfolizer.Presenting; +using VerifyXunit; +using Xunit; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.Tests.Phd; + +[UsesVerify] +public class PhdTests(ITestOutputHelper output) +{ + [Theory] + [MemberData(nameof(EntryDataKeys))] + public Task PhdIndexTest(string key) => VerifyString(key, new PhdIndex(EntryDataMap[key]).Dump()); + + [Theory] + [MemberData(nameof(EntryDataKeys))] + public Task PhdTableTest(string key) + { + var entry = EntryDataMap[key]; + var table = new PhdTable(entry); + var presenter = new StringPresenter(); + new PhdMarkdownTablePresenter(presenter).Present(table, new PhdTableStyle()); + string json = LightJsonSerializer.Serialize(entry, new LightJsonSettings { Indent = true }); + return VerifyString(key, presenter.Dump() + "\n" + json); + } + + private static readonly IDictionary EntryDataMap = new Dictionary + { + { + "default01", Root().Add( + Benchmark("Foo", "10ns", "11ns", "12ns"), + Benchmark("Bar", "200ns", "201ns", "202ns") + ) + }, + { + "default02", Root().Add( + Job(RuntimeMoniker.Net481).Add( + Benchmark("Foo", "10ns", "11ns", "12ns"), + Benchmark("Bar", "20ns", "21ns", "22ns")), + Job(RuntimeMoniker.Net70).Add( + Benchmark("Foo", "30ns", "31ns", "32ns"), + Benchmark("Bar", "40ns", "41ns", "42ns"))) + }, + { + "default03", Root().Add( + Job(RuntimeMoniker.Net70, Jit.RyuJit).Add( + Benchmark("Foo", "30ns", "31ns", "32ns"), + Benchmark("Bar", "40ns", "41ns", "42ns"))) + }, + { + "default04", Root().Add( + Job(RuntimeMoniker.Net481, Jit.LegacyJit).Add( + Benchmark("Foo", "10ns", "11ns", "12ns"), + Benchmark("Bar", "20ns", "21ns", "22ns")), + Job(RuntimeMoniker.Net70, Jit.RyuJit).Add( + Benchmark("Foo", "30ns", "31ns", "32ns"), + Benchmark("Bar", "40ns", "41ns", "42ns"))) + }, + { + "default05", Root().Add( + Enumerable.Range(0, 20).Select(index => + Job((RuntimeMoniker)index, Jit.RyuJit, index).Add( + Benchmark("Foo", index * 10 + 1 + "ns", index * 10 + 2 + "ns", index * 10 + 3 + "ns"), + Benchmark("Bar", index * 10 + 6 + "ns", index * 10 + 7 + "ns", index * 10 + 8 + "ns") + )).ToArray()) + }, + { + "sort01", new PhdEntry + { + Meta = new PhdMeta + { + Table = new PhdTableConfig + { + ColumnDefinitions = + { + new PhdColumnDefinition(".benchmark.method"), + new PhdColumnDefinition("=center") + }, + SortPolicies = + [ + new PhdSortPolicy("=center", PhdSortDirection.Descending) + ] + } + } + }.Add(Benchmark("Foo", "10ms"), Benchmark("Bar", "20ms")) + }, + { + "params01", new PhdEntry + { + Meta = new PhdMeta + { + Table = new PhdTableConfig + { + ColumnDefinitions = + { + new PhdColumnDefinition(".benchmark.method"), + new PhdColumnDefinition(".parameters"), + new PhdColumnDefinition("=center") + } + } + } + }.Add( + new PhdEntry + { + Parameters = new Dictionary { { "A", 1 }, { "B", 2 } } + }.Add(Benchmark("Foo", "10ms"))).Add( + new PhdEntry + { + Parameters = new Dictionary { { "A", 10 }, { "B", 20 } } + }.Add(Benchmark("Bar", "20ms"))) + } + }; + + [UsedImplicitly] public static TheoryData EntryDataKeys = TheoryDataHelper.Create(EntryDataMap.Keys); + + private static PhdEntry Root() => new PhdEntry + { + Meta = new PhdMeta + { + Table = new PhdTableConfig + { + ColumnDefinitions = + [ + new PhdColumnDefinition(".engine") { Cloud = "primary", IsSelfExplanatory = true, IsAtomic = true }, + new PhdColumnDefinition(".host.os") { Cloud = "primary", IsSelfExplanatory = true, IsAtomic = true }, + new PhdColumnDefinition(".host.cpu") { Cloud = "primary", IsSelfExplanatory = true, IsAtomic = true }, + new PhdColumnDefinition(".benchmark") { Cloud = "secondary" }, + new PhdColumnDefinition(".job") { Cloud = "secondary", Compressed = true }, + new PhdColumnDefinition("=center"), + new PhdColumnDefinition("=spread") + ] + } + }, + Engine = PhdMock.Engine, + Host = new HostEnvironmentInfoBuilder().Build().ToPhd() + }; + + private static PhdEntry Job(RuntimeMoniker? runtime = null, Jit? jit = null, int? affinity = null) => new PhdEntry + { + Job = new PhdJob + { + Environment = new BdnEnvironment { Runtime = runtime, Jit = jit, Affinity = affinity } + } + }; + + private static PhdEntry Benchmark(string name, params string[] metrics) => new PhdEntry + { + Benchmark = new BdnBenchmark { Type = "Bench", Method = name } + }.AddMetrics(metrics); + + private Task VerifyString(string key, string content) + { + output.WriteLine(content); + var settings = VerifyHelper.Create("Phd"); + settings.UseParameters(key); + return Verifier.Verify(content, settings); + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default01.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default01.verified.txt new file mode 100644 index 0000000000..a412c88898 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default01.verified.txt @@ -0,0 +1,190 @@ +[Keys] + .benchmark + .benchmark.method + .benchmark.type + .engine + .engine.name + .engine.version + .host + .host.chronometerFrequency + .host.configuration + .host.cpu + .host.cpu.logicalCoreCount + .host.cpu.maxFrequencyHz + .host.cpu.nominalFrequencyHz + .host.cpu.physicalCoreCount + .host.cpu.physicalProcessorCount + .host.cpu.processorName + .host.dotNetSdkVersion + .host.hardwareTimerKind + .host.hasAttachedDebugger + .host.hasRyuJit + .host.os + .host.os.display + .host.runtimeVersion + .iterationIndex + .unit + .value + +[Entry0] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .unit = ns + .value = 10 +[Entry1] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .unit = ns + .value = 11 +[Entry2] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .unit = ns + .value = 12 +[Entry3] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .unit = ns + .value = 200 +[Entry4] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .unit = ns + .value = 201 +[Entry5] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .unit = ns + .value = 202 \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default02.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default02.verified.txt new file mode 100644 index 0000000000..c8759facf6 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default02.verified.txt @@ -0,0 +1,391 @@ +[Keys] + .benchmark + .benchmark.method + .benchmark.type + .engine + .engine.name + .engine.version + .host + .host.chronometerFrequency + .host.configuration + .host.cpu + .host.cpu.logicalCoreCount + .host.cpu.maxFrequencyHz + .host.cpu.nominalFrequencyHz + .host.cpu.physicalCoreCount + .host.cpu.physicalProcessorCount + .host.cpu.processorName + .host.dotNetSdkVersion + .host.hardwareTimerKind + .host.hasAttachedDebugger + .host.hasRyuJit + .host.os + .host.os.display + .host.runtimeVersion + .iterationIndex + .job + .job.environment + .job.environment.runtime + .unit + .value + +[Entry0] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.runtime = Net481 + .unit = ns + .value = 10 +[Entry1] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.runtime = Net481 + .unit = ns + .value = 11 +[Entry2] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.runtime = Net481 + .unit = ns + .value = 12 +[Entry3] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.runtime = Net481 + .unit = ns + .value = 20 +[Entry4] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.runtime = Net481 + .unit = ns + .value = 21 +[Entry5] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.runtime = Net481 + .unit = ns + .value = 22 +[Entry6] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.runtime = Net70 + .unit = ns + .value = 30 +[Entry7] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.runtime = Net70 + .unit = ns + .value = 31 +[Entry8] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.runtime = Net70 + .unit = ns + .value = 32 +[Entry9] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.runtime = Net70 + .unit = ns + .value = 40 +[Entry10] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.runtime = Net70 + .unit = ns + .value = 41 +[Entry11] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.runtime = Net70 + .unit = ns + .value = 42 \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default03.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default03.verified.txt new file mode 100644 index 0000000000..af8395637c --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default03.verified.txt @@ -0,0 +1,218 @@ +[Keys] + .benchmark + .benchmark.method + .benchmark.type + .engine + .engine.name + .engine.version + .host + .host.chronometerFrequency + .host.configuration + .host.cpu + .host.cpu.logicalCoreCount + .host.cpu.maxFrequencyHz + .host.cpu.nominalFrequencyHz + .host.cpu.physicalCoreCount + .host.cpu.physicalProcessorCount + .host.cpu.processorName + .host.dotNetSdkVersion + .host.hardwareTimerKind + .host.hasAttachedDebugger + .host.hasRyuJit + .host.os + .host.os.display + .host.runtimeVersion + .iterationIndex + .job + .job.environment + .job.environment.jit + .job.environment.runtime + .unit + .value + +[Entry0] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 30 +[Entry1] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 31 +[Entry2] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 32 +[Entry3] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 40 +[Entry4] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 41 +[Entry5] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 42 \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default04.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default04.verified.txt new file mode 100644 index 0000000000..1179e76cd5 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default04.verified.txt @@ -0,0 +1,404 @@ +[Keys] + .benchmark + .benchmark.method + .benchmark.type + .engine + .engine.name + .engine.version + .host + .host.chronometerFrequency + .host.configuration + .host.cpu + .host.cpu.logicalCoreCount + .host.cpu.maxFrequencyHz + .host.cpu.nominalFrequencyHz + .host.cpu.physicalCoreCount + .host.cpu.physicalProcessorCount + .host.cpu.processorName + .host.dotNetSdkVersion + .host.hardwareTimerKind + .host.hasAttachedDebugger + .host.hasRyuJit + .host.os + .host.os.display + .host.runtimeVersion + .iterationIndex + .job + .job.environment + .job.environment.jit + .job.environment.runtime + .unit + .value + +[Entry0] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.jit = LegacyJit + .job.environment.runtime = Net481 + .unit = ns + .value = 10 +[Entry1] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.jit = LegacyJit + .job.environment.runtime = Net481 + .unit = ns + .value = 11 +[Entry2] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.jit = LegacyJit + .job.environment.runtime = Net481 + .unit = ns + .value = 12 +[Entry3] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.jit = LegacyJit + .job.environment.runtime = Net481 + .unit = ns + .value = 20 +[Entry4] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.jit = LegacyJit + .job.environment.runtime = Net481 + .unit = ns + .value = 21 +[Entry5] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.jit = LegacyJit + .job.environment.runtime = Net481 + .unit = ns + .value = 22 +[Entry6] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 30 +[Entry7] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 31 +[Entry8] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 32 +[Entry9] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 40 +[Entry10] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 41 +[Entry11] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 42 \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default05.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default05.verified.txt new file mode 100644 index 0000000000..ef0556ab95 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=default05.verified.txt @@ -0,0 +1,3873 @@ +[Keys] + .benchmark + .benchmark.method + .benchmark.type + .engine + .engine.name + .engine.version + .host + .host.chronometerFrequency + .host.configuration + .host.cpu + .host.cpu.logicalCoreCount + .host.cpu.maxFrequencyHz + .host.cpu.nominalFrequencyHz + .host.cpu.physicalCoreCount + .host.cpu.physicalProcessorCount + .host.cpu.processorName + .host.dotNetSdkVersion + .host.hardwareTimerKind + .host.hasAttachedDebugger + .host.hasRyuJit + .host.os + .host.os.display + .host.runtimeVersion + .iterationIndex + .job + .job.environment + .job.environment.affinity + .job.environment.jit + .job.environment.runtime + .unit + .value + +[Entry0] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 0 + .job.environment.jit = RyuJit + .job.environment.runtime = HostProcess + .unit = ns + .value = 1 +[Entry1] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 0 + .job.environment.jit = RyuJit + .job.environment.runtime = HostProcess + .unit = ns + .value = 2 +[Entry2] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 0 + .job.environment.jit = RyuJit + .job.environment.runtime = HostProcess + .unit = ns + .value = 3 +[Entry3] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 0 + .job.environment.jit = RyuJit + .job.environment.runtime = HostProcess + .unit = ns + .value = 6 +[Entry4] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 0 + .job.environment.jit = RyuJit + .job.environment.runtime = HostProcess + .unit = ns + .value = 7 +[Entry5] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 0 + .job.environment.jit = RyuJit + .job.environment.runtime = HostProcess + .unit = ns + .value = 8 +[Entry6] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 1 + .job.environment.jit = RyuJit + .job.environment.runtime = NotRecognized + .unit = ns + .value = 11 +[Entry7] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 1 + .job.environment.jit = RyuJit + .job.environment.runtime = NotRecognized + .unit = ns + .value = 12 +[Entry8] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 1 + .job.environment.jit = RyuJit + .job.environment.runtime = NotRecognized + .unit = ns + .value = 13 +[Entry9] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 1 + .job.environment.jit = RyuJit + .job.environment.runtime = NotRecognized + .unit = ns + .value = 16 +[Entry10] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 1 + .job.environment.jit = RyuJit + .job.environment.runtime = NotRecognized + .unit = ns + .value = 17 +[Entry11] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 1 + .job.environment.jit = RyuJit + .job.environment.runtime = NotRecognized + .unit = ns + .value = 18 +[Entry12] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 2 + .job.environment.jit = RyuJit + .job.environment.runtime = Mono + .unit = ns + .value = 21 +[Entry13] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 2 + .job.environment.jit = RyuJit + .job.environment.runtime = Mono + .unit = ns + .value = 22 +[Entry14] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 2 + .job.environment.jit = RyuJit + .job.environment.runtime = Mono + .unit = ns + .value = 23 +[Entry15] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 2 + .job.environment.jit = RyuJit + .job.environment.runtime = Mono + .unit = ns + .value = 26 +[Entry16] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 2 + .job.environment.jit = RyuJit + .job.environment.runtime = Mono + .unit = ns + .value = 27 +[Entry17] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 2 + .job.environment.jit = RyuJit + .job.environment.runtime = Mono + .unit = ns + .value = 28 +[Entry18] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 3 + .job.environment.jit = RyuJit + .job.environment.runtime = Net461 + .unit = ns + .value = 31 +[Entry19] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 3 + .job.environment.jit = RyuJit + .job.environment.runtime = Net461 + .unit = ns + .value = 32 +[Entry20] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 3 + .job.environment.jit = RyuJit + .job.environment.runtime = Net461 + .unit = ns + .value = 33 +[Entry21] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 3 + .job.environment.jit = RyuJit + .job.environment.runtime = Net461 + .unit = ns + .value = 36 +[Entry22] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 3 + .job.environment.jit = RyuJit + .job.environment.runtime = Net461 + .unit = ns + .value = 37 +[Entry23] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 3 + .job.environment.jit = RyuJit + .job.environment.runtime = Net461 + .unit = ns + .value = 38 +[Entry24] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 4 + .job.environment.jit = RyuJit + .job.environment.runtime = Net462 + .unit = ns + .value = 41 +[Entry25] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 4 + .job.environment.jit = RyuJit + .job.environment.runtime = Net462 + .unit = ns + .value = 42 +[Entry26] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 4 + .job.environment.jit = RyuJit + .job.environment.runtime = Net462 + .unit = ns + .value = 43 +[Entry27] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 4 + .job.environment.jit = RyuJit + .job.environment.runtime = Net462 + .unit = ns + .value = 46 +[Entry28] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 4 + .job.environment.jit = RyuJit + .job.environment.runtime = Net462 + .unit = ns + .value = 47 +[Entry29] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 4 + .job.environment.jit = RyuJit + .job.environment.runtime = Net462 + .unit = ns + .value = 48 +[Entry30] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 5 + .job.environment.jit = RyuJit + .job.environment.runtime = Net47 + .unit = ns + .value = 51 +[Entry31] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 5 + .job.environment.jit = RyuJit + .job.environment.runtime = Net47 + .unit = ns + .value = 52 +[Entry32] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 5 + .job.environment.jit = RyuJit + .job.environment.runtime = Net47 + .unit = ns + .value = 53 +[Entry33] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 5 + .job.environment.jit = RyuJit + .job.environment.runtime = Net47 + .unit = ns + .value = 56 +[Entry34] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 5 + .job.environment.jit = RyuJit + .job.environment.runtime = Net47 + .unit = ns + .value = 57 +[Entry35] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 5 + .job.environment.jit = RyuJit + .job.environment.runtime = Net47 + .unit = ns + .value = 58 +[Entry36] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 6 + .job.environment.jit = RyuJit + .job.environment.runtime = Net471 + .unit = ns + .value = 61 +[Entry37] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 6 + .job.environment.jit = RyuJit + .job.environment.runtime = Net471 + .unit = ns + .value = 62 +[Entry38] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 6 + .job.environment.jit = RyuJit + .job.environment.runtime = Net471 + .unit = ns + .value = 63 +[Entry39] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 6 + .job.environment.jit = RyuJit + .job.environment.runtime = Net471 + .unit = ns + .value = 66 +[Entry40] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 6 + .job.environment.jit = RyuJit + .job.environment.runtime = Net471 + .unit = ns + .value = 67 +[Entry41] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 6 + .job.environment.jit = RyuJit + .job.environment.runtime = Net471 + .unit = ns + .value = 68 +[Entry42] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 7 + .job.environment.jit = RyuJit + .job.environment.runtime = Net472 + .unit = ns + .value = 71 +[Entry43] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 7 + .job.environment.jit = RyuJit + .job.environment.runtime = Net472 + .unit = ns + .value = 72 +[Entry44] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 7 + .job.environment.jit = RyuJit + .job.environment.runtime = Net472 + .unit = ns + .value = 73 +[Entry45] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 7 + .job.environment.jit = RyuJit + .job.environment.runtime = Net472 + .unit = ns + .value = 76 +[Entry46] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 7 + .job.environment.jit = RyuJit + .job.environment.runtime = Net472 + .unit = ns + .value = 77 +[Entry47] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 7 + .job.environment.jit = RyuJit + .job.environment.runtime = Net472 + .unit = ns + .value = 78 +[Entry48] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 8 + .job.environment.jit = RyuJit + .job.environment.runtime = Net48 + .unit = ns + .value = 81 +[Entry49] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 8 + .job.environment.jit = RyuJit + .job.environment.runtime = Net48 + .unit = ns + .value = 82 +[Entry50] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 8 + .job.environment.jit = RyuJit + .job.environment.runtime = Net48 + .unit = ns + .value = 83 +[Entry51] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 8 + .job.environment.jit = RyuJit + .job.environment.runtime = Net48 + .unit = ns + .value = 86 +[Entry52] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 8 + .job.environment.jit = RyuJit + .job.environment.runtime = Net48 + .unit = ns + .value = 87 +[Entry53] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 8 + .job.environment.jit = RyuJit + .job.environment.runtime = Net48 + .unit = ns + .value = 88 +[Entry54] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 9 + .job.environment.jit = RyuJit + .job.environment.runtime = Net481 + .unit = ns + .value = 91 +[Entry55] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 9 + .job.environment.jit = RyuJit + .job.environment.runtime = Net481 + .unit = ns + .value = 92 +[Entry56] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 9 + .job.environment.jit = RyuJit + .job.environment.runtime = Net481 + .unit = ns + .value = 93 +[Entry57] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 9 + .job.environment.jit = RyuJit + .job.environment.runtime = Net481 + .unit = ns + .value = 96 +[Entry58] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 9 + .job.environment.jit = RyuJit + .job.environment.runtime = Net481 + .unit = ns + .value = 97 +[Entry59] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 9 + .job.environment.jit = RyuJit + .job.environment.runtime = Net481 + .unit = ns + .value = 98 +[Entry60] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 10 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp20 + .unit = ns + .value = 101 +[Entry61] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 10 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp20 + .unit = ns + .value = 102 +[Entry62] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 10 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp20 + .unit = ns + .value = 103 +[Entry63] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 10 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp20 + .unit = ns + .value = 106 +[Entry64] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 10 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp20 + .unit = ns + .value = 107 +[Entry65] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 10 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp20 + .unit = ns + .value = 108 +[Entry66] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 11 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp21 + .unit = ns + .value = 111 +[Entry67] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 11 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp21 + .unit = ns + .value = 112 +[Entry68] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 11 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp21 + .unit = ns + .value = 113 +[Entry69] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 11 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp21 + .unit = ns + .value = 116 +[Entry70] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 11 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp21 + .unit = ns + .value = 117 +[Entry71] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 11 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp21 + .unit = ns + .value = 118 +[Entry72] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 12 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp22 + .unit = ns + .value = 121 +[Entry73] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 12 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp22 + .unit = ns + .value = 122 +[Entry74] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 12 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp22 + .unit = ns + .value = 123 +[Entry75] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 12 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp22 + .unit = ns + .value = 126 +[Entry76] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 12 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp22 + .unit = ns + .value = 127 +[Entry77] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 12 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp22 + .unit = ns + .value = 128 +[Entry78] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 13 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp30 + .unit = ns + .value = 131 +[Entry79] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 13 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp30 + .unit = ns + .value = 132 +[Entry80] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 13 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp30 + .unit = ns + .value = 133 +[Entry81] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 13 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp30 + .unit = ns + .value = 136 +[Entry82] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 13 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp30 + .unit = ns + .value = 137 +[Entry83] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 13 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp30 + .unit = ns + .value = 138 +[Entry84] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 14 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp31 + .unit = ns + .value = 141 +[Entry85] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 14 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp31 + .unit = ns + .value = 142 +[Entry86] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 14 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp31 + .unit = ns + .value = 143 +[Entry87] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 14 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp31 + .unit = ns + .value = 146 +[Entry88] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 14 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp31 + .unit = ns + .value = 147 +[Entry89] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 14 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp31 + .unit = ns + .value = 148 +[Entry90] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 15 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp50 + .unit = ns + .value = 151 +[Entry91] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 15 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp50 + .unit = ns + .value = 152 +[Entry92] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 15 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp50 + .unit = ns + .value = 153 +[Entry93] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 15 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp50 + .unit = ns + .value = 156 +[Entry94] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 15 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp50 + .unit = ns + .value = 157 +[Entry95] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 15 + .job.environment.jit = RyuJit + .job.environment.runtime = NetCoreApp50 + .unit = ns + .value = 158 +[Entry96] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 16 + .job.environment.jit = RyuJit + .job.environment.runtime = Net50 + .unit = ns + .value = 161 +[Entry97] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 16 + .job.environment.jit = RyuJit + .job.environment.runtime = Net50 + .unit = ns + .value = 162 +[Entry98] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 16 + .job.environment.jit = RyuJit + .job.environment.runtime = Net50 + .unit = ns + .value = 163 +[Entry99] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 16 + .job.environment.jit = RyuJit + .job.environment.runtime = Net50 + .unit = ns + .value = 166 +[Entry100] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 16 + .job.environment.jit = RyuJit + .job.environment.runtime = Net50 + .unit = ns + .value = 167 +[Entry101] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 16 + .job.environment.jit = RyuJit + .job.environment.runtime = Net50 + .unit = ns + .value = 168 +[Entry102] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 17 + .job.environment.jit = RyuJit + .job.environment.runtime = Net60 + .unit = ns + .value = 171 +[Entry103] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 17 + .job.environment.jit = RyuJit + .job.environment.runtime = Net60 + .unit = ns + .value = 172 +[Entry104] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 17 + .job.environment.jit = RyuJit + .job.environment.runtime = Net60 + .unit = ns + .value = 173 +[Entry105] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 17 + .job.environment.jit = RyuJit + .job.environment.runtime = Net60 + .unit = ns + .value = 176 +[Entry106] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 17 + .job.environment.jit = RyuJit + .job.environment.runtime = Net60 + .unit = ns + .value = 177 +[Entry107] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 17 + .job.environment.jit = RyuJit + .job.environment.runtime = Net60 + .unit = ns + .value = 178 +[Entry108] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 18 + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 181 +[Entry109] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 18 + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 182 +[Entry110] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 18 + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 183 +[Entry111] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 18 + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 186 +[Entry112] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 18 + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 187 +[Entry113] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 18 + .job.environment.jit = RyuJit + .job.environment.runtime = Net70 + .unit = ns + .value = 188 +[Entry114] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 19 + .job.environment.jit = RyuJit + .job.environment.runtime = Net80 + .unit = ns + .value = 191 +[Entry115] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 19 + .job.environment.jit = RyuJit + .job.environment.runtime = Net80 + .unit = ns + .value = 192 +[Entry116] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 19 + .job.environment.jit = RyuJit + .job.environment.runtime = Net80 + .unit = ns + .value = 193 +[Entry117] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 0 + .job = + .job.environment = + .job.environment.affinity = 19 + .job.environment.jit = RyuJit + .job.environment.runtime = Net80 + .unit = ns + .value = 196 +[Entry118] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 1 + .job = + .job.environment = + .job.environment.affinity = 19 + .job.environment.jit = RyuJit + .job.environment.runtime = Net80 + .unit = ns + .value = 197 +[Entry119] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .engine = BenchmarkDotNet v0.1729.0-mock + .engine.name = BenchmarkDotNet + .engine.version = 0.1729.0-mock + .host = + .host.chronometerFrequency = 2531248 + .host.configuration = CONFIGURATION + .host.cpu = MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + .host.cpu.logicalCoreCount = 8 + .host.cpu.maxFrequencyHz = 3100000000 + .host.cpu.nominalFrequencyHz = 3100000000 + .host.cpu.physicalCoreCount = 4 + .host.cpu.physicalProcessorCount = 1 + .host.cpu.processorName = MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz + .host.dotNetSdkVersion = 1.0.x.mock + .host.hardwareTimerKind = Tsc + .host.hasAttachedDebugger = False + .host.hasRyuJit = True + .host.os = Microsoft Windows NT 10.0.x.mock + .host.os.display = Microsoft Windows NT 10.0.x.mock + .host.runtimeVersion = Clr 4.0.x.mock + .iterationIndex = 2 + .job = + .job.environment = + .job.environment.affinity = 19 + .job.environment.jit = RyuJit + .job.environment.runtime = Net80 + .unit = ns + .value = 198 \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=params01.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=params01.verified.txt new file mode 100644 index 0000000000..ccc03f6896 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=params01.verified.txt @@ -0,0 +1,31 @@ +[Keys] + .benchmark + .benchmark.method + .benchmark.type + .iterationIndex + .parameters + .parameters.a + .parameters.b + .unit + .value + +[Entry0] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .iterationIndex = 0 + .parameters = + .parameters.a = 1 + .parameters.b = 2 + .unit = ms + .value = 10 +[Entry1] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .iterationIndex = 0 + .parameters = + .parameters.a = 10 + .parameters.b = 20 + .unit = ms + .value = 20 \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=sort01.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=sort01.verified.txt new file mode 100644 index 0000000000..f1c1835a59 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdIndexTest_key=sort01.verified.txt @@ -0,0 +1,22 @@ +[Keys] + .benchmark + .benchmark.method + .benchmark.type + .iterationIndex + .unit + .value + +[Entry0] + .benchmark = Bench.Foo + .benchmark.method = Foo + .benchmark.type = Bench + .iterationIndex = 0 + .unit = ms + .value = 10 +[Entry1] + .benchmark = Bench.Bar + .benchmark.method = Bar + .benchmark.type = Bench + .iterationIndex = 0 + .unit = ms + .value = 20 \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default01.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default01.verified.txt new file mode 100644 index 0000000000..49176e7c53 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default01.verified.txt @@ -0,0 +1,123 @@ +BenchmarkDotNet v0.1729.0-mock, Microsoft Windows NT 10.0.x.mock +MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + +Type = Bench + +| Method | Center | Spread | +|:-------|---------:|--------:| +| Foo | 11.0 ns | 0.81 ns | +| Bar | 201.0 ns | 0.81 ns | + +{ + "engine": { + "name": "BenchmarkDotNet", + "version": "0.1729.0-mock" + }, + "host": { + "runtimeVersion": "Clr 4.0.x.mock", + "hasAttachedDebugger": false, + "hasRyuJit": true, + "configuration": "CONFIGURATION", + "dotNetSdkVersion": "1.0.x.mock", + "chronometerFrequency": 2531248, + "hardwareTimerKind": "Tsc", + "os": { + "display": "Microsoft Windows NT 10.0.x.mock" + }, + "cpu": { + "processorName": "MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", + "physicalProcessorCount": 1, + "physicalCoreCount": 4, + "logicalCoreCount": 8, + "nominalFrequencyHz": 3100000000, + "maxFrequencyHz": 3100000000 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 10, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 11, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 12, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 200, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 201, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 202, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ], + "meta": { + "table": { + "columnDefinitions": [ + { + "selector": ".engine", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.os", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.cpu", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".benchmark", + "cloud": "secondary" + }, + { + "selector": ".job", + "cloud": "secondary", + "compressed": true + }, + { + "selector": "=center" + }, + { + "selector": "=spread" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default02.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default02.verified.txt new file mode 100644 index 0000000000..f57b844e3f --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default02.verified.txt @@ -0,0 +1,189 @@ +BenchmarkDotNet v0.1729.0-mock, Microsoft Windows NT 10.0.x.mock +MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + +Type = Bench + +| Method | Runtime | Center | Spread | +|:-------|:--------|--------:|--------:| +| Foo | Net481 | 11.0 ns | 0.81 ns | +| Bar | Net481 | 21.0 ns | 0.81 ns | +| Foo | Net70 | 31.0 ns | 0.81 ns | +| Bar | Net70 | 41.0 ns | 0.81 ns | + +{ + "engine": { + "name": "BenchmarkDotNet", + "version": "0.1729.0-mock" + }, + "host": { + "runtimeVersion": "Clr 4.0.x.mock", + "hasAttachedDebugger": false, + "hasRyuJit": true, + "configuration": "CONFIGURATION", + "dotNetSdkVersion": "1.0.x.mock", + "chronometerFrequency": 2531248, + "hardwareTimerKind": "Tsc", + "os": { + "display": "Microsoft Windows NT 10.0.x.mock" + }, + "cpu": { + "processorName": "MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", + "physicalProcessorCount": 1, + "physicalCoreCount": 4, + "logicalCoreCount": 8, + "nominalFrequencyHz": 3100000000, + "maxFrequencyHz": 3100000000 + } + }, + "nested": [ + { + "job": { + "environment": { + "runtime": "net481" + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 10, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 11, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 12, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 20, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 21, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 22, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net70" + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 30, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 31, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 32, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 40, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 41, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 42, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + } + ], + "meta": { + "table": { + "columnDefinitions": [ + { + "selector": ".engine", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.os", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.cpu", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".benchmark", + "cloud": "secondary" + }, + { + "selector": ".job", + "cloud": "secondary", + "compressed": true + }, + { + "selector": "=center" + }, + { + "selector": "=spread" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default03.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default03.verified.txt new file mode 100644 index 0000000000..1216d13bfd --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default03.verified.txt @@ -0,0 +1,133 @@ +BenchmarkDotNet v0.1729.0-mock, Microsoft Windows NT 10.0.x.mock +MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + +Type = Bench, Runtime = Net70, Jit = RyuJit + +| Method | Center | Spread | +|:-------|--------:|--------:| +| Foo | 31.0 ns | 0.81 ns | +| Bar | 41.0 ns | 0.81 ns | + +{ + "engine": { + "name": "BenchmarkDotNet", + "version": "0.1729.0-mock" + }, + "host": { + "runtimeVersion": "Clr 4.0.x.mock", + "hasAttachedDebugger": false, + "hasRyuJit": true, + "configuration": "CONFIGURATION", + "dotNetSdkVersion": "1.0.x.mock", + "chronometerFrequency": 2531248, + "hardwareTimerKind": "Tsc", + "os": { + "display": "Microsoft Windows NT 10.0.x.mock" + }, + "cpu": { + "processorName": "MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", + "physicalProcessorCount": 1, + "physicalCoreCount": 4, + "logicalCoreCount": 8, + "nominalFrequencyHz": 3100000000, + "maxFrequencyHz": 3100000000 + } + }, + "nested": [ + { + "job": { + "environment": { + "runtime": "net70", + "jit": "ryuJit" + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 30, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 31, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 32, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 40, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 41, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 42, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + } + ], + "meta": { + "table": { + "columnDefinitions": [ + { + "selector": ".engine", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.os", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.cpu", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".benchmark", + "cloud": "secondary" + }, + { + "selector": ".job", + "cloud": "secondary", + "compressed": true + }, + { + "selector": "=center" + }, + { + "selector": "=spread" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default04.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default04.verified.txt new file mode 100644 index 0000000000..d6e11e30c8 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default04.verified.txt @@ -0,0 +1,194 @@ +BenchmarkDotNet v0.1729.0-mock, Microsoft Windows NT 10.0.x.mock +MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + +Type = Bench + +@Net481LegacyJit: Runtime = Net481, Jit = LegacyJit +@Net70RyuJit: Runtime = Net70, Jit = RyuJit + +| Method | Job | Center | Spread | +|:-------|:----------------|--------:|--------:| +| Foo | Net481LegacyJit | 11.0 ns | 0.81 ns | +| Bar | Net481LegacyJit | 21.0 ns | 0.81 ns | +| Foo | Net70RyuJit | 31.0 ns | 0.81 ns | +| Bar | Net70RyuJit | 41.0 ns | 0.81 ns | + +{ + "engine": { + "name": "BenchmarkDotNet", + "version": "0.1729.0-mock" + }, + "host": { + "runtimeVersion": "Clr 4.0.x.mock", + "hasAttachedDebugger": false, + "hasRyuJit": true, + "configuration": "CONFIGURATION", + "dotNetSdkVersion": "1.0.x.mock", + "chronometerFrequency": 2531248, + "hardwareTimerKind": "Tsc", + "os": { + "display": "Microsoft Windows NT 10.0.x.mock" + }, + "cpu": { + "processorName": "MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", + "physicalProcessorCount": 1, + "physicalCoreCount": 4, + "logicalCoreCount": 8, + "nominalFrequencyHz": 3100000000, + "maxFrequencyHz": 3100000000 + } + }, + "nested": [ + { + "job": { + "environment": { + "runtime": "net481", + "jit": "legacyJit" + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 10, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 11, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 12, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 20, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 21, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 22, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net70", + "jit": "ryuJit" + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 30, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 31, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 32, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 40, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 41, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 42, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + } + ], + "meta": { + "table": { + "columnDefinitions": [ + { + "selector": ".engine", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.os", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.cpu", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".benchmark", + "cloud": "secondary" + }, + { + "selector": ".job", + "cloud": "secondary", + "compressed": true + }, + { + "selector": "=center" + }, + { + "selector": "=spread" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default05.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default05.verified.txt new file mode 100644 index 0000000000..4c02ab89c5 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=default05.verified.txt @@ -0,0 +1,1276 @@ +BenchmarkDotNet v0.1729.0-mock, Microsoft Windows NT 10.0.x.mock +MockIntel Core i7-6700HQ CPU 2.60GHz (Max: 3.10GHz), 1 CPU, 8 logical and 4 physical cores + +Type = Bench, Jit = RyuJit + +@HostProcessAffinity0: Runtime = HostProcess, Affinity = 0 +@Squid: Runtime = NotRecognized, Affinity = 1 +@MonoAffinity2: Runtime = Mono, Affinity = 2 +@Net461Affinity3: Runtime = Net461, Affinity = 3 +@Net462Affinity4: Runtime = Net462, Affinity = 4 +@Net47Affinity5: Runtime = Net47, Affinity = 5 +@Net471Affinity6: Runtime = Net471, Affinity = 6 +@Net472Affinity7: Runtime = Net472, Affinity = 7 +@Net48Affinity8: Runtime = Net48, Affinity = 8 +@Net481Affinity9: Runtime = Net481, Affinity = 9 +@Liger: Runtime = NetCoreApp20, Affinity = 10 +@Rat: Runtime = NetCoreApp21, Affinity = 11 +@Skate: Runtime = NetCoreApp22, Affinity = 12 +@Perch: Runtime = NetCoreApp30, Affinity = 13 +@Roach: Runtime = NetCoreApp31, Affinity = 14 +@Roe: Runtime = NetCoreApp50, Affinity = 15 +@Net50Affinity16: Runtime = Net50, Affinity = 16 +@Net60Affinity17: Runtime = Net60, Affinity = 17 +@Net70Affinity18: Runtime = Net70, Affinity = 18 +@Net80Affinity19: Runtime = Net80, Affinity = 19 + +| Method | Job | Center | Spread | +|:-------|:---------------------|----------:|--------:| +| Foo | HostProcessAffinity0 | 2.00 ns | 0.81 ns | +| Bar | HostProcessAffinity0 | 7.00 ns | 0.81 ns | +| Foo | Squid | 12.00 ns | 0.81 ns | +| Bar | Squid | 17.00 ns | 0.81 ns | +| Foo | MonoAffinity2 | 22.00 ns | 0.81 ns | +| Bar | MonoAffinity2 | 27.00 ns | 0.81 ns | +| Foo | Net461Affinity3 | 32.00 ns | 0.81 ns | +| Bar | Net461Affinity3 | 37.00 ns | 0.81 ns | +| Foo | Net462Affinity4 | 42.00 ns | 0.81 ns | +| Bar | Net462Affinity4 | 47.00 ns | 0.81 ns | +| Foo | Net47Affinity5 | 52.00 ns | 0.81 ns | +| Bar | Net47Affinity5 | 57.00 ns | 0.81 ns | +| Foo | Net471Affinity6 | 62.00 ns | 0.81 ns | +| Bar | Net471Affinity6 | 67.00 ns | 0.81 ns | +| Foo | Net472Affinity7 | 72.00 ns | 0.81 ns | +| Bar | Net472Affinity7 | 77.00 ns | 0.81 ns | +| Foo | Net48Affinity8 | 82.00 ns | 0.81 ns | +| Bar | Net48Affinity8 | 87.00 ns | 0.81 ns | +| Foo | Net481Affinity9 | 92.00 ns | 0.81 ns | +| Bar | Net481Affinity9 | 97.00 ns | 0.81 ns | +| Foo | Liger | 102.00 ns | 0.81 ns | +| Bar | Liger | 107.00 ns | 0.81 ns | +| Foo | Rat | 112.00 ns | 0.81 ns | +| Bar | Rat | 117.00 ns | 0.81 ns | +| Foo | Skate | 122.00 ns | 0.81 ns | +| Bar | Skate | 127.00 ns | 0.81 ns | +| Foo | Perch | 132.00 ns | 0.81 ns | +| Bar | Perch | 137.00 ns | 0.81 ns | +| Foo | Roach | 142.00 ns | 0.81 ns | +| Bar | Roach | 147.00 ns | 0.81 ns | +| Foo | Roe | 152.00 ns | 0.81 ns | +| Bar | Roe | 157.00 ns | 0.81 ns | +| Foo | Net50Affinity16 | 162.00 ns | 0.81 ns | +| Bar | Net50Affinity16 | 167.00 ns | 0.81 ns | +| Foo | Net60Affinity17 | 172.00 ns | 0.81 ns | +| Bar | Net60Affinity17 | 177.00 ns | 0.81 ns | +| Foo | Net70Affinity18 | 182.00 ns | 0.81 ns | +| Bar | Net70Affinity18 | 187.00 ns | 0.81 ns | +| Foo | Net80Affinity19 | 192.00 ns | 0.81 ns | +| Bar | Net80Affinity19 | 197.00 ns | 0.81 ns | + +{ + "engine": { + "name": "BenchmarkDotNet", + "version": "0.1729.0-mock" + }, + "host": { + "runtimeVersion": "Clr 4.0.x.mock", + "hasAttachedDebugger": false, + "hasRyuJit": true, + "configuration": "CONFIGURATION", + "dotNetSdkVersion": "1.0.x.mock", + "chronometerFrequency": 2531248, + "hardwareTimerKind": "Tsc", + "os": { + "display": "Microsoft Windows NT 10.0.x.mock" + }, + "cpu": { + "processorName": "MockIntel(R) Core(TM) i7-6700HQ CPU 2.60GHz", + "physicalProcessorCount": 1, + "physicalCoreCount": 4, + "logicalCoreCount": 8, + "nominalFrequencyHz": 3100000000, + "maxFrequencyHz": 3100000000 + } + }, + "nested": [ + { + "job": { + "environment": { + "runtime": "hostProcess", + "jit": "ryuJit", + "affinity": 0 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 1, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 2, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 3, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 6, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 7, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 8, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "notRecognized", + "jit": "ryuJit", + "affinity": 1 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 11, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 12, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 13, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 16, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 17, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 18, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "mono", + "jit": "ryuJit", + "affinity": 2 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 21, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 22, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 23, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 26, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 27, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 28, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net461", + "jit": "ryuJit", + "affinity": 3 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 31, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 32, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 33, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 36, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 37, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 38, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net462", + "jit": "ryuJit", + "affinity": 4 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 41, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 42, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 43, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 46, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 47, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 48, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net47", + "jit": "ryuJit", + "affinity": 5 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 51, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 52, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 53, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 56, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 57, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 58, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net471", + "jit": "ryuJit", + "affinity": 6 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 61, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 62, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 63, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 66, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 67, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 68, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net472", + "jit": "ryuJit", + "affinity": 7 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 71, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 72, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 73, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 76, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 77, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 78, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net48", + "jit": "ryuJit", + "affinity": 8 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 81, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 82, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 83, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 86, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 87, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 88, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net481", + "jit": "ryuJit", + "affinity": 9 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 91, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 92, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 93, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 96, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 97, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 98, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "netCoreApp20", + "jit": "ryuJit", + "affinity": 10 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 101, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 102, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 103, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 106, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 107, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 108, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "netCoreApp21", + "jit": "ryuJit", + "affinity": 11 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 111, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 112, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 113, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 116, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 117, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 118, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "netCoreApp22", + "jit": "ryuJit", + "affinity": 12 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 121, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 122, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 123, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 126, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 127, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 128, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "netCoreApp30", + "jit": "ryuJit", + "affinity": 13 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 131, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 132, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 133, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 136, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 137, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 138, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "netCoreApp31", + "jit": "ryuJit", + "affinity": 14 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 141, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 142, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 143, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 146, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 147, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 148, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "netCoreApp50", + "jit": "ryuJit", + "affinity": 15 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 151, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 152, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 153, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 156, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 157, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 158, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net50", + "jit": "ryuJit", + "affinity": 16 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 161, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 162, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 163, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 166, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 167, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 168, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net60", + "jit": "ryuJit", + "affinity": 17 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 171, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 172, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 173, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 176, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 177, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 178, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net70", + "jit": "ryuJit", + "affinity": 18 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 181, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 182, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 183, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 186, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 187, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 188, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + }, + { + "job": { + "environment": { + "runtime": "net80", + "jit": "ryuJit", + "affinity": 19 + } + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 191, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 192, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 193, + "unit": "ns", + "iterationIndex": 2 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 196, + "unit": "ns", + "iterationIndex": 0 + }, + { + "value": 197, + "unit": "ns", + "iterationIndex": 1 + }, + { + "value": 198, + "unit": "ns", + "iterationIndex": 2 + } + ] + } + ] + } + ], + "meta": { + "table": { + "columnDefinitions": [ + { + "selector": ".engine", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.os", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".host.cpu", + "cloud": "primary", + "isSelfExplanatory": true, + "isAtomic": true + }, + { + "selector": ".benchmark", + "cloud": "secondary" + }, + { + "selector": ".job", + "cloud": "secondary", + "compressed": true + }, + { + "selector": "=center" + }, + { + "selector": "=spread" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=params01.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=params01.verified.txt new file mode 100644 index 0000000000..afa4ccffd7 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=params01.verified.txt @@ -0,0 +1,66 @@ +| Method | A | B | Center | +|:-------|---:|---:|--------:| +| Foo | 1 | 2 | 10.0 ms | +| Bar | 10 | 20 | 20.0 ms | + +{ + "nested": [ + { + "parameters": { + "a": 1, + "b": 2 + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 10, + "unit": "ms", + "iterationIndex": 0 + } + ] + } + ] + }, + { + "parameters": { + "a": 10, + "b": 20 + }, + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 20, + "unit": "ms", + "iterationIndex": 0 + } + ] + } + ] + } + ], + "meta": { + "table": { + "columnDefinitions": [ + { + "selector": ".benchmark.method" + }, + { + "selector": ".parameters" + }, + { + "selector": "=center" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=sort01.verified.txt b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=sort01.verified.txt new file mode 100644 index 0000000000..2466122dde --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Phd/VerifiedFiles/Phd.PhdTableTest_key=sort01.verified.txt @@ -0,0 +1,53 @@ +| Method | Center | +|:-------|--------:| +| Bar | 20.0 ms | +| Foo | 10.0 ms | + +{ + "nested": [ + { + "benchmark": { + "type": "Bench", + "method": "Foo" + }, + "nested": [ + { + "value": 10, + "unit": "ms", + "iterationIndex": 0 + } + ] + }, + { + "benchmark": { + "type": "Bench", + "method": "Bar" + }, + "nested": [ + { + "value": 20, + "unit": "ms", + "iterationIndex": 0 + } + ] + } + ], + "meta": { + "table": { + "columnDefinitions": [ + { + "selector": ".benchmark.method" + }, + { + "selector": "=center" + } + ], + "sortPolicies": [ + { + "selector": "=center", + "direction": "descending" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Portability/Cpu/CpuInfoFormatterTests.cs b/tests/BenchmarkDotNet.Tests/Portability/Cpu/CpuInfoFormatterTests.cs deleted file mode 100644 index 1fd7b14545..0000000000 --- a/tests/BenchmarkDotNet.Tests/Portability/Cpu/CpuInfoFormatterTests.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Text; -using System.Threading.Tasks; -using BenchmarkDotNet.Portability.Cpu; -using BenchmarkDotNet.Tests.Builders; -using VerifyXunit; -using Xunit; - -namespace BenchmarkDotNet.Tests.Portability.Cpu; - -[Collection("VerifyTests")] -[UsesVerify] -public class CpuInfoFormatterTests -{ - [Fact] - public Task FormatTest() - { - var captions = new StringBuilder(); - foreach (string? processorName in new[] { null, "", "Intel" }) - foreach (int? physicalProcessorCount in new int?[] { null, 0, 1, 2 }) - foreach (int? physicalCoreCount in new int?[] { null, 0, 1, 2 }) - foreach (int? logicalCoreCount in new int?[] { null, 0, 1, 2 }) - { - var mockCpuInfo = new CpuInfo(processorName, physicalProcessorCount, physicalCoreCount, logicalCoreCount, null, null); - captions.AppendLine(CpuInfoFormatter.Format(mockCpuInfo)); - } - - var settings = VerifySettingsFactory.Create(); - return Verifier.Verify(captions.ToString(), settings); - } -} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs b/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs index 29f8a65798..5fa5f47051 100644 --- a/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs +++ b/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using BenchmarkDotNet.Detectors; using Xunit; namespace BenchmarkDotNet.Tests @@ -110,7 +111,7 @@ public void CurrentRuntimeIsProperlyRecognized() var runtime = RuntimeInformation.GetCurrentRuntime(); #if NETFRAMEWORK - if (RuntimeInformation.IsWindows()) + if (OsDetector.IsWindows()) Assert.True(runtime is ClrRuntime); else Assert.True(runtime is MonoRuntime); diff --git a/tests/BenchmarkDotNet.Tests/SimpleJsonTests.cs b/tests/BenchmarkDotNet.Tests/SimpleJsonTests.cs index 2bb57fe436..fd599aea5b 100644 --- a/tests/BenchmarkDotNet.Tests/SimpleJsonTests.cs +++ b/tests/BenchmarkDotNet.Tests/SimpleJsonTests.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using Xunit; -using JsonSerializer = SimpleJson.SimpleJson; +using JsonSerializer = SimpleJson.SimpleJsonSerializer; namespace BenchmarkDotNet.Tests { diff --git a/tests/BenchmarkDotNet.Tests/Validators/ConfigCompatibilityValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/ConfigCompatibilityValidatorTests.cs index fc73793980..ab09ac2d1c 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/ConfigCompatibilityValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/ConfigCompatibilityValidatorTests.cs @@ -7,6 +7,7 @@ using BenchmarkDotNet.Tests.Loggers; using BenchmarkDotNet.Validators; using System.Linq; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Portability; using Xunit; using Xunit.Abstractions; @@ -22,7 +23,7 @@ public class ConfigCompatibilityValidatorTests [Fact] public void RunningBenchmarksWithIncompatibleConfigsMustFailWithCriticalError() { - if (!KnownIssue.Issue2299.IsFixed && RuntimeInformation.IsMono && RuntimeInformation.IsLinux()) + if (!KnownIssue.Issue2299.IsFixed && RuntimeInformation.IsMono && OsDetector.IsLinux()) { Output.WriteLine(KnownIssue.Issue2299.IgnoreMessage); return; From 296c9962c131ff116527f7e6688fdd0bd8493734 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Tue, 27 Aug 2024 20:58:01 +0200 Subject: [PATCH 31/57] Remove ConfigCompatibilityValidator, fix #2599 --- src/BenchmarkDotNet/Reports/Summary.cs | 4 +- .../Running/BenchmarkRunnerClean.cs | 11 -- .../ConfigCompatibilityValidator.cs | 37 ---- .../ConfigCompatibilityValidatorTests.cs | 181 ------------------ 4 files changed, 2 insertions(+), 231 deletions(-) delete mode 100644 src/BenchmarkDotNet/Validators/ConfigCompatibilityValidator.cs delete mode 100644 tests/BenchmarkDotNet.Tests/Validators/ConfigCompatibilityValidatorTests.cs diff --git a/src/BenchmarkDotNet/Reports/Summary.cs b/src/BenchmarkDotNet/Reports/Summary.cs index 2ccfd4628d..2d08f2a0d9 100644 --- a/src/BenchmarkDotNet/Reports/Summary.cs +++ b/src/BenchmarkDotNet/Reports/Summary.cs @@ -160,7 +160,7 @@ private static IOrderer GetConfiguredOrdererOrDefaultOne(IEnumerable config.Orderer != DefaultOrderer.Instance) .Select(config => config.Orderer) .Distinct() - .SingleOrDefault() + .FirstOrDefault() ?? DefaultOrderer.Instance; private static SummaryStyle GetConfiguredSummaryStyleOrDefaultOne(ImmutableArray benchmarkCases) @@ -173,7 +173,7 @@ private static SummaryStyle GetConfiguredSummaryStyleOrDefaultOne(ImmutableArray #nullable enable .Select(benchmark => benchmark.Config.SummaryStyle) .Distinct() - .SingleOrDefault() + .FirstOrDefault() ?? SummaryStyle.Default; // TODO: GcStats diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs index e8088702f1..6d9d48c3cc 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs @@ -353,17 +353,6 @@ private static ImmutableArray Validate(params BenchmarkRunInfo[ { var validationErrors = new List(); - if (benchmarks.Any(b => b.Config.Options.IsSet(ConfigOptions.JoinSummary))) - { - var joinedCases = benchmarks.SelectMany(b => b.BenchmarksCases).ToArray(); - - validationErrors.AddRange( - ConfigCompatibilityValidator - .FailOnError - .Validate(new ValidationParameters(joinedCases, null)) - ); - } - foreach (var benchmarkRunInfo in benchmarks) validationErrors.AddRange(benchmarkRunInfo.Config.GetCompositeValidator().Validate(new ValidationParameters(benchmarkRunInfo.BenchmarksCases, benchmarkRunInfo.Config))); diff --git a/src/BenchmarkDotNet/Validators/ConfigCompatibilityValidator.cs b/src/BenchmarkDotNet/Validators/ConfigCompatibilityValidator.cs deleted file mode 100644 index 9732f40383..0000000000 --- a/src/BenchmarkDotNet/Validators/ConfigCompatibilityValidator.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Linq; -using System.Collections.Generic; -using BenchmarkDotNet.Reports; - -namespace BenchmarkDotNet.Validators -{ - public class ConfigCompatibilityValidator : IValidator - { - public static readonly ConfigCompatibilityValidator FailOnError = new ConfigCompatibilityValidator(); - - public bool TreatsWarningsAsErrors => true; - - public IEnumerable Validate(ValidationParameters validationParameters) - { - var orderers = - validationParameters - .Benchmarks - .Where(benchmark => benchmark.Config.Orderer != Order.DefaultOrderer.Instance) - .Select(benchmark => benchmark.Config.Orderer) - .Distinct(); - - if (orderers.Count() > 1) - yield return new ValidationError(true, "You use JoinSummary options, but provided configurations cannot be joined. Only one Orderer per benchmark cases is allowed."); - - var styles = - validationParameters - .Benchmarks - .Where(benchmark => benchmark.Config.SummaryStyle != SummaryStyle.Default - && benchmark.Config.SummaryStyle != null) // Paranoid - .Select(benchmark => benchmark.Config.SummaryStyle) - .Distinct(); - - if (styles.Count() > 1) - yield return new ValidationError(true, "You use JoinSummary options, but provided configurations cannot be joined. Only one SummaryStyle per benchmark cases is allowed."); - } - } -} diff --git a/tests/BenchmarkDotNet.Tests/Validators/ConfigCompatibilityValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/ConfigCompatibilityValidatorTests.cs deleted file mode 100644 index ab09ac2d1c..0000000000 --- a/tests/BenchmarkDotNet.Tests/Validators/ConfigCompatibilityValidatorTests.cs +++ /dev/null @@ -1,181 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Columns; -using BenchmarkDotNet.Order; -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Reports; -using BenchmarkDotNet.Tests.Loggers; -using BenchmarkDotNet.Validators; -using System.Linq; -using BenchmarkDotNet.Detectors; -using BenchmarkDotNet.Portability; -using Xunit; -using Xunit.Abstractions; - -namespace BenchmarkDotNet.Tests.Validators -{ - public class ConfigCompatibilityValidatorTests - { - private ITestOutputHelper Output { get; } - - public ConfigCompatibilityValidatorTests(ITestOutputHelper output) => Output = output; - - [Fact] - public void RunningBenchmarksWithIncompatibleConfigsMustFailWithCriticalError() - { - if (!KnownIssue.Issue2299.IsFixed && RuntimeInformation.IsMono && OsDetector.IsLinux()) - { - Output.WriteLine(KnownIssue.Issue2299.IgnoreMessage); - return; - } - - var logger = new OutputLogger(Output); - var config = ManualConfig.CreateEmpty().AddLogger(logger); - var summary = - BenchmarkSwitcher - .FromTypes(new[] { typeof(BenchmarkClassWithExtraOrderer1), typeof(BenchmarkClassWithExtraOrderer2) }) - .RunAllJoined(config); - Assert.True(summary.HasCriticalValidationErrors); - Assert.Contains("You use JoinSummary options, but provided configurations cannot be joined", logger.GetLog()); - Assert.Contains("Orderer", logger.GetLog()); - } - - [Fact] - public void JoinedBenchmarksMustNotHaveDifferentExtraOrderers() - { - var benchmarks = new[] - { - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithExtraOrderer1)), - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithExtraOrderer2)) - }; - - var cases = benchmarks.SelectMany(b => b.BenchmarksCases).ToArray(); - - var validationErrors = - ConfigCompatibilityValidator - .FailOnError - .Validate(new ValidationParameters(cases, null)) - .ToArray(); - - Assert.NotEmpty(validationErrors); - Assert.StartsWith("You use JoinSummary options, but provided configurations cannot be joined", validationErrors.Single().Message); - Assert.Contains("Orderer", validationErrors.Single().Message); - } - - [Fact] - public void JoinedBenchmarksMayHaveOneExtraOrderer() - { - var benchmarks = new[] - { - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithExtraOrderer1)), - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithDefaults1)) - }; - - var cases = benchmarks.SelectMany(b => b.BenchmarksCases).ToArray(); - - var validationErrors = - ConfigCompatibilityValidator - .FailOnError - .Validate(new ValidationParameters(cases, null)) - .ToArray(); - - Assert.Empty(validationErrors); - } - - [Fact] - public void JoinedBenchmarksMayHaveDefaultOrderers() - { - var benchmarks = new[] - { - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithDefaults1)), - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithDefaults2)) - }; - - var cases = benchmarks.SelectMany(b => b.BenchmarksCases).ToArray(); - - var validationErrors = - ConfigCompatibilityValidator - .FailOnError - .Validate(new ValidationParameters(cases, null)) - .ToArray(); - - Assert.Empty(validationErrors); - } - - [Fact] - public void JoinedBenchmarksMustNotHaveDifferentExtraSummaryStyles() - { - // Note: RatioStyle.Value would be the same as SummaryStyle.Default (SummaryStyle implements IEquatable). - ManualConfig config1 = DefaultConfig.Instance.WithSummaryStyle( - SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend)); - ManualConfig config2 = DefaultConfig.Instance.WithSummaryStyle( - SummaryStyle.Default.WithRatioStyle(RatioStyle.Percentage)); - - var benchmarks = new[] - { - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithDefaults1), config1), - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithDefaults2), config2) - }; - - var cases = benchmarks.SelectMany(b => b.BenchmarksCases).ToArray(); - - var validationErrors = - ConfigCompatibilityValidator - .FailOnError - .Validate(new ValidationParameters(cases, null)) - .ToArray(); - - Assert.NotEmpty(validationErrors); - Assert.StartsWith("You use JoinSummary options, but provided configurations cannot be joined", validationErrors.Single().Message); - Assert.Contains("SummaryStyle", validationErrors.Single().Message); - } - - [Fact] - public void JoinedBenchmarksMayHaveOneExtraSummaryStyle() - { - ManualConfig config = DefaultConfig.Instance.WithSummaryStyle( - SummaryStyle.Default.WithZeroMetricValuesInContent()); - var benchmarks = new[] - { - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithDefaults1), null), - BenchmarkConverter.TypeToBenchmarks(typeof(BenchmarkClassWithDefaults2), config) - }; - - var cases = benchmarks.SelectMany(b => b.BenchmarksCases).ToArray(); - - var validationErrors = - ConfigCompatibilityValidator - .FailOnError - .Validate(new ValidationParameters(cases, null)) - .ToArray(); - - Assert.Empty(validationErrors); - } - - [Orderer(SummaryOrderPolicy.Method)] - public class BenchmarkClassWithExtraOrderer1 - { - [Benchmark] - public void Foo() { } - } - - [Orderer(SummaryOrderPolicy.Method)] - public class BenchmarkClassWithExtraOrderer2 - { - [Benchmark] - public void Bar() { } - } - - public class BenchmarkClassWithDefaults1 - { - [Benchmark] - public void Baz() { } - } - - public class BenchmarkClassWithDefaults2 - { - [Benchmark] - public void Buzz() { } - } - } -} From 92f33f21cb4030d66532b10cf44d878353f97197 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Wed, 28 Aug 2024 00:46:14 +0200 Subject: [PATCH 32/57] Refactor dotTrace and dotMemory diagnosers All the common logic of profilers moved into `SnapshotProfilerBase` which is the base for `DotMemoryDiagnoser` and `DotTraceDiagnoser`. The common class is inside the main package, so it can be reused by other tools (not only by JetBrains, applicable for any command-line profiler). The dotTrace/dotMemory diagnoser classes have unique simple implementation. `IsSupported` is duplicated on purpose since future versions of dotTrace and dotMemory may have different sets of supported runtimes. --- .../IntroDotMemoryDiagnoser.cs | 12 +- .../IntroDotTraceDiagnoser.cs | 13 +- .../DotMemoryDiagnoser.cs | 225 ++++++++---------- .../DotMemoryDiagnoserAttribute.cs | 28 +-- .../DotMemoryTool.cs | 140 ----------- .../Progress.cs | 38 --- .../DotTraceDiagnoser.cs | 224 ++++++++--------- .../DotTraceDiagnoserAttribute.cs | 28 +-- .../DotTraceToolBase.cs | 145 ----------- .../ExternalDotTraceTool.cs | 84 ------- .../InProcessDotTraceTool.cs | 28 --- .../Progress.cs | 38 --- .../Diagnosers/SnapshotProfilerBase.cs | 195 +++++++++++++++ .../dotMemory/DotMemoryTests.cs | 16 +- .../dotTrace/DotTraceTests.cs | 16 +- 15 files changed, 448 insertions(+), 782 deletions(-) delete mode 100644 src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs delete mode 100644 src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs delete mode 100644 src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs delete mode 100644 src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs delete mode 100644 src/BenchmarkDotNet.Diagnostics.dotTrace/InProcessDotTraceTool.cs delete mode 100644 src/BenchmarkDotNet.Diagnostics.dotTrace/Progress.cs create mode 100644 src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs diff --git a/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs b/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs index 846e2178f4..894bfc6c34 100644 --- a/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs +++ b/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs @@ -4,16 +4,10 @@ namespace BenchmarkDotNet.Samples { - // Enables dotMemory profiling for all jobs + // Profile benchmarks via dotMemory SelfApi profiling for all jobs [DotMemoryDiagnoser] - // Adds the default "external-process" job - // Profiling is performed using dotMemory Command-Line Profiler - // See: https://www.jetbrains.com/help/dotmemory/Working_with_dotMemory_Command-Line_Profiler.html - [SimpleJob] - // Adds an "in-process" job - // Profiling is performed using dotMemory SelfApi - // NuGet reference: https://www.nuget.org/packages/JetBrains.Profiler.SelfApi - [InProcess] + [SimpleJob] // external-process execution + [InProcess] // in-process execution public class IntroDotMemoryDiagnoser { [Params(1024)] diff --git a/samples/BenchmarkDotNet.Samples/IntroDotTraceDiagnoser.cs b/samples/BenchmarkDotNet.Samples/IntroDotTraceDiagnoser.cs index 351207c78b..047e6ee059 100644 --- a/samples/BenchmarkDotNet.Samples/IntroDotTraceDiagnoser.cs +++ b/samples/BenchmarkDotNet.Samples/IntroDotTraceDiagnoser.cs @@ -3,16 +3,11 @@ namespace BenchmarkDotNet.Samples { - // Enables dotTrace profiling for all jobs + // Profile benchmarks via dotTrace SelfApi profiling for all jobs + // See: https://www.nuget.org/packages/JetBrains.Profiler.SelfApi [DotTraceDiagnoser] - // Adds the default "external-process" job - // Profiling is performed using dotTrace command-line Tools - // See: https://www.jetbrains.com/help/profiler/Performance_Profiling__Profiling_Using_the_Command_Line.html - [SimpleJob] - // Adds an "in-process" job - // Profiling is performed using dotTrace SelfApi - // NuGet reference: https://www.nuget.org/packages/JetBrains.Profiler.SelfApi - [InProcess] + [SimpleJob] // external-process execution + [InProcess] // in-process execution public class IntroDotTraceDiagnoser { [Benchmark] diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs index 5639aa0580..c97f066fa4 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs @@ -1,148 +1,121 @@ using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using BenchmarkDotNet.Analysers; +using System.Reflection; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Reports; -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Validators; -using RunMode = BenchmarkDotNet.Diagnosers.RunMode; +using JetBrains.Profiler.SelfApi; -namespace BenchmarkDotNet.Diagnostics.dotMemory +namespace BenchmarkDotNet.Diagnostics.dotMemory; + +public class DotMemoryDiagnoser(Uri? nugetUrl = null, string? downloadTo = null) : SnapshotProfilerBase { - public class DotMemoryDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null) : IProfiler + public override string ShortName => "dotMemory"; + + protected override void InitTool(Progress progress) { - private DotMemoryTool? tool; + DotMemory.InitAsync(progress, nugetUrl, NuGetApi.V3, downloadTo).Wait(); + } - public IEnumerable Ids => new[] { "DotMemory" }; - public string ShortName => "dotMemory"; + protected override void AttachToCurrentProcess(string snapshotFile) + { + DotMemory.Attach(new DotMemory.Config().SaveToFile(snapshotFile)); + } - public RunMode GetRunMode(BenchmarkCase benchmarkCase) - { - return IsSupported(benchmarkCase.Job.Environment.GetRuntime().RuntimeMoniker) ? RunMode.ExtraRun : RunMode.None; - } + protected override void AttachToProcessByPid(int pid, string snapshotFile) + { + DotMemory.Attach(new DotMemory.Config().ProfileExternalProcess(pid).SaveToFile(snapshotFile)); + } - private readonly List snapshotFilePaths = new (); + protected override void TakeSnapshot() + { + DotMemory.GetSnapshot(); + } - public void Handle(HostSignal signal, DiagnoserActionParameters parameters) - { - var logger = parameters.Config.GetCompositeLogger(); - var job = parameters.BenchmarkCase.Job; + protected override void Detach() + { + DotMemory.Detach(); + } - var runtimeMoniker = job.Environment.GetRuntime().RuntimeMoniker; - if (!IsSupported(runtimeMoniker)) - { - logger.WriteLineError($"Runtime '{runtimeMoniker}' is not supported by dotMemory"); - return; - } + protected override string CreateSnapshotFilePath(DiagnoserActionParameters parameters) + { + return ArtifactFileNameHelper.GetFilePath(parameters, "snapshots", DateTime.Now, "dmw", ".0000".Length); + } - switch (signal) - { - case HostSignal.BeforeAnythingElse: - if (tool is null) - { - tool = new DotMemoryTool(logger, nugetUrl, downloadTo: toolsDownloadFolder); - tool.Init(); - } - break; - case HostSignal.BeforeActualRun: - if (tool is null) - throw new InvalidOperationException("DotMemory tool is not initialized"); - snapshotFilePaths.Add(tool.Start(parameters)); - break; - case HostSignal.AfterActualRun: - if (tool is null) - throw new InvalidOperationException("DotMemory tool is not initialized"); - tool.Stop(); - tool = null; - break; - } - } + protected override string GetRunnerPath() + { + var consoleRunnerPackageField = typeof(DotMemory).GetField("ConsoleRunnerPackage", BindingFlags.NonPublic | BindingFlags.Static); + if (consoleRunnerPackageField == null) + throw new InvalidOperationException("Field 'ConsoleRunnerPackage' not found."); - public IEnumerable Exporters => Enumerable.Empty(); - public IEnumerable Analysers => Enumerable.Empty(); + object? consoleRunnerPackage = consoleRunnerPackageField.GetValue(null); + if (consoleRunnerPackage == null) + throw new InvalidOperationException("Unable to get value of 'ConsoleRunnerPackage'."); - public IEnumerable Validate(ValidationParameters validationParameters) - { - var runtimeMonikers = validationParameters.Benchmarks.Select(b => b.Job.Environment.GetRuntime().RuntimeMoniker).Distinct(); - foreach (var runtimeMoniker in runtimeMonikers) - { - if (!IsSupported(runtimeMoniker)) - yield return new ValidationError(true, $"Runtime '{runtimeMoniker}' is not supported by dotMemory"); - } - } + var consoleRunnerPackageType = consoleRunnerPackage.GetType(); + var getRunnerPathMethod = consoleRunnerPackageType.GetMethod("GetRunnerPath"); + if (getRunnerPathMethod == null) + throw new InvalidOperationException("Method 'GetRunnerPath' not found."); - internal static bool IsSupported(RuntimeMoniker runtimeMoniker) - { - switch (runtimeMoniker) - { - case RuntimeMoniker.HostProcess: - case RuntimeMoniker.Net461: - case RuntimeMoniker.Net462: - case RuntimeMoniker.Net47: - case RuntimeMoniker.Net471: - case RuntimeMoniker.Net472: - case RuntimeMoniker.Net48: - case RuntimeMoniker.Net481: - case RuntimeMoniker.Net50: - case RuntimeMoniker.Net60: - case RuntimeMoniker.Net70: - case RuntimeMoniker.Net80: - case RuntimeMoniker.Net90: - return true; - case RuntimeMoniker.NotRecognized: - case RuntimeMoniker.Mono: - case RuntimeMoniker.NativeAot60: - case RuntimeMoniker.NativeAot70: - case RuntimeMoniker.NativeAot80: - case RuntimeMoniker.NativeAot90: - case RuntimeMoniker.Wasm: - case RuntimeMoniker.WasmNet50: - case RuntimeMoniker.WasmNet60: - case RuntimeMoniker.WasmNet70: - case RuntimeMoniker.WasmNet80: - case RuntimeMoniker.WasmNet90: - case RuntimeMoniker.MonoAOTLLVM: - case RuntimeMoniker.MonoAOTLLVMNet60: - case RuntimeMoniker.MonoAOTLLVMNet70: - case RuntimeMoniker.MonoAOTLLVMNet80: - case RuntimeMoniker.MonoAOTLLVMNet90: - case RuntimeMoniker.Mono60: - case RuntimeMoniker.Mono70: - case RuntimeMoniker.Mono80: - case RuntimeMoniker.Mono90: -#pragma warning disable CS0618 // Type or member is obsolete - case RuntimeMoniker.NetCoreApp50: -#pragma warning restore CS0618 // Type or member is obsolete - return false; - case RuntimeMoniker.NetCoreApp20: - case RuntimeMoniker.NetCoreApp21: - case RuntimeMoniker.NetCoreApp22: - return OsDetector.IsWindows(); - case RuntimeMoniker.NetCoreApp30: - case RuntimeMoniker.NetCoreApp31: - return OsDetector.IsWindows() || OsDetector.IsLinux(); - default: - throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported"); - } - } + string? runnerPath = getRunnerPathMethod.Invoke(consoleRunnerPackage, null) as string; + if (runnerPath == null) + throw new InvalidOperationException("Unable to invoke 'GetRunnerPath'."); - public IEnumerable ProcessResults(DiagnoserResults results) => ImmutableArray.Empty; + return runnerPath; + } - public void DisplayResults(ILogger logger) + internal override bool IsSupported(RuntimeMoniker runtimeMoniker) + { + switch (runtimeMoniker) { - if (snapshotFilePaths.Any()) - { - logger.WriteLineInfo("The following dotMemory snapshots were generated:"); - foreach (string snapshotFilePath in snapshotFilePaths) - logger.WriteLineInfo($"* {snapshotFilePath}"); - } + case RuntimeMoniker.HostProcess: + case RuntimeMoniker.Net461: + case RuntimeMoniker.Net462: + case RuntimeMoniker.Net47: + case RuntimeMoniker.Net471: + case RuntimeMoniker.Net472: + case RuntimeMoniker.Net48: + case RuntimeMoniker.Net481: + case RuntimeMoniker.Net50: + case RuntimeMoniker.Net60: + case RuntimeMoniker.Net70: + case RuntimeMoniker.Net80: + case RuntimeMoniker.Net90: + return true; + case RuntimeMoniker.NotRecognized: + case RuntimeMoniker.Mono: + case RuntimeMoniker.NativeAot60: + case RuntimeMoniker.NativeAot70: + case RuntimeMoniker.NativeAot80: + case RuntimeMoniker.NativeAot90: + case RuntimeMoniker.Wasm: + case RuntimeMoniker.WasmNet50: + case RuntimeMoniker.WasmNet60: + case RuntimeMoniker.WasmNet70: + case RuntimeMoniker.WasmNet80: + case RuntimeMoniker.WasmNet90: + case RuntimeMoniker.MonoAOTLLVM: + case RuntimeMoniker.MonoAOTLLVMNet60: + case RuntimeMoniker.MonoAOTLLVMNet70: + case RuntimeMoniker.MonoAOTLLVMNet80: + case RuntimeMoniker.MonoAOTLLVMNet90: + case RuntimeMoniker.Mono60: + case RuntimeMoniker.Mono70: + case RuntimeMoniker.Mono80: + case RuntimeMoniker.Mono90: +#pragma warning disable CS0618 // Type or member is obsolete + case RuntimeMoniker.NetCoreApp50: +#pragma warning restore CS0618 // Type or member is obsolete + return false; + case RuntimeMoniker.NetCoreApp20: + case RuntimeMoniker.NetCoreApp21: + case RuntimeMoniker.NetCoreApp22: + return OsDetector.IsWindows(); + case RuntimeMoniker.NetCoreApp30: + case RuntimeMoniker.NetCoreApp31: + return OsDetector.IsWindows() || OsDetector.IsLinux(); + default: + throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported"); } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs index f9a3612471..c0fb55d4f1 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs @@ -1,22 +1,22 @@ using System; using BenchmarkDotNet.Configs; -namespace BenchmarkDotNet.Diagnostics.dotMemory +namespace BenchmarkDotNet.Diagnostics.dotMemory; + +[AttributeUsage(AttributeTargets.Class)] +public class DotMemoryDiagnoserAttribute : Attribute, IConfigSource { - [AttributeUsage(AttributeTargets.Class)] - public class DotMemoryDiagnoserAttribute : Attribute, IConfigSource - { - public IConfig Config { get; } + public IConfig Config { get; } - public DotMemoryDiagnoserAttribute() - { - Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser()); - } + public DotMemoryDiagnoserAttribute() + { + var diagnoser = new DotMemoryDiagnoser(); + Config = ManualConfig.CreateEmpty().AddDiagnoser(diagnoser); + } - public DotMemoryDiagnoserAttribute(string? nugetUrl = null, string? toolsDownloadFolder = null) - { - var nugetUri = nugetUrl == null ? null : new Uri(nugetUrl); - Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser(nugetUri, toolsDownloadFolder)); - } + public DotMemoryDiagnoserAttribute(Uri? nugetUrl, string? downloadTo = null) + { + var diagnoser = new DotMemoryDiagnoser(nugetUrl, downloadTo); + Config = ManualConfig.CreateEmpty().AddDiagnoser(diagnoser); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs deleted file mode 100644 index c28e08fdb2..0000000000 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Helpers; -using BenchmarkDotNet.Loggers; -using JetBrains.Profiler.SelfApi; - -namespace BenchmarkDotNet.Diagnostics.dotMemory -{ - internal sealed class DotMemoryTool - { - private readonly ILogger logger; - private readonly Uri? nugetUrl; - private readonly NuGetApi nugetApi; - private readonly string? downloadTo; - - public DotMemoryTool(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null) - { - this.logger = logger; - this.nugetUrl = nugetUrl; - this.nugetApi = nugetApi; - this.downloadTo = downloadTo; - } - - public void Init() - { - try - { - logger.WriteLineInfo("Ensuring that dotMemory prerequisite is installed..."); - var progress = new Progress(logger, "Installing DotMemory"); - DotMemory.InitAsync(progress, nugetUrl, nugetApi, downloadTo).Wait(); - logger.WriteLineInfo("dotMemory prerequisite is installed"); - logger.WriteLineInfo($"dotMemory runner path: {GetRunnerPath()}"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - } - } - - public string Start(DiagnoserActionParameters parameters) - { - string snapshotFile = ArtifactFileNameHelper.GetFilePath(parameters, "snapshots", DateTime.Now, "dmw", ".0000".Length); - string? snapshotDirectory = Path.GetDirectoryName(snapshotFile); - logger.WriteLineInfo($"Target snapshot file: {snapshotFile}"); - if (!Directory.Exists(snapshotDirectory) && snapshotDirectory != null) - { - try - { - Directory.CreateDirectory(snapshotDirectory); - } - catch (Exception e) - { - logger.WriteLineError($"Failed to create directory: {snapshotDirectory}"); - logger.WriteLineError(e.ToString()); - } - } - - try - { - logger.WriteLineInfo("Attaching dotMemory to the process..."); - Attach(parameters, snapshotFile); - logger.WriteLineInfo("dotMemory is successfully attached"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - return snapshotFile; - } - - return snapshotFile; - } - - public void Stop() - { - try - { - logger.WriteLineInfo("Taking dotMemory snapshot..."); - Snapshot(); - logger.WriteLineInfo("dotMemory snapshot is successfully taken"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - } - - try - { - logger.WriteLineInfo("Detaching dotMemory from the process..."); - Detach(); - logger.WriteLineInfo("dotMemory is successfully detached"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - } - } - - private void Attach(DiagnoserActionParameters parameters, string snapshotFile) - { - var config = new DotMemory.Config(); - - var pid = parameters.Process.Id; - var currentPid = Process.GetCurrentProcess().Id; - if (pid != currentPid) - config = config.ProfileExternalProcess(pid); - - config = config.SaveToFile(snapshotFile); - DotMemory.Attach(config); - } - - private void Snapshot() => DotMemory.GetSnapshot(); - - private void Detach() => DotMemory.Detach(); - - private string GetRunnerPath() - { - var consoleRunnerPackageField = typeof(DotMemory).GetField("ConsoleRunnerPackage", BindingFlags.NonPublic | BindingFlags.Static); - if (consoleRunnerPackageField == null) - throw new InvalidOperationException("Field 'ConsoleRunnerPackage' not found."); - - object? consoleRunnerPackage = consoleRunnerPackageField.GetValue(null); - if (consoleRunnerPackage == null) - throw new InvalidOperationException("Unable to get value of 'ConsoleRunnerPackage'."); - - var consoleRunnerPackageType = consoleRunnerPackage.GetType(); - var getRunnerPathMethod = consoleRunnerPackageType.GetMethod("GetRunnerPath"); - if (getRunnerPathMethod == null) - throw new InvalidOperationException("Method 'GetRunnerPath' not found."); - - string? runnerPath = getRunnerPathMethod.Invoke(consoleRunnerPackage, null) as string; - if (runnerPath == null) - throw new InvalidOperationException("Unable to invoke 'GetRunnerPath'."); - - return runnerPath; - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs deleted file mode 100644 index 738997bb6d..0000000000 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Diagnostics; -using BenchmarkDotNet.Loggers; - -namespace BenchmarkDotNet.Diagnostics.dotMemory -{ - public class Progress : IProgress - { - private static readonly TimeSpan ReportInterval = TimeSpan.FromSeconds(0.1); - - private readonly ILogger logger; - private readonly string title; - - public Progress(ILogger logger, string title) - { - this.logger = logger; - this.title = title; - } - - private int lastProgress; - private Stopwatch? stopwatch; - - public void Report(double value) - { - int progress = (int)Math.Floor(value); - bool needToReport = stopwatch == null || - (stopwatch != null && stopwatch?.Elapsed > ReportInterval) || - progress == 100; - - if (lastProgress != progress && needToReport) - { - logger.WriteLineInfo($"{title}: {progress}%"); - lastProgress = progress; - stopwatch = Stopwatch.StartNew(); - } - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs index da06c83472..be02cc30c8 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs @@ -1,142 +1,124 @@ using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using BenchmarkDotNet.Analysers; +using System.Reflection; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Reports; -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Toolchains; -using BenchmarkDotNet.Validators; -using RunMode = BenchmarkDotNet.Diagnosers.RunMode; +using JetBrains.Profiler.SelfApi; -namespace BenchmarkDotNet.Diagnostics.dotTrace +namespace BenchmarkDotNet.Diagnostics.dotTrace; + +public class DotTraceDiagnoser(Uri? nugetUrl = null, string? downloadTo = null) : SnapshotProfilerBase { - public class DotTraceDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null) : IProfiler + public override string ShortName => "dotTrace"; + + protected override void InitTool(Progress progress) { - public IEnumerable Ids => new[] { "DotTrace" }; - public string ShortName => "dotTrace"; + DotTrace.InitAsync(progress, nugetUrl, NuGetApi.V3, downloadTo).Wait(); + } - public RunMode GetRunMode(BenchmarkCase benchmarkCase) - { - return IsSupported(benchmarkCase.Job.Environment.GetRuntime().RuntimeMoniker) ? RunMode.ExtraRun : RunMode.None; - } + protected override void AttachToCurrentProcess(string snapshotFile) + { + DotTrace.Attach(new DotTrace.Config().SaveToFile(snapshotFile)); + DotTrace.StartCollectingData(); + } - private readonly List snapshotFilePaths = new (); + protected override void AttachToProcessByPid(int pid, string snapshotFile) + { + DotTrace.Attach(new DotTrace.Config().ProfileExternalProcess(pid).SaveToFile(snapshotFile)); + DotTrace.StartCollectingData(); + } - public void Handle(HostSignal signal, DiagnoserActionParameters parameters) - { - var job = parameters.BenchmarkCase.Job; - bool isInProcess = job.GetToolchain().IsInProcess; - var logger = parameters.Config.GetCompositeLogger(); - DotTraceToolBase tool = isInProcess - ? new InProcessDotTraceTool(logger, nugetUrl, downloadTo: toolsDownloadFolder) - : new ExternalDotTraceTool(logger, nugetUrl, downloadTo: toolsDownloadFolder); + protected override void TakeSnapshot() + { + DotTrace.StopCollectingData(); + DotTrace.SaveData(); + } - var runtimeMoniker = job.Environment.GetRuntime().RuntimeMoniker; - if (!IsSupported(runtimeMoniker)) - { - logger.WriteLineError($"Runtime '{runtimeMoniker}' is not supported by dotTrace"); - return; - } + protected override void Detach() + { + DotTrace.Detach(); + } - switch (signal) - { - case HostSignal.BeforeAnythingElse: - tool.Init(parameters); - break; - case HostSignal.BeforeActualRun: - snapshotFilePaths.Add(tool.Start(parameters)); - break; - case HostSignal.AfterActualRun: - tool.Stop(parameters); - break; - } - } + protected override string CreateSnapshotFilePath(DiagnoserActionParameters parameters) + { + return ArtifactFileNameHelper.GetFilePath(parameters, "snapshots", DateTime.Now, "dtp", ".0000".Length); + } - public IEnumerable Exporters => Enumerable.Empty(); - public IEnumerable Analysers => Enumerable.Empty(); + protected override string GetRunnerPath() + { + var consoleRunnerPackageField = typeof(DotTrace).GetField("ConsoleRunnerPackage", BindingFlags.NonPublic | BindingFlags.Static); + if (consoleRunnerPackageField == null) + throw new InvalidOperationException("Field 'ConsoleRunnerPackage' not found."); - public IEnumerable Validate(ValidationParameters validationParameters) - { - var runtimeMonikers = validationParameters.Benchmarks.Select(b => b.Job.Environment.GetRuntime().RuntimeMoniker).Distinct(); - foreach (var runtimeMoniker in runtimeMonikers) - { - if (!IsSupported(runtimeMoniker)) - yield return new ValidationError(true, $"Runtime '{runtimeMoniker}' is not supported by dotTrace"); - } - } + object? consoleRunnerPackage = consoleRunnerPackageField.GetValue(null); + if (consoleRunnerPackage == null) + throw new InvalidOperationException("Unable to get value of 'ConsoleRunnerPackage'."); - internal static bool IsSupported(RuntimeMoniker runtimeMoniker) - { - switch (runtimeMoniker) - { - case RuntimeMoniker.HostProcess: - case RuntimeMoniker.Net461: - case RuntimeMoniker.Net462: - case RuntimeMoniker.Net47: - case RuntimeMoniker.Net471: - case RuntimeMoniker.Net472: - case RuntimeMoniker.Net48: - case RuntimeMoniker.Net481: - case RuntimeMoniker.Net50: - case RuntimeMoniker.Net60: - case RuntimeMoniker.Net70: - case RuntimeMoniker.Net80: - case RuntimeMoniker.Net90: - return true; - case RuntimeMoniker.NotRecognized: - case RuntimeMoniker.Mono: - case RuntimeMoniker.NativeAot60: - case RuntimeMoniker.NativeAot70: - case RuntimeMoniker.NativeAot80: - case RuntimeMoniker.NativeAot90: - case RuntimeMoniker.Wasm: - case RuntimeMoniker.WasmNet50: - case RuntimeMoniker.WasmNet60: - case RuntimeMoniker.WasmNet70: - case RuntimeMoniker.WasmNet80: - case RuntimeMoniker.WasmNet90: - case RuntimeMoniker.MonoAOTLLVM: - case RuntimeMoniker.MonoAOTLLVMNet60: - case RuntimeMoniker.MonoAOTLLVMNet70: - case RuntimeMoniker.MonoAOTLLVMNet80: - case RuntimeMoniker.MonoAOTLLVMNet90: - case RuntimeMoniker.Mono60: - case RuntimeMoniker.Mono70: - case RuntimeMoniker.Mono80: - case RuntimeMoniker.Mono90: -#pragma warning disable CS0618 // Type or member is obsolete - case RuntimeMoniker.NetCoreApp50: -#pragma warning restore CS0618 // Type or member is obsolete - return false; - case RuntimeMoniker.NetCoreApp20: - case RuntimeMoniker.NetCoreApp21: - case RuntimeMoniker.NetCoreApp22: - return OsDetector.IsWindows(); - case RuntimeMoniker.NetCoreApp30: - case RuntimeMoniker.NetCoreApp31: - return OsDetector.IsWindows() || OsDetector.IsLinux(); - default: - throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported"); - } - } + var consoleRunnerPackageType = consoleRunnerPackage.GetType(); + var getRunnerPathMethod = consoleRunnerPackageType.GetMethod("GetRunnerPath"); + if (getRunnerPathMethod == null) + throw new InvalidOperationException("Method 'GetRunnerPath' not found."); - public IEnumerable ProcessResults(DiagnoserResults results) => ImmutableArray.Empty; + string? runnerPath = getRunnerPathMethod.Invoke(consoleRunnerPackage, null) as string; + if (runnerPath == null) + throw new InvalidOperationException("Unable to invoke 'GetRunnerPath'."); + + return runnerPath; + } - public void DisplayResults(ILogger logger) + internal override bool IsSupported(RuntimeMoniker runtimeMoniker) + { + switch (runtimeMoniker) { - if (snapshotFilePaths.Any()) - { - logger.WriteLineInfo("The following dotTrace snapshots were generated:"); - foreach (string snapshotFilePath in snapshotFilePaths) - logger.WriteLineInfo($"* {snapshotFilePath}"); - } + case RuntimeMoniker.HostProcess: + case RuntimeMoniker.Net461: + case RuntimeMoniker.Net462: + case RuntimeMoniker.Net47: + case RuntimeMoniker.Net471: + case RuntimeMoniker.Net472: + case RuntimeMoniker.Net48: + case RuntimeMoniker.Net481: + case RuntimeMoniker.Net50: + case RuntimeMoniker.Net60: + case RuntimeMoniker.Net70: + case RuntimeMoniker.Net80: + case RuntimeMoniker.Net90: + return true; + case RuntimeMoniker.NotRecognized: + case RuntimeMoniker.Mono: + case RuntimeMoniker.NativeAot60: + case RuntimeMoniker.NativeAot70: + case RuntimeMoniker.NativeAot80: + case RuntimeMoniker.NativeAot90: + case RuntimeMoniker.Wasm: + case RuntimeMoniker.WasmNet50: + case RuntimeMoniker.WasmNet60: + case RuntimeMoniker.WasmNet70: + case RuntimeMoniker.WasmNet80: + case RuntimeMoniker.WasmNet90: + case RuntimeMoniker.MonoAOTLLVM: + case RuntimeMoniker.MonoAOTLLVMNet60: + case RuntimeMoniker.MonoAOTLLVMNet70: + case RuntimeMoniker.MonoAOTLLVMNet80: + case RuntimeMoniker.MonoAOTLLVMNet90: + case RuntimeMoniker.Mono60: + case RuntimeMoniker.Mono70: + case RuntimeMoniker.Mono80: + case RuntimeMoniker.Mono90: +#pragma warning disable CS0618 // Type or member is obsolete + case RuntimeMoniker.NetCoreApp50: +#pragma warning restore CS0618 // Type or member is obsolete + return false; + case RuntimeMoniker.NetCoreApp20: + case RuntimeMoniker.NetCoreApp21: + case RuntimeMoniker.NetCoreApp22: + return OsDetector.IsWindows(); + case RuntimeMoniker.NetCoreApp30: + case RuntimeMoniker.NetCoreApp31: + return OsDetector.IsWindows() || OsDetector.IsLinux(); + default: + throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported"); } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs index 90d4173016..f056a98cbd 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs @@ -1,22 +1,22 @@ using System; using BenchmarkDotNet.Configs; -namespace BenchmarkDotNet.Diagnostics.dotTrace +namespace BenchmarkDotNet.Diagnostics.dotTrace; + +[AttributeUsage(AttributeTargets.Class)] +public class DotTraceDiagnoserAttribute : Attribute, IConfigSource { - [AttributeUsage(AttributeTargets.Class)] - public class DotTraceDiagnoserAttribute : Attribute, IConfigSource - { - public IConfig Config { get; } + public IConfig Config { get; } - public DotTraceDiagnoserAttribute() - { - Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser()); - } + public DotTraceDiagnoserAttribute() + { + var diagnoser = new DotTraceDiagnoser(); + Config = ManualConfig.CreateEmpty().AddDiagnoser(diagnoser); + } - public DotTraceDiagnoserAttribute(string? nugetUrl = null, string? toolsDownloadFolder = null) - { - var nugetUri = nugetUrl == null ? null : new Uri(nugetUrl); - Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser(nugetUri, toolsDownloadFolder)); - } + public DotTraceDiagnoserAttribute(Uri? nugetUrl, string? downloadTo = null) + { + var diagnoser = new DotTraceDiagnoser(nugetUrl, downloadTo); + Config = ManualConfig.CreateEmpty().AddDiagnoser(diagnoser); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs deleted file mode 100644 index 2fb36e9c66..0000000000 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Helpers; -using BenchmarkDotNet.Loggers; -using JetBrains.Profiler.SelfApi; - -namespace BenchmarkDotNet.Diagnostics.dotTrace -{ - internal abstract class DotTraceToolBase - { - private readonly ILogger logger; - private readonly Uri? nugetUrl; - private readonly NuGetApi nugetApi; - private readonly string? downloadTo; - - protected DotTraceToolBase(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null) - { - this.logger = logger; - this.nugetUrl = nugetUrl; - this.nugetApi = nugetApi; - this.downloadTo = downloadTo; - } - - public void Init(DiagnoserActionParameters parameters) - { - try - { - logger.WriteLineInfo("Ensuring that dotTrace prerequisite is installed..."); - var progress = new Progress(logger, "Installing DotTrace"); - DotTrace.InitAsync(progress, nugetUrl, nugetApi, downloadTo).Wait(); - logger.WriteLineInfo("dotTrace prerequisite is installed"); - logger.WriteLineInfo($"dotTrace runner path: {GetRunnerPath()}"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - } - } - - protected abstract bool AttachOnly { get; } - protected abstract void Attach(DiagnoserActionParameters parameters, string snapshotFile); - protected abstract void StartCollectingData(); - protected abstract void SaveData(); - protected abstract void Detach(); - - public string Start(DiagnoserActionParameters parameters) - { - string snapshotFile = ArtifactFileNameHelper.GetFilePath(parameters, "snapshots", DateTime.Now, "dtp", ".0000".Length); - string? snapshotDirectory = Path.GetDirectoryName(snapshotFile); - logger.WriteLineInfo($"Target snapshot file: {snapshotFile}"); - if (!Directory.Exists(snapshotDirectory) && snapshotDirectory != null) - { - try - { - Directory.CreateDirectory(snapshotDirectory); - } - catch (Exception e) - { - logger.WriteLineError($"Failed to create directory: {snapshotDirectory}"); - logger.WriteLineError(e.ToString()); - } - } - - try - { - logger.WriteLineInfo("Attaching dotTrace to the process..."); - Attach(parameters, snapshotFile); - logger.WriteLineInfo("dotTrace is successfully attached"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - return snapshotFile; - } - - if (!AttachOnly) - { - try - { - logger.WriteLineInfo("Start collecting data using dataTrace..."); - StartCollectingData(); - logger.WriteLineInfo("Data collecting is successfully started"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - } - } - - return snapshotFile; - } - - public void Stop(DiagnoserActionParameters parameters) - { - if (!AttachOnly) - { - try - { - logger.WriteLineInfo("Saving dotTrace snapshot..."); - SaveData(); - logger.WriteLineInfo("dotTrace snapshot is successfully saved to the artifact folder"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - } - - try - { - logger.WriteLineInfo("Detaching dotTrace from the process..."); - Detach(); - logger.WriteLineInfo("dotTrace is successfully detached"); - } - catch (Exception e) - { - logger.WriteLineError(e.ToString()); - } - } - } - - protected string GetRunnerPath() - { - var consoleRunnerPackageField = typeof(DotTrace).GetField("ConsoleRunnerPackage", BindingFlags.NonPublic | BindingFlags.Static); - if (consoleRunnerPackageField == null) - throw new InvalidOperationException("Field 'ConsoleRunnerPackage' not found."); - - object? consoleRunnerPackage = consoleRunnerPackageField.GetValue(null); - if (consoleRunnerPackage == null) - throw new InvalidOperationException("Unable to get value of 'ConsoleRunnerPackage'."); - - var consoleRunnerPackageType = consoleRunnerPackage.GetType(); - var getRunnerPathMethod = consoleRunnerPackageType.GetMethod("GetRunnerPath"); - if (getRunnerPathMethod == null) - throw new InvalidOperationException("Method 'GetRunnerPath' not found."); - - string? runnerPath = getRunnerPathMethod.Invoke(consoleRunnerPackage, null) as string; - if (runnerPath == null) - throw new InvalidOperationException("Unable to invoke 'GetRunnerPath'."); - - return runnerPath; - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs deleted file mode 100644 index c7f1cf18c8..0000000000 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Loggers; -using JetBrains.Profiler.SelfApi; -using ILogger = BenchmarkDotNet.Loggers.ILogger; - -namespace BenchmarkDotNet.Diagnostics.dotTrace -{ - internal class ExternalDotTraceTool : DotTraceToolBase - { - private static readonly TimeSpan AttachTimeout = TimeSpan.FromMinutes(5); - - public ExternalDotTraceTool(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null) : - base(logger, nugetUrl, nugetApi, downloadTo) { } - - protected override bool AttachOnly => true; - - protected override void Attach(DiagnoserActionParameters parameters, string snapshotFile) - { - var logger = parameters.Config.GetCompositeLogger(); - - string runnerPath = GetRunnerPath(); - int pid = parameters.Process.Id; - string arguments = $"attach {pid} --save-to=\"{snapshotFile}\" --service-output=on"; - - logger.WriteLineInfo($"Starting process: '{runnerPath} {arguments}'"); - - var processStartInfo = new ProcessStartInfo - { - FileName = runnerPath, - WorkingDirectory = "", - Arguments = arguments, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - var attachWaitingTask = new TaskCompletionSource(); - var process = new Process { StartInfo = processStartInfo }; - try - { - process.OutputDataReceived += (_, args) => - { - string? content = args.Data; - if (content != null) - { - logger.WriteLineInfo("[dotTrace] " + content); - if (content.Contains("##dotTrace[\"started\"")) - attachWaitingTask.TrySetResult(true); - } - }; - process.ErrorDataReceived += (_, args) => - { - string? content = args.Data; - if (content != null) - logger.WriteLineError("[dotTrace] " + args.Data); - }; - process.Exited += (_, _) => { attachWaitingTask.TrySetResult(false); }; - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - } - catch (Exception e) - { - attachWaitingTask.TrySetResult(false); - logger.WriteLineError(e.ToString()); - } - - if (!attachWaitingTask.Task.Wait(AttachTimeout)) - throw new Exception($"Failed to attach dotTrace to the target process (timeout: {AttachTimeout.TotalSeconds} sec)"); - if (!attachWaitingTask.Task.Result) - throw new Exception($"Failed to attach dotTrace to the target process (ExitCode={process.ExitCode})"); - } - - protected override void StartCollectingData() { } - - protected override void SaveData() { } - - protected override void Detach() { } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/InProcessDotTraceTool.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/InProcessDotTraceTool.cs deleted file mode 100644 index a02c9c1995..0000000000 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/InProcessDotTraceTool.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Loggers; -using JetBrains.Profiler.SelfApi; - -namespace BenchmarkDotNet.Diagnostics.dotTrace -{ - internal class InProcessDotTraceTool : DotTraceToolBase - { - public InProcessDotTraceTool(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null) : - base(logger, nugetUrl, nugetApi, downloadTo) { } - - protected override bool AttachOnly => false; - - protected override void Attach(DiagnoserActionParameters parameters, string snapshotFile) - { - var config = new DotTrace.Config(); - config.SaveToFile(snapshotFile); - DotTrace.Attach(config); - } - - protected override void StartCollectingData() => DotTrace.StartCollectingData(); - - protected override void SaveData() => DotTrace.SaveData(); - - protected override void Detach() => DotTrace.Detach(); - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/Progress.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/Progress.cs deleted file mode 100644 index c353939f1f..0000000000 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/Progress.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Diagnostics; -using BenchmarkDotNet.Loggers; - -namespace BenchmarkDotNet.Diagnostics.dotTrace -{ - public class Progress : IProgress - { - private static readonly TimeSpan ReportInterval = TimeSpan.FromSeconds(0.1); - - private readonly ILogger logger; - private readonly string title; - - public Progress(ILogger logger, string title) - { - this.logger = logger; - this.title = title; - } - - private int lastProgress; - private Stopwatch? stopwatch; - - public void Report(double value) - { - int progress = (int)Math.Floor(value); - bool needToReport = stopwatch == null || - (stopwatch != null && stopwatch?.Elapsed > ReportInterval) || - progress == 100; - - if (lastProgress != progress && needToReport) - { - logger.WriteLineInfo($"{title}: {progress}%"); - lastProgress = progress; - stopwatch = Stopwatch.StartNew(); - } - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs b/src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs new file mode 100644 index 0000000000..3105ceeeb9 --- /dev/null +++ b/src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using BenchmarkDotNet.Analysers; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Validators; + +namespace BenchmarkDotNet.Diagnosers; + +public abstract class SnapshotProfilerBase : IProfiler +{ + public abstract string ShortName { get; } + + protected abstract void InitTool(Progress progress); + protected abstract void AttachToCurrentProcess(string snapshotFile); + protected abstract void AttachToProcessByPid(int pid, string snapshotFile); + protected abstract void TakeSnapshot(); + protected abstract void Detach(); + + protected abstract string CreateSnapshotFilePath(DiagnoserActionParameters parameters); + protected abstract string GetRunnerPath(); + internal abstract bool IsSupported(RuntimeMoniker runtimeMoniker); + + private readonly List snapshotFilePaths = []; + + public IEnumerable Ids => [ShortName]; + public IEnumerable Exporters => []; + public IEnumerable Analysers => []; + + public RunMode GetRunMode(BenchmarkCase benchmarkCase) => + IsSupported(benchmarkCase.Job.Environment.GetRuntime().RuntimeMoniker) ? RunMode.ExtraRun : RunMode.None; + + public void Handle(HostSignal signal, DiagnoserActionParameters parameters) + { + var logger = parameters.Config.GetCompositeLogger(); + var job = parameters.BenchmarkCase.Job; + + var runtimeMoniker = job.Environment.GetRuntime().RuntimeMoniker; + if (!IsSupported(runtimeMoniker)) + { + logger.WriteLineError($"Runtime '{runtimeMoniker}' is not supported by dotMemory"); + return; + } + + switch (signal) + { + case HostSignal.BeforeAnythingElse: + Init(logger); + break; + case HostSignal.BeforeActualRun: + string snapshotFilePath = Start(logger, parameters); + snapshotFilePaths.Add(snapshotFilePath); + break; + case HostSignal.AfterActualRun: + Stop(logger); + break; + } + } + + public IEnumerable Validate(ValidationParameters validationParameters) + { + var runtimeMonikers = validationParameters.Benchmarks.Select(b => b.Job.Environment.GetRuntime().RuntimeMoniker).Distinct(); + foreach (var runtimeMoniker in runtimeMonikers) + if (!IsSupported(runtimeMoniker)) + yield return new ValidationError(true, $"Runtime '{runtimeMoniker}' is not supported by dotMemory"); + } + + public IEnumerable ProcessResults(DiagnoserResults results) => ImmutableArray.Empty; + + public void DisplayResults(ILogger logger) + { + if (snapshotFilePaths.Count != 0) + { + logger.WriteLineInfo($"The following {ShortName} snapshots were generated:"); + foreach (string snapshotFilePath in snapshotFilePaths) + logger.WriteLineInfo($"* {snapshotFilePath}"); + } + } + + private void Init(ILogger logger) + { + try + { + logger.WriteLineInfo($"Ensuring that {ShortName} prerequisite is installed..."); + var progress = new Progress(logger, $"Installing {ShortName}"); + InitTool(progress); + logger.WriteLineInfo($"{ShortName} prerequisite is installed"); + logger.WriteLineInfo($"{ShortName} runner path: {GetRunnerPath()}"); + } + catch (Exception e) + { + logger.WriteLineError(e.ToString()); + } + } + + private string Start(ILogger logger, DiagnoserActionParameters parameters) + { + string snapshotFilePath = CreateSnapshotFilePath(parameters); + string? snapshotDirectory = Path.GetDirectoryName(snapshotFilePath); + logger.WriteLineInfo($"Target snapshot file: {snapshotFilePath}"); + if (!Directory.Exists(snapshotDirectory) && snapshotDirectory != null) + { + try + { + Directory.CreateDirectory(snapshotDirectory); + } + catch (Exception e) + { + logger.WriteLineError($"Failed to create directory: {snapshotDirectory}"); + logger.WriteLineError(e.ToString()); + } + } + + try + { + logger.WriteLineInfo($"Attaching {ShortName} to the process..."); + Attach(parameters, snapshotFilePath); + logger.WriteLineInfo($"{ShortName} is successfully attached"); + } + catch (Exception e) + { + logger.WriteLineError(e.ToString()); + return snapshotFilePath; + } + + return snapshotFilePath; + } + + private void Stop(ILogger logger) + { + try + { + logger.WriteLineInfo($"Taking {ShortName} snapshot..."); + TakeSnapshot(); + logger.WriteLineInfo($"{ShortName} snapshot is successfully taken"); + } + catch (Exception e) + { + logger.WriteLineError(e.ToString()); + } + + try + { + logger.WriteLineInfo($"Detaching {ShortName} from the process..."); + Detach(); + logger.WriteLineInfo($"{ShortName} is successfully detached"); + } + catch (Exception e) + { + logger.WriteLineError(e.ToString()); + } + } + + + private void Attach(DiagnoserActionParameters parameters, string snapshotFile) + { + int pid = parameters.Process.Id; + int currentPid = Process.GetCurrentProcess().Id; + if (pid != currentPid) + AttachToProcessByPid(pid, snapshotFile); + else + AttachToCurrentProcess(snapshotFile); + } + + protected class Progress(ILogger logger, string title) : IProgress + { + private static readonly TimeSpan ReportInterval = TimeSpan.FromSeconds(0.1); + + private int lastProgress; + private Stopwatch? stopwatch; + + public void Report(double value) + { + int progress = (int)Math.Floor(value); + bool needToReport = stopwatch == null || + (stopwatch != null && stopwatch?.Elapsed > ReportInterval) || + progress == 100; + + if (lastProgress != progress && needToReport) + { + logger.WriteLineInfo($"{title}: {progress}%"); + lastProgress = progress; + stopwatch = Stopwatch.StartNew(); + } + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs b/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs index 7142486280..d3aec8dd96 100644 --- a/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs +++ b/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs @@ -3,15 +3,15 @@ using BenchmarkDotNet.Jobs; using Xunit; -namespace BenchmarkDotNet.Tests.dotMemory +namespace BenchmarkDotNet.Tests.dotMemory; + +public class DotMemoryTests { - public class DotMemoryTests + [Fact] + public void AllRuntimeMonikerAreKnown() { - [Fact] - public void AllRuntimeMonikerAreKnown() - { - foreach (RuntimeMoniker moniker in Enum.GetValues(typeof(RuntimeMoniker))) - DotMemoryDiagnoser.IsSupported(moniker); // Just check that it doesn't throw exceptions - } + var diagnoser = new DotMemoryDiagnoser(); + foreach (RuntimeMoniker moniker in Enum.GetValues(typeof(RuntimeMoniker))) + diagnoser.IsSupported(moniker); // Just check that it doesn't throw exceptions } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/dotTrace/DotTraceTests.cs b/tests/BenchmarkDotNet.Tests/dotTrace/DotTraceTests.cs index a71cae2aef..751412fd55 100644 --- a/tests/BenchmarkDotNet.Tests/dotTrace/DotTraceTests.cs +++ b/tests/BenchmarkDotNet.Tests/dotTrace/DotTraceTests.cs @@ -3,15 +3,15 @@ using BenchmarkDotNet.Jobs; using Xunit; -namespace BenchmarkDotNet.Tests.dotTrace +namespace BenchmarkDotNet.Tests.dotTrace; + +public class DotTraceTests { - public class DotTraceTests + [Fact] + public void AllRuntimeMonikerAreKnown() { - [Fact] - public void AllRuntimeMonikerAreKnown() - { - foreach (RuntimeMoniker moniker in Enum.GetValues(typeof(RuntimeMoniker))) - DotTraceDiagnoser.IsSupported(moniker); // Just check that it doesn't throw exceptions - } + var diagnoser = new DotTraceDiagnoser(); + foreach (RuntimeMoniker moniker in Enum.GetValues(typeof(RuntimeMoniker))) + diagnoser.IsSupported(moniker); // Just check that it doesn't throw exceptions } } \ No newline at end of file From 475ab4518c16e10687bc0ad502b981a6994bb674 Mon Sep 17 00:00:00 2001 From: Andrey Akinshin Date: Wed, 28 Aug 2024 09:56:35 +0200 Subject: [PATCH 33/57] Move Smoke benchmarks to BenchmarkDotNet.Samples IntroSmokeIncrements was taken from https://github.com/dotnet/BenchmarkDotNet/pull/2334#issuecomment-1988673372 --- .../IntroSmokeEmptyBasic.cs | 82 +++++++++++ .../IntroSmokeIncrements.cs | 138 ++++++++++++++++++ .../IntroSmokeValueTypes.cs | 70 +++++++++ .../Smoke/SmokeEmpty.cs | 85 ----------- .../Smoke/SmokeValueTypes.cs | 74 ---------- 5 files changed, 290 insertions(+), 159 deletions(-) create mode 100644 samples/BenchmarkDotNet.Samples/IntroSmokeEmptyBasic.cs create mode 100644 samples/BenchmarkDotNet.Samples/IntroSmokeIncrements.cs create mode 100644 samples/BenchmarkDotNet.Samples/IntroSmokeValueTypes.cs delete mode 100644 tests/BenchmarkDotNet.IntegrationTests.ManualRunning/Smoke/SmokeEmpty.cs delete mode 100644 tests/BenchmarkDotNet.IntegrationTests.ManualRunning/Smoke/SmokeValueTypes.cs diff --git a/samples/BenchmarkDotNet.Samples/IntroSmokeEmptyBasic.cs b/samples/BenchmarkDotNet.Samples/IntroSmokeEmptyBasic.cs new file mode 100644 index 0000000000..39783dc74a --- /dev/null +++ b/samples/BenchmarkDotNet.Samples/IntroSmokeEmptyBasic.cs @@ -0,0 +1,82 @@ +using BenchmarkDotNet.Attributes; + +namespace BenchmarkDotNet.Samples; + +[DisassemblyDiagnoser] +public class IntroSmokeEmptyBasic +{ + [Benchmark] public void Void1() {} + [Benchmark] public void Void2() {} + [Benchmark] public void Void3() {} + [Benchmark] public void Void4() {} + + [Benchmark] public byte Byte1() => 0; + [Benchmark] public byte Byte2() => 0; + [Benchmark] public byte Byte3() => 0; + [Benchmark] public byte Byte4() => 0; + + [Benchmark] public sbyte Sbyte1() => 0; + [Benchmark] public sbyte Sbyte2() => 0; + [Benchmark] public sbyte Sbyte3() => 0; + [Benchmark] public sbyte Sbyte4() => 0; + + [Benchmark] public short Short1() => 0; + [Benchmark] public short Short2() => 0; + [Benchmark] public short Short3() => 0; + [Benchmark] public short Short4() => 0; + + [Benchmark] public ushort Ushort1() => 0; + [Benchmark] public ushort Ushort2() => 0; + [Benchmark] public ushort Ushort3() => 0; + [Benchmark] public ushort Ushort4() => 0; + + [Benchmark] public int Int1() => 0; + [Benchmark] public int Int2() => 0; + [Benchmark] public int Int3() => 0; + [Benchmark] public int Int4() => 0; + + [Benchmark] public uint Uint1() => 0u; + [Benchmark] public uint Uint2() => 0u; + [Benchmark] public uint Uint3() => 0u; + [Benchmark] public uint Uint4() => 0u; + + [Benchmark] public bool Bool1() => false; + [Benchmark] public bool Bool2() => false; + [Benchmark] public bool Bool3() => false; + [Benchmark] public bool Bool4() => false; + + [Benchmark] public char Char1() => 'a'; + [Benchmark] public char Char2() => 'a'; + [Benchmark] public char Char3() => 'a'; + [Benchmark] public char Char4() => 'a'; + + [Benchmark] public float Float1() => 0f; + [Benchmark] public float Float2() => 0f; + [Benchmark] public float Float3() => 0f; + [Benchmark] public float Float4() => 0f; + + [Benchmark] public double Double1() => 0d; + [Benchmark] public double Double2() => 0d; + [Benchmark] public double Double3() => 0d; + [Benchmark] public double Double4() => 0d; + + [Benchmark] public long Long1() => 0L; + [Benchmark] public long Long2() => 0L; + [Benchmark] public long Long3() => 0L; + [Benchmark] public long Long4() => 0L; + + [Benchmark] public ulong Ulong1() => 0uL; + [Benchmark] public ulong Ulong2() => 0uL; + [Benchmark] public ulong Ulong3() => 0uL; + [Benchmark] public ulong Ulong4() => 0uL; + + [Benchmark] public string String1() => ""; + [Benchmark] public string String2() => ""; + [Benchmark] public string String3() => ""; + [Benchmark] public string String4() => ""; + + [Benchmark] public object? Object1() => null; + [Benchmark] public object? Object2() => null; + [Benchmark] public object? Object3() => null; + [Benchmark] public object? Object4() => null; +} \ No newline at end of file diff --git a/samples/BenchmarkDotNet.Samples/IntroSmokeIncrements.cs b/samples/BenchmarkDotNet.Samples/IntroSmokeIncrements.cs new file mode 100644 index 0000000000..6dfd15433d --- /dev/null +++ b/samples/BenchmarkDotNet.Samples/IntroSmokeIncrements.cs @@ -0,0 +1,138 @@ +using BenchmarkDotNet.Attributes; + +namespace BenchmarkDotNet.Samples; + +public class IntroSmokeIncrements +{ + public int Field; + + [Benchmark] + public void Increment01() + { + Field++; + } + + [Benchmark] + public void Increment02() + { + Field++; + Field++; + } + + [Benchmark] + public void Increment03() + { + Field++; + Field++; + Field++; + } + + [Benchmark] + public void Increment04() + { + Field++; + Field++; + Field++; + Field++; + } + + [Benchmark] + public void Increment05() + { + Field++; + Field++; + Field++; + Field++; + Field++; + } + + [Benchmark] + public void Increment06() + { + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + } + + [Benchmark] + public void Increment07() + { + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + } + + [Benchmark] + public void Increment08() + { + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + } + + [Benchmark] + public void Increment09() + { + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + } + + [Benchmark] + public void Increment10() + { + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + } + + [Benchmark] + public void Increment20() + { + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + Field++; + } +} \ No newline at end of file diff --git a/samples/BenchmarkDotNet.Samples/IntroSmokeValueTypes.cs b/samples/BenchmarkDotNet.Samples/IntroSmokeValueTypes.cs new file mode 100644 index 0000000000..66bce4d921 --- /dev/null +++ b/samples/BenchmarkDotNet.Samples/IntroSmokeValueTypes.cs @@ -0,0 +1,70 @@ +using System; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Environments; + +namespace BenchmarkDotNet.Samples; + +[MemoryDiagnoser, DisassemblyDiagnoser] +public class IntroSmokeValueTypes +{ + [Benchmark] public Jit ReturnEnum() => Jit.RyuJit; + + [Benchmark] public DateTime ReturnDateTime() => new DateTime(); + + [Benchmark] public DateTime? ReturnNullableDateTime() => new DateTime(); + [Benchmark] public int? ReturnNullableInt() => 0; + + public struct StructWithReferencesOnly { public object _ref; } + [Benchmark] public StructWithReferencesOnly ReturnStructWithReferencesOnly() => new StructWithReferencesOnly(); + + public struct EmptyStruct { } + [Benchmark] public EmptyStruct ReturnEmptyStruct() => new EmptyStruct(); + + [Benchmark] public ValueTuple ReturnGenericStructOfValueType() => new ValueTuple(0); + [Benchmark] public ValueTuple ReturnGenericStructOfReferenceType() => new ValueTuple(null); + + [Benchmark] public ValueTask ReturnValueTaskOfValueType() => new ValueTask(0); + [Benchmark] public ValueTask ReturnValueTaskOfReferenceType() => new ValueTask(result: null); + + [Benchmark] public byte ReturnByte() => 0; + public struct Byte1 { public byte _1; } + [Benchmark] public Byte1 ReturnByte1() => new Byte1(); + public struct Byte2 { public byte _1, _2; } + [Benchmark] public Byte2 ReturnByte2() => new Byte2(); + public struct Byte3 { public byte _1, _2, _3; } + [Benchmark] public Byte3 ReturnByte3() => new Byte3(); + public struct Byte4 { public byte _1, _2, _3, _4; } + [Benchmark] public Byte4 ReturnByte4() => new Byte4(); + + [Benchmark] public short ReturnShort() => 0; + public struct Short1 { public short _1; } + [Benchmark] public Short1 ReturnShort1() => new Short1(); + public struct Short2 { public short _1, _2; } + [Benchmark] public Short2 ReturnShort2() => new Short2(); + public struct Short3 { public short _1, _2, _3; } + [Benchmark] public Short3 ReturnShort3() => new Short3(); + public struct Short4 { public short _1, _2, _3, _4; } + [Benchmark] public Short4 ReturnShort4() => new Short4(); + + [Benchmark] public int ReturnInt() => 0; + public struct Int1 { public int _1; } + [Benchmark] public Int1 ReturnInt1() => new Int1(); + public struct Int2 { public int _1, _2; } + [Benchmark] public Int2 ReturnInt2() => new Int2(); + public struct Int3 { public int _1, _2, _3; } + [Benchmark] public Int3 ReturnInt3() => new Int3(); + public struct Int4 { public int _1, _2, _3, _4; } + [Benchmark] public Int4 ReturnInt4() => new Int4(); + + [Benchmark] public long ReturnLong() => 0; + public struct Long1 { public long _1; } + [Benchmark] public Long1 ReturnLong1() => new Long1(); + public struct Long2 { public long _1, _2; } + [Benchmark] public Long2 ReturnLong2() => new Long2(); + public struct Long3 { public long _1, _2, _3; } + [Benchmark] public Long3 ReturnLong3() => new Long3(); + public struct Long4 { public long _1, _2, _3, _4; } + [Benchmark] public Long4 ReturnLong4() => new Long4(); +} +// ReSharper restore InconsistentNaming \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/Smoke/SmokeEmpty.cs b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/Smoke/SmokeEmpty.cs deleted file mode 100644 index f8607e4263..0000000000 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/Smoke/SmokeEmpty.cs +++ /dev/null @@ -1,85 +0,0 @@ -using BenchmarkDotNet.Attributes; - -namespace BenchmarkDotNet.IntegrationTests.ManualRunning.Smoke -{ - [MedianColumn, Q3Column, MaxColumn] - [LegacyJitX64Job, RyuJitX64Job, MonoJob] - [KeepBenchmarkFiles] - public class SmokeEmpty - { - [Benchmark] public void Void1() {} - [Benchmark] public void Void2() {} - [Benchmark] public void Void3() {} - [Benchmark] public void Void4() {} - - [Benchmark] public byte Byte1() => 0; - [Benchmark] public byte Byte2() => 0; - [Benchmark] public byte Byte3() => 0; - [Benchmark] public byte Byte4() => 0; - - [Benchmark] public sbyte Sbyte1() => 0; - [Benchmark] public sbyte Sbyte2() => 0; - [Benchmark] public sbyte Sbyte3() => 0; - [Benchmark] public sbyte Sbyte4() => 0; - - [Benchmark] public short Short1() => 0; - [Benchmark] public short Short2() => 0; - [Benchmark] public short Short3() => 0; - [Benchmark] public short Short4() => 0; - - [Benchmark] public ushort Ushort1() => 0; - [Benchmark] public ushort Ushort2() => 0; - [Benchmark] public ushort Ushort3() => 0; - [Benchmark] public ushort Ushort4() => 0; - - [Benchmark] public int Int1() => 0; - [Benchmark] public int Int2() => 0; - [Benchmark] public int Int3() => 0; - [Benchmark] public int Int4() => 0; - - [Benchmark] public uint Uint1() => 0u; - [Benchmark] public uint Uint2() => 0u; - [Benchmark] public uint Uint3() => 0u; - [Benchmark] public uint Uint4() => 0u; - - [Benchmark] public bool Bool1() => false; - [Benchmark] public bool Bool2() => false; - [Benchmark] public bool Bool3() => false; - [Benchmark] public bool Bool4() => false; - - [Benchmark] public char Char1() => 'a'; - [Benchmark] public char Char2() => 'a'; - [Benchmark] public char Char3() => 'a'; - [Benchmark] public char Char4() => 'a'; - - [Benchmark] public float Float1() => 0f; - [Benchmark] public float Float2() => 0f; - [Benchmark] public float Float3() => 0f; - [Benchmark] public float Float4() => 0f; - - [Benchmark] public double Double1() => 0d; - [Benchmark] public double Double2() => 0d; - [Benchmark] public double Double3() => 0d; - [Benchmark] public double Double4() => 0d; - - [Benchmark] public long Long1() => 0L; - [Benchmark] public long Long2() => 0L; - [Benchmark] public long Long3() => 0L; - [Benchmark] public long Long4() => 0L; - - [Benchmark] public ulong Ulong1() => 0uL; - [Benchmark] public ulong Ulong2() => 0uL; - [Benchmark] public ulong Ulong3() => 0uL; - [Benchmark] public ulong Ulong4() => 0uL; - - [Benchmark] public string String1() => ""; - [Benchmark] public string String2() => ""; - [Benchmark] public string String3() => ""; - [Benchmark] public string String4() => ""; - - [Benchmark] public object Object1() => null; - [Benchmark] public object Object2() => null; - [Benchmark] public object Object3() => null; - [Benchmark] public object Object4() => null; - } -} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/Smoke/SmokeValueTypes.cs b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/Smoke/SmokeValueTypes.cs deleted file mode 100644 index 34b93a6d75..0000000000 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/Smoke/SmokeValueTypes.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Threading.Tasks; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Environments; - -namespace BenchmarkDotNet.IntegrationTests.ManualRunning.Smoke -{ - // ReSharper disable InconsistentNaming - - [RyuJitX64Job, LegacyJitX64Job, LegacyJitX86Job] - [MemoryDiagnoser] - public class SmokeValueTypes - { - [Benchmark] public Jit ReturnEnum() => Jit.RyuJit; - - [Benchmark] public DateTime ReturnDateTime() => new DateTime(); - - [Benchmark] public DateTime? ReturnNullableDateTime() => new DateTime(); - [Benchmark] public int? ReturnNullableInt() => 0; - - public struct StructWithReferencesOnly { public object _ref; } - [Benchmark] public StructWithReferencesOnly ReturnStructWithReferencesOnly() => new StructWithReferencesOnly(); - - public struct EmptyStruct { } - [Benchmark] public EmptyStruct ReturnEmptyStruct() => new EmptyStruct(); - - [Benchmark] public ValueTuple ReturnGenericStructOfValueType() => new ValueTuple(0); - [Benchmark] public ValueTuple ReturnGenericStructOfReferenceType() => new ValueTuple(null); - - [Benchmark] public ValueTask ReturnValueTaskOfValueType() => new ValueTask(0); - [Benchmark] public ValueTask ReturnValueTaskOfReferenceType() => new ValueTask(result: null); - - [Benchmark] public byte ReturnByte() => 0; - public struct Byte1 { public byte _1; } - [Benchmark] public Byte1 ReturnByte1() => new Byte1(); - public struct Byte2 { public byte _1, _2; } - [Benchmark] public Byte2 ReturnByte2() => new Byte2(); - public struct Byte3 { public byte _1, _2, _3; } - [Benchmark] public Byte3 ReturnByte3() => new Byte3(); - public struct Byte4 { public byte _1, _2, _3, _4; } - [Benchmark] public Byte4 ReturnByte4() => new Byte4(); - - [Benchmark] public short ReturnShort() => 0; - public struct Short1 { public short _1; } - [Benchmark] public Short1 ReturnShort1() => new Short1(); - public struct Short2 { public short _1, _2; } - [Benchmark] public Short2 ReturnShort2() => new Short2(); - public struct Short3 { public short _1, _2, _3; } - [Benchmark] public Short3 ReturnShort3() => new Short3(); - public struct Short4 { public short _1, _2, _3, _4; } - [Benchmark] public Short4 ReturnShort4() => new Short4(); - - [Benchmark] public int ReturnInt() => 0; - public struct Int1 { public int _1; } - [Benchmark] public Int1 ReturnInt1() => new Int1(); - public struct Int2 { public int _1, _2; } - [Benchmark] public Int2 ReturnInt2() => new Int2(); - public struct Int3 { public int _1, _2, _3; } - [Benchmark] public Int3 ReturnInt3() => new Int3(); - public struct Int4 { public int _1, _2, _3, _4; } - [Benchmark] public Int4 ReturnInt4() => new Int4(); - - [Benchmark] public long ReturnLong() => 0; - public struct Long1 { public long _1; } - [Benchmark] public Long1 ReturnLong1() => new Long1(); - public struct Long2 { public long _1, _2; } - [Benchmark] public Long2 ReturnLong2() => new Long2(); - public struct Long3 { public long _1, _2, _3; } - [Benchmark] public Long3 ReturnLong3() => new Long3(); - public struct Long4 { public long _1, _2, _3, _4; } - [Benchmark] public Long4 ReturnLong4() => new Long4(); - } - // ReSharper restore InconsistentNaming -} \ No newline at end of file From ca5dfdf106affb0922f5b8002a67272a27d3fd03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 28 Aug 2024 15:59:23 +0200 Subject: [PATCH 34/57] Clean up unsupported native AOT flags (#2616) * Clean up unsupported native AOT flags * `llcOptimizationPreference` was renamed to `OptimizationPreference` and became supported in .NET 8 (or maybe .NET 7, it doesn't matter) * `IlcGenerateStackTraceData` is now `StackTraceSupport` and supported. (Also got renamed in .NET 8 or earlier.) I'm leaving the old names of properties for backcompat. We could also delete them. I don't know how much BDN cares about .NET versions that are out of support. IlcGenerateCompleteTypeMetadata is unsupported, we don't test it, it's a mode for troubleshooting trimming issues for people who ignore trimming warnings. I don't think BDN should be setting this. You'll only find bugs (https://github.com/dotnet/runtime/issues/106439). So deleting that. Not deleting the API because it looks to be a public API. Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs index c9c964d7eb..b3adfc3e3c 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs @@ -133,11 +133,12 @@ private string GenerateProjectForNuGetBuild(BuildPartition buildPartition, Artif false true false - true + true {ilcOptimizationPreference} + {ilcOptimizationPreference} {GetTrimmingSettings()} - {ilcGenerateCompleteTypeMetadata} {ilcGenerateStackTraceData} + {ilcGenerateStackTraceData} false false false From bc3abf9e69f7c1c7a3a058d2945d5c2db86dd262 Mon Sep 17 00:00:00 2001 From: Tim Cassell <35501420+timcassell@users.noreply.github.com> Date: Tue, 17 Sep 2024 04:06:43 -0400 Subject: [PATCH 35/57] =?UTF-8?q?=EF=BB=BFUpdate=20clrmd=20to=203.1=20(#24?= =?UTF-8?q?88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BenchmarkDotNet/BenchmarkDotNet.csproj | 2 +- .../Disassemblers/Arm64Disassembler.cs | 2 +- ...Disassembler.cs => ClrMdV3Disassembler.cs} | 93 +++++-------------- .../Disassemblers/IntelDisassembler.cs | 2 +- .../SameArchitectureDisassembler.cs | 6 +- 5 files changed, 31 insertions(+), 74 deletions(-) rename src/BenchmarkDotNet/Disassemblers/{ClrMdV2Disassembler.cs => ClrMdV3Disassembler.cs} (77%) diff --git a/src/BenchmarkDotNet/BenchmarkDotNet.csproj b/src/BenchmarkDotNet/BenchmarkDotNet.csproj index 547c50b6e5..e4235ebd57 100644 --- a/src/BenchmarkDotNet/BenchmarkDotNet.csproj +++ b/src/BenchmarkDotNet/BenchmarkDotNet.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs b/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs index 16ebbde219..ce5176c4ca 100644 --- a/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs +++ b/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs @@ -139,7 +139,7 @@ public void Feed(Arm64Instruction instruction) public Arm64RegisterId RegisterId { get { return _registerId; } } } - internal class Arm64Disassembler : ClrMdV2Disassembler + internal class Arm64Disassembler : ClrMdV3Disassembler { internal sealed class RuntimeSpecificData { diff --git a/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs b/src/BenchmarkDotNet/Disassemblers/ClrMdV3Disassembler.cs similarity index 77% rename from src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs rename to src/BenchmarkDotNet/Disassemblers/ClrMdV3Disassembler.cs index aff7d276d6..6bd01426d8 100644 --- a/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs +++ b/src/BenchmarkDotNet/Disassemblers/ClrMdV3Disassembler.cs @@ -12,8 +12,9 @@ namespace BenchmarkDotNet.Disassemblers { - // This Disassembler uses ClrMd v2x. Please keep it in sync with ClrMdV1Disassembler (if possible). - internal abstract class ClrMdV2Disassembler + // This Disassembler uses ClrMd v3x. Please keep it in sync with ClrMdV1Disassembler (if possible). + internal abstract class ClrMdV3Disassembler + { private static readonly ulong MinValidAddress = GetMinValidAddress(); @@ -65,7 +66,7 @@ internal DisassemblyResult AttachAndDisassemble(Settings settings) state.Todo.Enqueue( new MethodInfo( // the Disassembler Entry Method is always parameterless, so check by name is enough - typeWithBenchmark.Methods.Single(method => method.IsPublic && method.Name == settings.MethodName), + typeWithBenchmark.Methods.Single(method => method.Attributes.HasFlag(System.Reflection.MethodAttributes.Public) && method.Name == settings.MethodName), 0)); } @@ -150,9 +151,10 @@ private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, if (!CanBeDisassembled(method)) { - if (method.IsPInvoke) + if (method.Attributes.HasFlag(System.Reflection.MethodAttributes.PinvokeImpl)) return CreateEmpty(method, "PInvoke method"); - if (method.IL is null || method.IL.Length == 0) + var ilInfo = method.GetILInfo(); + if (ilInfo is null || ilInfo.Length == 0) return CreateEmpty(method, "Extern method"); if (method.CompilationType == MethodCompilationType.None) return CreateEmpty(method, "Method was not JITted yet."); @@ -215,60 +217,30 @@ private IEnumerable Decode(ILToNativeMap map, State state, int depth, ClrMe private static ILToNativeMap[] GetCompleteNativeMap(ClrMethod method, ClrRuntime runtime) { - if (!TryReadNativeCodeAddresses(runtime, method, out ulong startAddress, out ulong endAddress)) + // it's better to use one single map rather than few small ones + // it's simply easier to get next instruction when decoding ;) + + var hotColdInfo = method.HotColdInfo; + if (hotColdInfo.HotSize > 0 && hotColdInfo.HotStart > 0) { - startAddress = method.NativeCode; - endAddress = ulong.MaxValue; + return hotColdInfo.ColdSize <= 0 + ? new[] { new ILToNativeMap() { StartAddress = hotColdInfo.HotStart, EndAddress = hotColdInfo.HotStart + hotColdInfo.HotSize, ILOffset = -1 } } + : new[] + { + new ILToNativeMap() { StartAddress = hotColdInfo.HotStart, EndAddress = hotColdInfo.HotStart + hotColdInfo.HotSize, ILOffset = -1 }, + new ILToNativeMap() { StartAddress = hotColdInfo.ColdStart, EndAddress = hotColdInfo.ColdStart + hotColdInfo.ColdSize, ILOffset = -1 } + }; } - ILToNativeMap[] sortedMaps = method.ILOffsetMap // CanBeDisassembled ensures that there is at least one map in ILOffsetMap - .Where(map => map.StartAddress >= startAddress && map.StartAddress < endAddress) // can be false for Tier 0 maps, EndAddress is not checked on purpose here - .Where(map => map.StartAddress < map.EndAddress) // some maps have 0 length (they don't have corresponding assembly code?) + return method.ILOffsetMap + .Where(map => map.StartAddress < map.EndAddress) // some maps have 0 length? .OrderBy(map => map.StartAddress) // we need to print in the machine code order, not IL! #536 - .Select(map => new ILToNativeMap() - { - StartAddress = map.StartAddress, - // some maps have EndAddress > codeHeaderData.MethodStart + codeHeaderData.MethodSize and contain garbage (#2074). They need to be fixed! - EndAddress = Math.Min(map.EndAddress, endAddress), - ILOffset = map.ILOffset - }) .ToArray(); - - if (sortedMaps.Length == 0) - { - // In such situation ILOffsetMap most likely describes Tier 0, while CodeHeaderData Tier 1. - // Since we care about Tier 1 (if it's present), we "fake" a Tier 1 map. - return new[] { new ILToNativeMap() { StartAddress = startAddress, EndAddress = endAddress } }; - } - else if (sortedMaps[0].StartAddress != startAddress || (sortedMaps[sortedMaps.Length - 1].EndAddress != endAddress && endAddress != ulong.MaxValue)) - { - // In such situation ILOffsetMap most likely is missing few bytes. We just "extend" it to avoid producing "bad" instructions. - return new[] { new ILToNativeMap() { StartAddress = startAddress, EndAddress = endAddress } }; - } - - return sortedMaps; } private static DisassembledMethod CreateEmpty(ClrMethod method, string reason) => DisassembledMethod.Empty(method.Signature, method.NativeCode, reason); - protected static bool TryReadNativeCodeAddresses(ClrRuntime runtime, ClrMethod method, out ulong startAddress, out ulong endAddress) - { - if (method is not null - && runtime.DacLibrary.SOSDacInterface.GetCodeHeaderData(method.NativeCode, out var codeHeaderData) == HResult.S_OK - && codeHeaderData.MethodSize > 0) // false for extern methods! - { - // HotSize can be missing or be invalid (https://github.com/microsoft/clrmd/issues/1036). - // So we fetch the method size on our own. - startAddress = codeHeaderData.MethodStart; - endAddress = codeHeaderData.MethodStart + codeHeaderData.MethodSize; - return true; - } - - startAddress = endAddress = 0; - return false; - } - protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, State state, int depth, ClrMethod currentMethod) { if (!IsValidAddress(address) || state.AddressToNameMapping.ContainsKey(address)) @@ -284,18 +256,10 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, } var method = runtime.GetMethodByInstructionPointer(address); - if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0) - { - if (runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress)) - { - method = runtime.GetMethodByInstructionPointer(newAddress); - - method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, newAddress); - } - } - else + if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0 + && runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress)) { - method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, address); + method = runtime.GetMethodByInstructionPointer(newAddress); } if (method is null) @@ -314,7 +278,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, return; } - var methodTableName = runtime.DacLibrary.SOSDacInterface.GetMethodTableName(address); + var methodTableName = runtime.GetTypeByMethodTable(address)?.Name; if (!string.IsNullOrEmpty(methodTableName)) { state.AddressToNameMapping.Add(address, $"MT_{methodTableName}"); @@ -350,13 +314,6 @@ protected void FlushCachedDataIfNeeded(IDataReader dataTargetDataReader, ulong a } } - // GetMethodByInstructionPointer sometimes returns wrong methods. - // In case given address does not belong to the methods range, null is returned. - private static ClrMethod WorkaroundGetMethodByInstructionPointerBug(ClrRuntime runtime, ClrMethod method, ulong newAddress) - => TryReadNativeCodeAddresses(runtime, method, out ulong startAddress, out ulong endAddress) && !(startAddress >= newAddress && newAddress <= endAddress) - ? null - : method; - private class SharpComparer : IEqualityComparer { public bool Equals(Sharp x, Sharp y) diff --git a/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs b/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs index f29faae037..3fd541528a 100644 --- a/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs +++ b/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs @@ -7,7 +7,7 @@ namespace BenchmarkDotNet.Disassemblers { - internal class IntelDisassembler : ClrMdV2Disassembler + internal class IntelDisassembler : ClrMdV3Disassembler { internal sealed class RuntimeSpecificData { diff --git a/src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs b/src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs index cf810cdd86..8ce026a5dd 100644 --- a/src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs +++ b/src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs @@ -8,16 +8,16 @@ namespace BenchmarkDotNet.Disassemblers internal class SameArchitectureDisassembler { private readonly DisassemblyDiagnoserConfig config; - private ClrMdV2Disassembler? clrMdV2Disassembler; + private ClrMdV3Disassembler? clrMdV3Disassembler; internal SameArchitectureDisassembler(DisassemblyDiagnoserConfig config) => this.config = config; internal DisassemblyResult Disassemble(DiagnoserActionParameters parameters) // delay the creation to avoid exceptions at DisassemblyDiagnoser ctor - => (clrMdV2Disassembler ??= CreateDisassemblerForCurrentArchitecture()) + => (clrMdV3Disassembler ??= CreateDisassemblerForCurrentArchitecture()) .AttachAndDisassemble(BuildDisassemblerSettings(parameters)); - private static ClrMdV2Disassembler CreateDisassemblerForCurrentArchitecture() + private static ClrMdV3Disassembler CreateDisassemblerForCurrentArchitecture() => RuntimeInformation.GetCurrentPlatform() switch { Platform.X86 or Platform.X64 => new IntelDisassembler(), From 3a2d115ace6dc7a68a3c00a001243e098e3e4229 Mon Sep 17 00:00:00 2001 From: Tim Cassell <35501420+timcassell@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:05:13 -0400 Subject: [PATCH 36/57] =?UTF-8?q?=EF=BB=BFRemoved=20support=20for=20netcor?= =?UTF-8?q?eapp3.0=20and=20older=20runtimes.=20(#2505)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++--- docs/articles/configs/toolchains.md | 24 +++++++++--------- docs/articles/faq.md | 8 +++--- docs/articles/guides/console-args.md | 25 ++++++++++--------- docs/articles/guides/troubleshooting.md | 6 ++--- docs/articles/overview.md | 2 +- .../BenchmarkDotNet.Samples/IntroEnvVars.cs | 4 +-- .../IntroFluentConfigBuilder.cs | 2 +- .../Jobs/RuntimeMoniker.cs | 13 +++++++--- .../DotMemoryDiagnoser.cs | 5 ---- .../DotTraceDiagnoser.cs | 5 ---- src/BenchmarkDotNet/BenchmarkDotNet.csproj | 6 ++--- .../ConsoleArguments/CommandLineOptions.cs | 8 +++--- .../ConsoleArguments/ConfigParser.cs | 4 --- .../Diagnosers/EventPipeProfiler.cs | 4 +-- .../Diagnosers/ThreadingDiagnoser.cs | 4 +-- .../Disassemblers/DisassemblyDiagnoser.cs | 5 ---- src/BenchmarkDotNet/Engines/GcStats.cs | 2 +- .../Environments/Runtimes/CoreRuntime.cs | 12 +++++---- .../Environments/Runtimes/Runtime.cs | 2 +- .../Extensions/RuntimeMonikerExtensions.cs | 8 ------ .../Helpers/ArtifactFileNameHelper.cs | 2 +- .../Toolchains/CoreRun/CoreRunToolchain.cs | 4 +-- .../Toolchains/CsProj/CsProjCoreToolchain.cs | 4 +++ .../CustomDotNetCliToolchainBuilder.cs | 2 +- .../DotNetCli/NetCoreAppSettings.cs | 9 +++++-- .../Toolchains/ToolchainExtensions.cs | 12 --------- .../Validators/DotNetSdkVersionValidator.cs | 4 --- ...ts.ManualRunning.MultipleFrameworks.csproj | 21 ++++++---------- .../MultipleFrameworksTest.cs | 17 ++++++------- .../LocalNativeAotToolchainTests.cs | 4 +-- .../MemoryDiagnoserTests.cs | 3 +-- .../ConfigParserTests.cs | 22 ++++++++-------- .../Configs/ImmutableConfigTests.cs | 8 +++--- .../BenchmarkDotNet.Tests/Configs/JobTests.cs | 2 +- .../CsProjGeneratorTests.cs | 20 +++++++-------- .../Reports/SummaryTests.cs | 2 +- .../Running/BenchmarkConverterTests.cs | 2 +- .../RuntimeVersionDetectionTests.cs | 23 +++++------------ .../XUnit/EnvRequirement.cs | 3 +-- .../XUnit/EnvRequirementChecker.cs | 1 - .../dotMemory/DotMemoryTests.cs | 9 ++++++- .../dotTrace/DotTraceTests.cs | 9 ++++++- tests/runCoreTests.cmd | 12 ++++----- tests/runCoreTests.sh | 4 +-- 45 files changed, 159 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index 3022e0ebb3..96feca8f9e 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ The measured data can be exported to different formats (md, html, csv, xml, json ![](https://raw.githubusercontent.com/dotnet/BenchmarkDotNet/ec962b0bd6854c991d7a3ebd77037579165acb36/docs/images/v0.12.0/rplot.png) -*Supported runtimes:* .NET 5+, .NET Framework 4.6.1+, .NET Core 2.0+, Mono, NativeAOT +*Supported runtimes:* .NET 5+, .NET Framework 4.6.1+, .NET Core 3.1+, Mono, NativeAOT *Supported languages:* C#, F#, Visual Basic *Supported OS:* Windows, Linux, macOS *Supported architectures:* x86, x64, ARM, ARM64, Wasm and LoongArch64 @@ -135,8 +135,8 @@ If you want to compare benchmarks with each other, mark one of the benchmarks as the [baseline](https://benchmarkdotnet.org/articles/features/baselines.html) via `[Benchmark(Baseline = true)]`: BenchmarkDotNet will compare it with all of the other benchmarks. If you want to compare performance in different environments, use [jobs](https://benchmarkdotnet.org/articles/configs/jobs.html). -For example, you can run all the benchmarks on .NET Core 3.0 and Mono via - `[SimpleJob(RuntimeMoniker.NetCoreApp30)]` and `[SimpleJob(RuntimeMoniker.Mono)]`. +For example, you can run all the benchmarks on .NET Core 3.1 and Mono via + `[SimpleJob(RuntimeMoniker.NetCoreApp31)]` and `[SimpleJob(RuntimeMoniker.Mono)]`. If you don't like attributes, you can call most of the APIs via the fluent style and write code like this: diff --git a/docs/articles/configs/toolchains.md b/docs/articles/configs/toolchains.md index 0bae3ffdff..538891d601 100644 --- a/docs/articles/configs/toolchains.md +++ b/docs/articles/configs/toolchains.md @@ -18,14 +18,14 @@ When you run your benchmarks without specifying the toolchain in an explicit way If you want to test multiple frameworks, your project file **MUST target all of them** and you **MUST install the corresponding SDKs**: ```xml -netcoreapp3.0;netcoreapp2.1;net48 +netcoreapp3.1;net8.0;net48 ``` If you run your benchmarks without specifying any custom settings, BenchmarkDotNet is going to run the benchmarks **using the same framework as the host process**: ```cmd -dotnet run -c Release -f netcoreapp2.1 # is going to run the benchmarks using .NET Core 2.1 -dotnet run -c Release -f netcoreapp3.0 # is going to run the benchmarks using .NET Core 3.0 +dotnet run -c Release -f netcoreapp3.1 # is going to run the benchmarks using .NET Core 3.1 +dotnet run -c Release -f net8.0 # is going to run the benchmarks using .NET 8.0 dotnet run -c Release -f net48 # is going to run the benchmarks using .NET 4.8 mono $pathToExe # is going to run the benchmarks using Mono from your PATH ``` @@ -33,8 +33,8 @@ mono $pathToExe # is going to run the benchmarks using Mo To run the benchmarks for multiple runtimes with a single command, you need to specify the target framework moniker names via `--runtimes|-r` console argument: ```cmd -dotnet run -c Release -f netcoreapp2.1 --runtimes netcoreapp2.1 netcoreapp3.0 # is going to run the benchmarks using .NET Core 2.1 and .NET Core 3.0 -dotnet run -c Release -f netcoreapp2.1 --runtimes netcoreapp2.1 net48 # is going to run the benchmarks using .NET Core 2.1 and .NET 4.8 +dotnet run -c Release -f net8.0 --runtimes net8.0 netcoreapp3.1 # is going to run the benchmarks using .NET 8.0 and .NET Core 3.1 +dotnet run -c Release -f net8.0 --runtimes net8.0 net48 # is going to run the benchmarks using .NET 8.0 and .NET 4.8 ``` What is going to happen if you provide multiple Full .NET Framework monikers? Let's say: @@ -67,8 +67,8 @@ namespace BenchmarkDotNet.Samples { [SimpleJob(RuntimeMoniker.Net48)] [SimpleJob(RuntimeMoniker.Mono)] - [SimpleJob(RuntimeMoniker.NetCoreApp21)] - [SimpleJob(RuntimeMoniker.NetCoreApp30)] + [SimpleJob(RuntimeMoniker.NetCoreApp31)] + [SimpleJob(RuntimeMoniker.Net80)] public class TheClassWithBenchmarks ``` @@ -115,9 +115,9 @@ public class MyConfig : ManualConfig Add(Job.Default.With( CsProjCoreToolchain.From( new NetCoreAppSettings( - targetFrameworkMoniker: "netcoreapp2.1", - runtimeFrameworkVersion: "2.1.0-preview2-25628-01", - name: ".NET Core 2.1")))); + targetFrameworkMoniker: "net8.0-windows", + runtimeFrameworkVersion: "8.0.101", + name: ".NET 8.0 Windows")))); } } ``` @@ -146,11 +146,11 @@ public class CustomPathsConfig : ManualConfig public CustomPathsConfig() { var dotnetCli32bit = NetCoreAppSettings - .NetCoreApp20 + .NetCoreApp31 .WithCustomDotNetCliPath(@"C:\Program Files (x86)\dotnet\dotnet.exe", "32 bit cli"); var dotnetCli64bit = NetCoreAppSettings - .NetCoreApp20 + .NetCoreApp31 .WithCustomDotNetCliPath(@"C:\Program Files\dotnet\dotnet.exe", "64 bit cli"); AddJob(Job.RyuJitX86.WithToolchain(CsProjCoreToolchain.From(dotnetCli32bit)).WithId("32 bit cli")); diff --git a/docs/articles/faq.md b/docs/articles/faq.md index 1fcfb13b1a..8f1cd41dc3 100644 --- a/docs/articles/faq.md +++ b/docs/articles/faq.md @@ -13,9 +13,9 @@ See also: [BenchmarkDotNet#237](https://github.com/dotnet/BenchmarkDotNet/issues * **Q** Why can't I install BenchmarkDotNet in a new .NET Core Console App in Visual Studio 2017? - **A** BenchmarkDotNet supports only netcoreapp2.0+. + **A** BenchmarkDotNet supports only netcoreapp3.1+. Some old Visual Studio 2017 can create a new application which targets netcoreapp1.0. -You should upgrade it up to 2.0. +You should upgrade it up to 3.1. If you want to target netcoreapp1.0 in your main assembly, it's recommended to create a separated project for benchmarks. * **Q** I created a new .NET Core Console App in Visual Studio 2017. Now I want to run my code on CoreCLR, full .NET Framework, and Mono. How can I do it? @@ -23,7 +23,7 @@ If you want to target netcoreapp1.0 in your main assembly, it's recommended to c **A** Use the following lines in your `.csproj` file: ```xml - netcoreapp2.0;net46 + netcoreapp3.1;net46 AnyCPU ``` @@ -33,7 +33,7 @@ If you want to target netcoreapp1.0 in your main assembly, it's recommended to c [CoreJob, ClrJob, MonoJob] ``` -* **Q** My source code targets old versions of .NET Framework or .NET Core, but BenchmarkDotNet requires `net461` and `netcoreapp2.0`. How can I run benchmarks in this case? +* **Q** My source code targets old versions of .NET Framework or .NET Core, but BenchmarkDotNet requires `net461` and `netcoreapp3.1`. How can I run benchmarks in this case? **A** It's a good practice to introduce an additional console application (e.g. `MyAwesomeLibrary.Benchmarks`) which will depend on your code and BenchmarkDotNet. Due to the fact that users usually run benchmarks in a develop environment and don't distribute benchmarks for users, it shouldn't be a problem. diff --git a/docs/articles/guides/console-args.md b/docs/articles/guides/console-args.md index 6fc5ddb245..032cc81da6 100644 --- a/docs/articles/guides/console-args.md +++ b/docs/articles/guides/console-args.md @@ -117,28 +117,29 @@ You can also filter the benchmarks by categories: The `--runtimes` or just `-r` allows you to run the benchmarks for selected Runtimes. Available options are: * Clr - BDN will either use Roslyn (if you run it as .NET app) or latest installed .NET SDK to build the benchmarks (if you run it as .NET Core app). -* Core - if you run it as .NET Core app, BDN will use the same target framework moniker, if you run it as .NET app it's going to use netcoreapp2.1. +* Core - if you run it as .NET Core app, BDN will use the same target framework moniker, if you run it as .NET app it's going to use net8.0. * Mono - it's going to use the Mono from `$Path`, you can override it with `--monoPath`. -* net46, net461, net462, net47, net471, net472 - to build and run benchmarks against specific .NET framework version. -* netcoreapp2.0, netcoreapp2.1, netcoreapp2.2, netcoreapp3.0, netcoreapp3.1, net5.0, net6.0, net7.0 - to build and run benchmarks against specific .NET Core version. -* nativeaot5.0, nativeaot6.0, nativeaot7.0 - to build and run benchmarks using NativeAOT. Can be customized with additional options: `--ilcPath`, `--ilCompilerVersion`. +* net46, net461, net462, net47, net471, net472, net48, net481 - to build and run benchmarks against specific .NET Framework version. +* netcoreapp3.1, net5.0, net6.0, net7.0, net8.0 - to build and run benchmarks against specific .NET (Core) version. +* nativeaot5.0, nativeaot6.0, nativeaot7.0, nativeaot8.0 - to build and run benchmarks using NativeAOT. Can be customized with additional options: `--ilcPath`, `--ilCompilerVersion`. +* mono6.0, mono7.0, mono8.0 - to build and run benchmarks with .Net 6+ using MonoVM. -Example: run the benchmarks for .NET 4.7.2 and .NET Core 2.1: +Example: run the benchmarks for .NET 4.7.2 and .NET 8.0: ```log -dotnet run -c Release -- --runtimes net472 netcoreapp2.1 +dotnet run -c Release -- --runtimes net472 net8.0 ``` -Example: run the benchmarks for .NET Core 3.0 and latest .NET SDK installed on your PC: +Example: run the benchmarks for .NET Core 3.1 and latest .NET SDK installed on your PC: ```log -dotnet run -c Release -f netcoreapp3.0 -- --runtimes clr core +dotnet run -c Release -f netcoreapp3.1 -- --runtimes clr core ``` -But same command executed with `-f netcoreapp2.0` is going to run the benchmarks for .NET Core 2.0: +But same command executed with `-f net6.0` is going to run the benchmarks for .NET 6.0: ```log -dotnet run -c Release -f netcoreapp2.0 -- --runtimes clr core +dotnet run -c Release -f net6.0 -- --runtimes clr core ``` ## Number of invocations and iterations @@ -207,10 +208,10 @@ To perform a Mann–Whitney U Test and display the results in a dedicated column * `--statisticalTest`- Threshold for Mann–Whitney U Test. Examples: 5%, 10ms, 100ns, 1s -Example: run Mann–Whitney U test with relative ratio of 5% for all benchmarks for .NET Core 2.0 (base) vs .NET Core 2.1 (diff). .NET Core 2.0 will be baseline because it was first. +Example: run Mann–Whitney U test with relative ratio of 5% for all benchmarks for .NET 6.0 (base) vs .NET 8.0 (diff). .NET 6.0 will be baseline because it was first. ```log -dotnet run -c Release -- --filter * --runtimes netcoreapp2.0 netcoreapp2.1 --statisticalTest 5% +dotnet run -c Release -- --filter * --runtimes net6.0 net8.0 --statisticalTest 5% ``` ## More diff --git a/docs/articles/guides/troubleshooting.md b/docs/articles/guides/troubleshooting.md index 1a998886cc..2574f3d1aa 100644 --- a/docs/articles/guides/troubleshooting.md +++ b/docs/articles/guides/troubleshooting.md @@ -11,7 +11,7 @@ How do you know that BenchmarkDotNet has failed to build the project? BDN is goi // ***** BenchmarkRunner: Start ***** // ***** Found 1 benchmark(s) in total ***** // ***** Building 1 exe(s) in Parallel: Start ***** -// start dotnet restore /p:UseSharedCompilation=false /p:BuildInParallel=false /m:1 /p:Deterministic=true /p:Optimize=true in C:\Projects\BenchmarkDotNet\samples\BenchmarkDotNet.Samples\bin\Release\netcoreapp2.1\c6045772-d3c7-4dbe-ab37-4aca6dcb6ec4 +// start dotnet restore /p:UseSharedCompilation=false /p:BuildInParallel=false /m:1 /p:Deterministic=true /p:Optimize=true in C:\Projects\BenchmarkDotNet\samples\BenchmarkDotNet.Samples\bin\Release\net8.0\c6045772-d3c7-4dbe-ab37-4aca6dcb6ec4 // command took 0.51s and exited with 1 // ***** Done, took 00:00:00 (0.66 sec) ***** // Found 1 benchmarks: @@ -20,10 +20,10 @@ How do you know that BenchmarkDotNet has failed to build the project? BDN is goi // Build Error: Standard output: Standard error: - C:\Projects\BenchmarkDotNet\samples\BenchmarkDotNet.Samples\bin\Release\netcoreapp2.1\c6045772-d3c7-4dbe-ab37-4aca6dcb6ec4\BenchmarkDotNet.Autogenerated.csproj(36,1): error MSB4025: The project file could not be loaded. Unexpected end of file while parsing Comment has occurred. Line 36, position 1. + C:\Projects\BenchmarkDotNet\samples\BenchmarkDotNet.Samples\bin\Release\net8.0\c6045772-d3c7-4dbe-ab37-4aca6dcb6ec4\BenchmarkDotNet.Autogenerated.csproj(36,1): error MSB4025: The project file could not be loaded. Unexpected end of file while parsing Comment has occurred. Line 36, position 1. // BenchmarkDotNet has failed to build the auto-generated boilerplate code. -// It can be found in C:\Projects\BenchmarkDotNet\samples\BenchmarkDotNet.Samples\bin\Release\netcoreapp2.1\c6045772-d3c7-4dbe-ab37-4aca6dcb6ec4 +// It can be found in C:\Projects\BenchmarkDotNet\samples\BenchmarkDotNet.Samples\bin\Release\net8.0\c6045772-d3c7-4dbe-ab37-4aca6dcb6ec4 // Please follow the troubleshooting guide: https://benchmarkdotnet.org/articles/guides/troubleshooting.html ``` diff --git a/docs/articles/overview.md b/docs/articles/overview.md index e777049ee1..f6f502d004 100644 --- a/docs/articles/overview.md +++ b/docs/articles/overview.md @@ -10,7 +10,7 @@ name: Overview Create new console application and install the [BenchmarkDotNet](https://www.nuget.org/packages/BenchmarkDotNet/) NuGet package. We support: * *Projects:* classic and modern with PackageReferences -* *Runtimes:* Full .NET Framework (4.6+), .NET Core (2.0+), Mono, NativeAOT +* *Runtimes:* Full .NET Framework (4.6+), .NET Core (3.1+), Mono, NativeAOT * *OS:* Windows, Linux, MacOS * *Languages:* C#, F#, VB diff --git a/samples/BenchmarkDotNet.Samples/IntroEnvVars.cs b/samples/BenchmarkDotNet.Samples/IntroEnvVars.cs index 59bd2db82f..822f73b6bc 100644 --- a/samples/BenchmarkDotNet.Samples/IntroEnvVars.cs +++ b/samples/BenchmarkDotNet.Samples/IntroEnvVars.cs @@ -14,8 +14,8 @@ private class ConfigWithCustomEnvVars : ManualConfig public ConfigWithCustomEnvVars() { - AddJob(Job.Default.WithRuntime(CoreRuntime.Core21).WithId("Inlining enabled")); - AddJob(Job.Default.WithRuntime(CoreRuntime.Core21) + AddJob(Job.Default.WithRuntime(CoreRuntime.Core80).WithId("Inlining enabled")); + AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) .WithEnvironmentVariables(new EnvironmentVariable(JitNoInline, "1")) .WithId("Inlining disabled")); } diff --git a/samples/BenchmarkDotNet.Samples/IntroFluentConfigBuilder.cs b/samples/BenchmarkDotNet.Samples/IntroFluentConfigBuilder.cs index 94c34c1cbd..4ac29919ee 100644 --- a/samples/BenchmarkDotNet.Samples/IntroFluentConfigBuilder.cs +++ b/samples/BenchmarkDotNet.Samples/IntroFluentConfigBuilder.cs @@ -38,7 +38,7 @@ public static void Run() .Run( DefaultConfig.Instance .AddJob(Job.Default.WithRuntime(ClrRuntime.Net462)) - .AddJob(Job.Default.WithRuntime(CoreRuntime.Core21)) + .AddJob(Job.Default.WithRuntime(CoreRuntime.Core80)) .AddValidator(ExecutionValidator.FailOnError)); } } diff --git a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs index 4fc2db7388..78541a763f 100644 --- a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs +++ b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs @@ -57,22 +57,27 @@ public enum RuntimeMoniker /// /// .NET Core 2.0 /// - NetCoreApp20, + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] + // Assigning explicit values so we can check for them without the compiler erroring. + NetCoreApp20 = 10, /// /// .NET Core 2.1 /// - NetCoreApp21, + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] + NetCoreApp21 = 11, /// /// .NET Core 2.2 /// - NetCoreApp22, + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] + NetCoreApp22 = 12, /// /// .NET Core 3.0 /// - NetCoreApp30, + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] + NetCoreApp30 = 13, /// /// .NET Core 3.1 diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs index c97f066fa4..c61f308fcc 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs @@ -107,11 +107,6 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.NetCoreApp50: #pragma warning restore CS0618 // Type or member is obsolete return false; - case RuntimeMoniker.NetCoreApp20: - case RuntimeMoniker.NetCoreApp21: - case RuntimeMoniker.NetCoreApp22: - return OsDetector.IsWindows(); - case RuntimeMoniker.NetCoreApp30: case RuntimeMoniker.NetCoreApp31: return OsDetector.IsWindows() || OsDetector.IsLinux(); default: diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs index be02cc30c8..b650ab84fa 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs @@ -110,11 +110,6 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.NetCoreApp50: #pragma warning restore CS0618 // Type or member is obsolete return false; - case RuntimeMoniker.NetCoreApp20: - case RuntimeMoniker.NetCoreApp21: - case RuntimeMoniker.NetCoreApp22: - return OsDetector.IsWindows(); - case RuntimeMoniker.NetCoreApp30: case RuntimeMoniker.NetCoreApp31: return OsDetector.IsWindows() || OsDetector.IsLinux(); default: diff --git a/src/BenchmarkDotNet/BenchmarkDotNet.csproj b/src/BenchmarkDotNet/BenchmarkDotNet.csproj index e4235ebd57..d18f3a1ea5 100644 --- a/src/BenchmarkDotNet/BenchmarkDotNet.csproj +++ b/src/BenchmarkDotNet/BenchmarkDotNet.csproj @@ -22,9 +22,9 @@ - - - + + + diff --git a/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs b/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs index e3aed1fedd..1dcc93281a 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs @@ -237,8 +237,8 @@ public static IEnumerable Examples yield return new Example("Use Job.ShortRun for running the benchmarks", shortName, new CommandLineOptions { BaseJob = "short" }); yield return new Example("Run benchmarks in process", shortName, new CommandLineOptions { RunInProcess = true }); - yield return new Example("Run benchmarks for .NET 4.7.2, .NET Core 2.1 and Mono. .NET 4.7.2 will be baseline because it was first.", longName, new CommandLineOptions { Runtimes = new[] { "net472", "netcoreapp2.1", "Mono" } }); - yield return new Example("Run benchmarks for .NET Core 2.0, .NET Core 2.1 and .NET Core 2.2. .NET Core 2.0 will be baseline because it was first.", longName, new CommandLineOptions { Runtimes = new[] { "netcoreapp2.0", "netcoreapp2.1", "netcoreapp2.2" } }); + yield return new Example("Run benchmarks for .NET 4.7.2, .NET 8.0 and Mono. .NET 4.7.2 will be baseline because it was first.", longName, new CommandLineOptions { Runtimes = new[] { "net472", "net8.0", "Mono" } }); + yield return new Example("Run benchmarks for .NET Core 3.1, .NET 6.0 and .NET 8.0. .NET Core 3.1 will be baseline because it was first.", longName, new CommandLineOptions { Runtimes = new[] { "netcoreapp3.1", "net6.0", "net8.0" } }); yield return new Example("Use MemoryDiagnoser to get GC stats", shortName, new CommandLineOptions { UseMemoryDiagnoser = true }); yield return new Example("Use DisassemblyDiagnoser to get disassembly", shortName, new CommandLineOptions { UseDisassemblyDiagnoser = true }); yield return new Example("Use HardwareCountersDiagnoser to get hardware counter info", longName, new CommandLineOptions { HardwareCounters = new[] { nameof(HardwareCounter.CacheMisses), nameof(HardwareCounter.InstructionRetired) } }); @@ -250,8 +250,8 @@ public static IEnumerable Examples yield return new Example("Run selected benchmarks once per iteration", longName, new CommandLineOptions { RunOncePerIteration = true }); yield return new Example("Run selected benchmarks 100 times per iteration. Perform single warmup iteration and 5 actual workload iterations", longName, new CommandLineOptions { InvocationCount = 100, WarmupIterationCount = 1, IterationCount = 5}); yield return new Example("Run selected benchmarks 250ms per iteration. Perform from 9 to 15 iterations", longName, new CommandLineOptions { IterationTimeInMilliseconds = 250, MinIterationCount = 9, MaxIterationCount = 15}); - yield return new Example("Run MannWhitney test with relative ratio of 5% for all benchmarks for .NET Core 2.0 (base) vs .NET Core 2.1 (diff). .NET Core 2.0 will be baseline because it was provided as first.", longName, - new CommandLineOptions { Filters = new[] { "*"}, Runtimes = new[] { "netcoreapp2.0", "netcoreapp2.1" }, StatisticalTestThreshold = "5%" }); + yield return new Example("Run MannWhitney test with relative ratio of 5% for all benchmarks for .NET 6.0 (base) vs .NET 8.0 (diff). .NET Core 6.0 will be baseline because it was provided as first.", longName, + new CommandLineOptions { Filters = new[] { "*"}, Runtimes = new[] { "net6.0", "net8.0" }, StatisticalTestThreshold = "5%" }); yield return new Example("Run benchmarks using environment variables 'ENV_VAR_KEY_1' with value 'value_1' and 'ENV_VAR_KEY_2' with value 'value_2'", longName, new CommandLineOptions { EnvironmentVariables = new[] { "ENV_VAR_KEY_1:value_1", "ENV_VAR_KEY_2:value_2" } }); yield return new Example("Hide Mean and Ratio columns (use double quotes for multi-word columns: \"Alloc Ratio\")", shortName, new CommandLineOptions { HiddenColumns = new[] { "Mean", "Ratio" }, }); diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs index 355f1a6b75..9ec37c39eb 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs @@ -519,10 +519,6 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma .WithRuntime(runtimeMoniker.GetRuntime()) .WithToolchain(CsProjClassicNetToolchain.From(runtimeId, options.RestorePath?.FullName, options.CliPath?.FullName)); - case RuntimeMoniker.NetCoreApp20: - case RuntimeMoniker.NetCoreApp21: - case RuntimeMoniker.NetCoreApp22: - case RuntimeMoniker.NetCoreApp30: case RuntimeMoniker.NetCoreApp31: #pragma warning disable CS0618 // Type or member is obsolete case RuntimeMoniker.NetCoreApp50: diff --git a/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs b/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs index c1c76e8c9e..81f1abdf75 100644 --- a/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs +++ b/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs @@ -62,9 +62,9 @@ public IEnumerable Validate(ValidationParameters validationPara { var runtime = benchmark.Job.ResolveValue(EnvironmentMode.RuntimeCharacteristic, EnvironmentResolver.Instance); - if (runtime.RuntimeMoniker < RuntimeMoniker.NetCoreApp30) + if (runtime.RuntimeMoniker < RuntimeMoniker.NetCoreApp31) { - yield return new ValidationError(true, $"{nameof(EventPipeProfiler)} supports only .NET Core 3.0+", benchmark); + yield return new ValidationError(true, $"{nameof(EventPipeProfiler)} supports only .NET Core 3.1+", benchmark); } } } diff --git a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs index 057a0bb624..dd2a60efca 100644 --- a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs @@ -43,9 +43,9 @@ public IEnumerable Validate(ValidationParameters validationPara { var runtime = benchmark.Job.ResolveValue(EnvironmentMode.RuntimeCharacteristic, EnvironmentResolver.Instance); - if (runtime.RuntimeMoniker < RuntimeMoniker.NetCoreApp30) + if (runtime.RuntimeMoniker < RuntimeMoniker.NetCoreApp31) { - yield return new ValidationError(true, $"{nameof(ThreadingDiagnoser)} supports only .NET Core 3.0+", benchmark); + yield return new ValidationError(true, $"{nameof(ThreadingDiagnoser)} supports only .NET Core 3.1+", benchmark); } } } diff --git a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs index 7d0f95326c..1c98b0752c 100644 --- a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs +++ b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs @@ -117,11 +117,6 @@ public IEnumerable Validate(ValidationParameters validationPara { var runtime = benchmark.Job.ResolveValue(EnvironmentMode.RuntimeCharacteristic, EnvironmentResolver.Instance); - if (runtime.RuntimeMoniker < RuntimeMoniker.NetCoreApp30) - { - yield return new ValidationError(true, $"{nameof(DisassemblyDiagnoser)} supports only .NET Core 3.0+", benchmark); - } - if (ptrace_scope.Value == "2") { yield return new ValidationError(false, $"ptrace_scope is set to 2, {nameof(DisassemblyDiagnoser)} is going to work only if you run as sudo"); diff --git a/src/BenchmarkDotNet/Engines/GcStats.cs b/src/BenchmarkDotNet/Engines/GcStats.cs index ca60b0deea..9c4c639737 100644 --- a/src/BenchmarkDotNet/Engines/GcStats.cs +++ b/src/BenchmarkDotNet/Engines/GcStats.cs @@ -39,7 +39,7 @@ private GcStats(int gen0Collections, int gen1Collections, int gen2Collections, l public long? GetBytesAllocatedPerOperation(BenchmarkCase benchmarkCase) { - bool excludeAllocationQuantumSideEffects = benchmarkCase.GetRuntime().RuntimeMoniker <= RuntimeMoniker.NetCoreApp20; // the issue got fixed for .NET Core 2.0+ https://github.com/dotnet/coreclr/issues/10207 + bool excludeAllocationQuantumSideEffects = benchmarkCase.GetRuntime().RuntimeMoniker <= (RuntimeMoniker) 10; // the issue got fixed for .NET Core 2.0+ https://github.com/dotnet/coreclr/issues/10207 long? allocatedBytes = GetTotalAllocatedBytes(excludeAllocationQuantumSideEffects); return allocatedBytes == null ? null diff --git a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs index d86e4b0f40..8e4c508285 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs @@ -11,9 +11,13 @@ namespace BenchmarkDotNet.Environments { public class CoreRuntime : Runtime { + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] public static readonly CoreRuntime Core20 = new (RuntimeMoniker.NetCoreApp20, "netcoreapp2.0", ".NET Core 2.0"); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] public static readonly CoreRuntime Core21 = new (RuntimeMoniker.NetCoreApp21, "netcoreapp2.1", ".NET Core 2.1"); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] public static readonly CoreRuntime Core22 = new (RuntimeMoniker.NetCoreApp22, "netcoreapp2.2", ".NET Core 2.2"); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] public static readonly CoreRuntime Core30 = new (RuntimeMoniker.NetCoreApp30, "netcoreapp3.0", ".NET Core 3.0"); public static readonly CoreRuntime Core31 = new (RuntimeMoniker.NetCoreApp31, "netcoreapp3.1", ".NET Core 3.1"); public static readonly CoreRuntime Core50 = new (RuntimeMoniker.Net50, "net5.0", ".NET 5.0"); @@ -64,10 +68,6 @@ internal static CoreRuntime FromVersion(Version version) { switch (version) { - case Version v when v.Major == 2 && v.Minor == 0: return Core20; - case Version v when v.Major == 2 && v.Minor == 1: return Core21; - case Version v when v.Major == 2 && v.Minor == 2: return Core22; - case Version v when v.Major == 3 && v.Minor == 0: return Core30; case Version v when v.Major == 3 && v.Minor == 1: return Core31; case Version v when v.Major == 5 && v.Minor == 0: return GetPlatformSpecific(Core50); case Version v when v.Major == 6 && v.Minor == 0: return GetPlatformSpecific(Core60); @@ -75,7 +75,9 @@ internal static CoreRuntime FromVersion(Version version) case Version v when v.Major == 8 && v.Minor == 0: return GetPlatformSpecific(Core80); case Version v when v.Major == 9 && v.Minor == 0: return GetPlatformSpecific(Core90); default: - return CreateForNewVersion($"net{version.Major}.{version.Minor}", $".NET {version.Major}.{version.Minor}"); + return version >= new Version(3, 1) + ? CreateForNewVersion($"net{version.Major}.{version.Minor}", $".NET {version.Major}.{version.Minor}") + : throw new PlatformNotSupportedException($"netcoreapp{version.Major}.{version.Minor} is no longer supported. Use a newer runtime version or use BenchmarkDotNet v0.14.X or older."); } } diff --git a/src/BenchmarkDotNet/Environments/Runtimes/Runtime.cs b/src/BenchmarkDotNet/Environments/Runtimes/Runtime.cs index b695de321c..4a46e6456d 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/Runtime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/Runtime.cs @@ -18,7 +18,7 @@ public abstract class Runtime : IEquatable public RuntimeMoniker RuntimeMoniker { get; } /// - /// MsBuild Target Framework Moniker, example: net462, netcoreapp2.1 + /// MsBuild Target Framework Moniker, example: net462, net8.0 /// public string MsBuildMoniker { get; } diff --git a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs index 5ba51eebdd..03d44c1ba7 100644 --- a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs @@ -24,14 +24,6 @@ internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker) return ClrRuntime.Net48; case RuntimeMoniker.Net481: return ClrRuntime.Net481; - case RuntimeMoniker.NetCoreApp20: - return CoreRuntime.Core20; - case RuntimeMoniker.NetCoreApp21: - return CoreRuntime.Core21; - case RuntimeMoniker.NetCoreApp22: - return CoreRuntime.Core22; - case RuntimeMoniker.NetCoreApp30: - return CoreRuntime.Core30; case RuntimeMoniker.NetCoreApp31: return CoreRuntime.Core31; case RuntimeMoniker.Net50: diff --git a/src/BenchmarkDotNet/Helpers/ArtifactFileNameHelper.cs b/src/BenchmarkDotNet/Helpers/ArtifactFileNameHelper.cs index b85f9fd297..6873bb834b 100644 --- a/src/BenchmarkDotNet/Helpers/ArtifactFileNameHelper.cs +++ b/src/BenchmarkDotNet/Helpers/ArtifactFileNameHelper.cs @@ -69,7 +69,7 @@ private static string GetLimitedFilePath(DiagnoserActionParameters details, stri private static string GetFilePath(string fileName, DiagnoserActionParameters details, string? subfolder, DateTime? creationTime, string fileExtension) { - // if we run for more than one toolchain, the output file name should contain the name too so we can differ net462 vs netcoreapp2.1 etc + // if we run for more than one toolchain, the output file name should contain the name too so we can differ net462 vs net8.0 etc if (details.Config.GetJobs().Select(job => ToolchainExtensions.GetToolchain(job)).Distinct().Count() > 1) fileName += $"-{details.BenchmarkCase.Job.Environment.Runtime?.Name ?? details.BenchmarkCase.GetToolchain()?.Name ?? details.BenchmarkCase.Job.Id}"; diff --git a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs index 8bdacf37a4..3cbaa80f65 100644 --- a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs @@ -15,12 +15,12 @@ public class CoreRunToolchain : IToolchain /// /// the path to CoreRun /// /should a copy of CoreRun be performed? True by default. The toolchain replaces old dependencies in CoreRun folder with newer versions if used by the benchmarks. - /// TFM, netcoreapp2.1 is the default + /// TFM, net8.0 is the default /// path to dotnet cli, if not provided the one from PATH will be used /// display name, CoreRun is the default value /// the directory to restore packages to public CoreRunToolchain(FileInfo coreRun, bool createCopy = true, - string targetFrameworkMoniker = "netcoreapp2.1", + string targetFrameworkMoniker = "net8.0", FileInfo? customDotNetCliPath = null, DirectoryInfo? restorePath = null, string displayName = "CoreRun") { diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs index c6cf6248df..65fa9239d2 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs @@ -15,9 +15,13 @@ namespace BenchmarkDotNet.Toolchains.CsProj [PublicAPI] public class CsProjCoreToolchain : Toolchain, IEquatable { + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] [PublicAPI] public static readonly IToolchain NetCoreApp20 = From(NetCoreAppSettings.NetCoreApp20); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] [PublicAPI] public static readonly IToolchain NetCoreApp21 = From(NetCoreAppSettings.NetCoreApp21); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] [PublicAPI] public static readonly IToolchain NetCoreApp22 = From(NetCoreAppSettings.NetCoreApp22); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] [PublicAPI] public static readonly IToolchain NetCoreApp30 = From(NetCoreAppSettings.NetCoreApp30); [PublicAPI] public static readonly IToolchain NetCoreApp31 = From(NetCoreAppSettings.NetCoreApp31); [PublicAPI] public static readonly IToolchain NetCoreApp50 = From(NetCoreAppSettings.NetCoreApp50); diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/CustomDotNetCliToolchainBuilder.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/CustomDotNetCliToolchainBuilder.cs index 3e56c6be24..6223f9ea3c 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/CustomDotNetCliToolchainBuilder.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/CustomDotNetCliToolchainBuilder.cs @@ -50,7 +50,7 @@ public CustomDotNetCliToolchainBuilder UseNuGetClearTag(bool value) return this; } - /// TFM, example: netcoreapp2.1 + /// TFM, example: net8.0 [PublicAPI] [SuppressMessage("ReSharper", "ParameterHidesMember")] public CustomDotNetCliToolchainBuilder TargetFrameworkMoniker(string targetFrameworkMoniker) diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs index 6f48698cb9..9da421396f 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs @@ -1,5 +1,6 @@ using BenchmarkDotNet.Toolchains.MonoAotLLVM; using JetBrains.Annotations; +using System; namespace BenchmarkDotNet.Toolchains.DotNetCli { @@ -9,9 +10,13 @@ namespace BenchmarkDotNet.Toolchains.DotNetCli [PublicAPI] public class NetCoreAppSettings { + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp20 = new ("netcoreapp2.0", null, ".NET Core 2.0"); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp21 = new ("netcoreapp2.1", null, ".NET Core 2.1"); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp22 = new ("netcoreapp2.2", null, ".NET Core 2.2"); + [Obsolete("This runtime is no longer supported. Use a newer runtime or use BenchmarkDotNet v0.14.X or older.", true)] [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp30 = new ("netcoreapp3.0", null, ".NET Core 3.0"); [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp31 = new ("netcoreapp3.1", null, ".NET Core 3.1"); [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp50 = new ("net5.0", null, ".NET 5.0"); @@ -22,7 +27,7 @@ public class NetCoreAppSettings /// /// - /// sample values: netcoreapp2.0, netcoreapp2.1 + /// sample values: net6.0, net8.0 /// /// /// used in the auto-generated .csproj file @@ -64,7 +69,7 @@ public NetCoreAppSettings( } /// - /// sample values: netcoreapp2.0, netcoreapp2.1 + /// sample values: net6.0, net8.0 /// public string TargetFrameworkMoniker { get; } diff --git a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs index 499d5d8f40..75d2898d0a 100644 --- a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs +++ b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs @@ -123,18 +123,6 @@ private static IToolchain GetToolchain(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.Net481: return CsProjClassicNetToolchain.Net481; - case RuntimeMoniker.NetCoreApp20: - return CsProjCoreToolchain.NetCoreApp20; - - case RuntimeMoniker.NetCoreApp21: - return CsProjCoreToolchain.NetCoreApp21; - - case RuntimeMoniker.NetCoreApp22: - return CsProjCoreToolchain.NetCoreApp22; - - case RuntimeMoniker.NetCoreApp30: - return CsProjCoreToolchain.NetCoreApp30; - case RuntimeMoniker.NetCoreApp31: return CsProjCoreToolchain.NetCoreApp31; #pragma warning disable CS0618 // Type or member is obsolete diff --git a/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs b/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs index 8b61a0d51c..93a3af80dd 100644 --- a/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs +++ b/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs @@ -204,10 +204,6 @@ private static string GetSdkVersionFromMoniker(RuntimeMoniker runtimeMoniker) RuntimeMoniker.Net472 => "4.7.2", RuntimeMoniker.Net48 => "4.8", RuntimeMoniker.Net481 => "4.8.1", - RuntimeMoniker.NetCoreApp20 => "2.0", - RuntimeMoniker.NetCoreApp21 => "2.1", - RuntimeMoniker.NetCoreApp22 => "2.2", - RuntimeMoniker.NetCoreApp30 => "3.0", RuntimeMoniker.NetCoreApp31 => "3.1", RuntimeMoniker.Net50 => "5.0", RuntimeMoniker.Net60 => "6.0", diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj index 8c8374eaf0..13377c7765 100644 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj @@ -2,18 +2,11 @@ BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks - + + net462;net48;netcoreapp3.1;net8.0 + + $(NoWarn);NETSDK1138;NU1901;NU1902;NU1903;NU1904 false - - - - - - - - - - net462;net48;net8.0 true BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks @@ -45,10 +38,10 @@ - - + - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/MultipleFrameworksTest.cs b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/MultipleFrameworksTest.cs index 0e9eb6f80d..a73a47e7c2 100644 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/MultipleFrameworksTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/MultipleFrameworksTest.cs @@ -8,21 +8,18 @@ namespace BenchmarkDotNet.IntegrationTests.ManualRunning { - // Note: To properly test this locally, modify - // BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj, - // following the comments in that file. public class MultipleFrameworksTest : BenchmarkTestExecutor { private const string TfmEnvVarName = "TfmEnvVarName"; [Theory] - [InlineData(RuntimeMoniker.Net461)] + [InlineData(RuntimeMoniker.Net462)] [InlineData(RuntimeMoniker.Net48)] - [InlineData(RuntimeMoniker.NetCoreApp20)] + [InlineData(RuntimeMoniker.NetCoreApp31)] [InlineData(RuntimeMoniker.Net80)] public void EachFrameworkIsRebuilt(RuntimeMoniker runtime) { -#if NET461 +#if NET462 // We cannot detect what target framework version the host was compiled for on full Framework, // which causes the RoslynToolchain to be used instead of CsProjClassicNetToolchain when the host is full Framework // (because full Framework always uses the version that's installed on the machine, unlike Core), @@ -43,12 +40,12 @@ public void EachFrameworkIsRebuilt(RuntimeMoniker runtime) public class ValuePerTfm { private const RuntimeMoniker moniker = -#if NET461 - RuntimeMoniker.Net461; +#if NET462 + RuntimeMoniker.Net462; #elif NET48 RuntimeMoniker.Net48; -#elif NETCOREAPP2_0 - RuntimeMoniker.NetCoreApp20; +#elif NETCOREAPP3_1 + RuntimeMoniker.NetCoreApp31; #elif NET8_0 RuntimeMoniker.Net80; #else diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/LocalNativeAotToolchainTests.cs b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/LocalNativeAotToolchainTests.cs index c8740a3829..64f469efc7 100644 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/LocalNativeAotToolchainTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/LocalNativeAotToolchainTests.cs @@ -13,7 +13,7 @@ namespace BenchmarkDotNet.IntegrationTests.ManualRunning /// to run these tests please clone and build NativeAOT first, /// then update the hardcoded path /// and run following command from console: - /// dotnet test -c Release -f netcoreapp2.1 --filter "FullyQualifiedName~BenchmarkDotNet.IntegrationTests.ManualRunning.LocalNativeAotToolchainTests" + /// dotnet test -c Release -f net8.0 --filter "FullyQualifiedName~BenchmarkDotNet.IntegrationTests.ManualRunning.LocalNativeAotToolchainTests" /// /// in perfect world we would do this OOB for you, but building NativeAOT /// so it's not part of our CI jobs @@ -29,7 +29,7 @@ public void CanBenchmarkLocalBuildUsingRyuJit() { var config = ManualConfig.CreateEmpty() .AddJob(Job.Dry - .WithRuntime(NativeAotRuntime.Net60) + .WithRuntime(NativeAotRuntime.Net80) .WithToolchain( NativeAotToolchain.CreateBuilder() .UseLocalBuild(new System.IO.DirectoryInfo(IlcPath)) diff --git a/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs b/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs index db3dddfe2e..b805af6d26 100755 --- a/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs @@ -257,8 +257,7 @@ public void Allocate() } } - [TheoryEnvSpecific(".NET Core 3.0 preview6+ exposes a GC.GetTotalAllocatedBytes method which makes it possible to work", - EnvRequirement.DotNetCore30Only)] + [Theory(Skip = "Test is flaky even in latest .Net")] [MemberData(nameof(GetToolchains))] [Trait(Constants.Category, Constants.BackwardCompatibilityCategory)] public void MemoryDiagnoserIsAccurateForMultiThreadedBenchmarks(IToolchain toolchain) diff --git a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs index 5da3fd7a21..73d29d3611 100644 --- a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs @@ -271,10 +271,10 @@ public void IlCompilerPathParsedCorrectly() } [Theory] - [InlineData("netcoreapp2.0", true)] - [InlineData("netcoreapp2.1", true)] - [InlineData("netcoreapp2.2", true)] - [InlineData("netcoreapp3.0", true)] + [InlineData("netcoreapp3.1", true)] + [InlineData("net5.0", true)] + [InlineData("net6.0", true)] + [InlineData("net8.0", true)] [InlineData("net462", false)] [InlineData("net48", false)] public void DotNetCliParsedCorrectly(string tfm, bool isCore) @@ -333,7 +333,7 @@ public void WhenConfigOptionsFlagsAreNotSpecifiedTheyAreNotSet() public void PackagesPathParsedCorrectly() { var fakeRestoreDirectory = new FileInfo(typeof(object).Assembly.Location).Directory.FullName; - var config = ConfigParser.Parse(new[] { "-r", "netcoreapp3.0", "--packages", fakeRestoreDirectory }, new OutputLogger(Output)).config; + var config = ConfigParser.Parse(new[] { "-r", "netcoreapp3.1", "--packages", fakeRestoreDirectory }, new OutputLogger(Output)).config; Assert.Single(config.GetJobs()); CsProjCoreToolchain toolchain = config.GetJobs().Single().GetToolchain() as CsProjCoreToolchain; @@ -345,7 +345,7 @@ public void PackagesPathParsedCorrectly() public void UserCanSpecifyBuildTimeout() { const int timeoutInSeconds = 10; - var config = ConfigParser.Parse(new[] { "-r", "netcoreapp3.0", "--buildTimeout", timeoutInSeconds.ToString() }, new OutputLogger(Output)).config; + var config = ConfigParser.Parse(new[] { "-r", "netcoreapp3.1", "--buildTimeout", timeoutInSeconds.ToString() }, new OutputLogger(Output)).config; Assert.Single(config.GetJobs()); CsProjCoreToolchain toolchain = config.GetJobs().Single().GetToolchain() as CsProjCoreToolchain; @@ -356,7 +356,7 @@ public void UserCanSpecifyBuildTimeout() [Fact] public void WhenUserDoesNotSpecifyTimeoutTheDefaultValueIsUsed() { - var config = ConfigParser.Parse(new[] { "-r", "netcoreapp3.0" }, new OutputLogger(Output)).config; + var config = ConfigParser.Parse(new[] { "-r", "netcoreapp3.1" }, new OutputLogger(Output)).config; Assert.Single(config.GetJobs()); CsProjCoreToolchain toolchain = config.GetJobs().Single().GetToolchain() as CsProjCoreToolchain; @@ -414,15 +414,15 @@ public void PlatformSpecificMonikersAreSupported(string msBuildMoniker) [Fact] public void CanCompareFewDifferentRuntimes() { - var config = ConfigParser.Parse(new[] { "--runtimes", "net462", "MONO", "netcoreapp3.0", "nativeaot6.0", "nativeAOT7.0", "nativeAOT8.0" }, + var config = ConfigParser.Parse(["--runtimes", "net462", "MONO", "netcoreapp3.1", "nativeaot6.0", "nativeAOT7.0", "nativeAOT8.0"], new OutputLogger(Output)).config; Assert.True(config.GetJobs().First().Meta.Baseline); // when the user provides multiple runtimes the first one should be marked as baseline Assert.Single(config.GetJobs().Where(job => job.Environment.Runtime is ClrRuntime clrRuntime && clrRuntime.MsBuildMoniker == "net462")); Assert.Single(config.GetJobs().Where(job => job.Environment.Runtime is MonoRuntime)); Assert.Single(config.GetJobs().Where(job => - job.Environment.Runtime is CoreRuntime coreRuntime && coreRuntime.MsBuildMoniker == "netcoreapp3.0" && - coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp30)); + job.Environment.Runtime is CoreRuntime coreRuntime && coreRuntime.MsBuildMoniker == "netcoreapp3.1" && + coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp31)); Assert.Single(config.GetJobs().Where(job => job.Environment.Runtime is NativeAotRuntime nativeAot && nativeAot.MsBuildMoniker == "net6.0" && nativeAot.RuntimeMoniker == RuntimeMoniker.NativeAot60)); @@ -439,7 +439,7 @@ public void CanCompareFewDifferentRuntimes() [InlineData("10ms")] public void CanUseStatisticalTestsToCompareFewDifferentRuntimes(string threshold) { - string[] arguments = ["--runtimes", "netcoreapp2.1", "netcoreapp2.2", "--statisticalTest", threshold]; + string[] arguments = ["--runtimes", "net6.0", "net8.0", "--statisticalTest", threshold]; var config = ConfigParser.Parse(arguments, new OutputLogger(Output)).config; var mockSummary = MockFactory.CreateSummary(config); diff --git a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs index 2eae58c7c1..2393cb737c 100644 --- a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs +++ b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs @@ -249,10 +249,10 @@ public void MissingDependencyIsNotAddedWhenItIsAlreadyPresent() [Fact] public void WhenTwoConfigsAreAddedTheRegularJobsAreJustAdded() { - var configWithClrJob = CreateConfigFromJobs(Job.Default.WithRuntime(CoreRuntime.Core21)); - var configWithCoreJob = CreateConfigFromJobs(Job.Default.WithRuntime(ClrRuntime.Net462)); + var configWithClrJob = CreateConfigFromJobs(Job.Default.WithRuntime(ClrRuntime.Net462)); + var configWithCoreJob = CreateConfigFromJobs(Job.Default.WithRuntime(CoreRuntime.Core80)); - foreach (var added in AddLeftToTheRightAndRightToTheLef(configWithClrJob, configWithCoreJob)) + foreach (var added in AddLeftToTheRightAndRightToTheLef(configWithCoreJob, configWithClrJob)) { var runnableJobs = added.GetJobs(); @@ -269,7 +269,7 @@ public void WhenTwoConfigsAreAddedTheMutatorJobsAreAppliedToAllOtherJobs() var configWithMutatorJob = CreateConfigFromJobs(Job.Default.WithWarmupCount(warmupCount).AsMutator()); var configWithTwoStandardJobs = CreateConfigFromJobs( Job.Default.WithRuntime(ClrRuntime.Net462), - Job.Default.WithRuntime(CoreRuntime.Core21)); + Job.Default.WithRuntime(CoreRuntime.Core80)); foreach (var added in AddLeftToTheRightAndRightToTheLef(configWithTwoStandardJobs, configWithMutatorJob)) { diff --git a/tests/BenchmarkDotNet.Tests/Configs/JobTests.cs b/tests/BenchmarkDotNet.Tests/Configs/JobTests.cs index 050b84de7c..fd47e9fd83 100644 --- a/tests/BenchmarkDotNet.Tests/Configs/JobTests.cs +++ b/tests/BenchmarkDotNet.Tests/Configs/JobTests.cs @@ -403,7 +403,7 @@ public static void Test06CharacteristicHacks() [Fact] public static void MutatorAppliedToOtherJobOverwritesOnlyTheConfiguredSettings() { - var jobBefore = Job.Default.WithRuntime(CoreRuntime.Core30); // this is a default job with Runtime set to Core + var jobBefore = Job.Default.WithRuntime(CoreRuntime.Core80); // this is a default job with Runtime set to Core var copy = jobBefore.UnfreezeCopy(); Assert.False(copy.HasValue(RunMode.MaxIterationCountCharacteristic)); diff --git a/tests/BenchmarkDotNet.Tests/CsProjGeneratorTests.cs b/tests/BenchmarkDotNet.Tests/CsProjGeneratorTests.cs index 706e74b561..ef1225432e 100644 --- a/tests/BenchmarkDotNet.Tests/CsProjGeneratorTests.cs +++ b/tests/BenchmarkDotNet.Tests/CsProjGeneratorTests.cs @@ -26,7 +26,7 @@ public class CsProjGeneratorTests [Theory] [InlineData("net471", false)] - [InlineData("netcoreapp3.0", true)] + [InlineData("netcoreapp3.1", true)] public void ItsPossibleToCustomizeProjectSdkBasedOnProjectSdkFromTheProjectFile(string targetFrameworkMoniker, bool isNetCore) { const string withCustomProjectSdk = @" @@ -41,7 +41,7 @@ public void ItsImpossibleToCustomizeProjectSdkForFullFrameworkAppsBasedOnTheImpo { const string withCustomProjectImport = @" - + "; AssertParsedSdkName(withCustomProjectImport, "net471", "Microsoft.NET.Sdk", false); @@ -52,10 +52,10 @@ public void ItsPossibleToCustomizeProjectSdkForNetCoreAppsBasedOnTheImportOfSdk( { const string withCustomProjectImport = @" - + "; - AssertParsedSdkName(withCustomProjectImport, "netcoreapp3.0", "Microsoft.NET.Sdk.WindowsDesktop", true); + AssertParsedSdkName(withCustomProjectImport, "netcoreapp3.1", "Microsoft.NET.Sdk.WindowsDesktop", true); } [AssertionMethod] @@ -87,7 +87,7 @@ public void UseWpfSettingGetsCopied() "; - var sut = new CsProjGenerator("netcoreapp3.0", null, null, null, true); + var sut = new CsProjGenerator("netcoreapp3.1", null, null, null, true); var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(withUseWpfTrue); @@ -117,7 +117,7 @@ public void SettingsFromPropsFileImportedUsingAbsolutePathGetCopies() "; - var sut = new CsProjGenerator("netcoreapp3.0", null, null, null, true); + var sut = new CsProjGenerator("netcoreapp3.1", null, null, null, true); var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(importingAbsolutePath); @@ -149,7 +149,7 @@ public void SettingsFromPropsFileImportedUsingRelativePathGetCopies() "; - var sut = new CsProjGenerator("netcoreapp3.0", null, null, null, true); + var sut = new CsProjGenerator("netcoreapp3.1", null, null, null, true); var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(importingRelativePath); @@ -171,7 +171,7 @@ public void RuntimeHostConfigurationOptionIsCopied() {runtimeHostConfigurationOptionChunk} "; - var sut = new CsProjGenerator("netcoreapp3.0", null, null, null, true); + var sut = new CsProjGenerator("netcoreapp3.1", null, null, null, true); var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(source); @@ -196,7 +196,7 @@ public void TheDefaultFilePathShouldBeUsedWhenAnAssemblyLocationIsEmpty() var benchmarkCase = BenchmarkCase.Create(target, Job.Default, null, config); var benchmarks = new[] { new BenchmarkBuildInfo(benchmarkCase, config.CreateImmutableConfig(), 999) }; - var projectGenerator = new SteamLoadedBuildPartition("netcoreapp3.0", null, null, null, true); + var projectGenerator = new SteamLoadedBuildPartition("netcoreapp3.1", null, null, null, true); string binariesPath = projectGenerator.ResolvePathForBinaries(new BuildPartition(benchmarks, new Resolver()), programName); string expectedPath = Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), "BenchmarkDotNet.Bin"), programName); @@ -210,7 +210,7 @@ public void TestAssemblyFilePathIsUsedWhenTheAssemblyLocationIsNotEmpty() var target = new Descriptor(MockFactory.MockType, MockFactory.MockMethodInfo); var benchmarkCase = BenchmarkCase.Create(target, Job.Default, null, ManualConfig.CreateEmpty().CreateImmutableConfig()); var benchmarks = new[] { new BenchmarkBuildInfo(benchmarkCase, ManualConfig.CreateEmpty().CreateImmutableConfig(), 0) }; - var projectGenerator = new SteamLoadedBuildPartition("netcoreapp3.0", null, null, null, true); + var projectGenerator = new SteamLoadedBuildPartition("netcoreapp3.1", null, null, null, true); var buildPartition = new BuildPartition(benchmarks, new Resolver()); string binariesPath = projectGenerator.ResolvePathForBinaries(buildPartition, programName); diff --git a/tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs b/tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs index 8952accb40..92811e5933 100644 --- a/tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs +++ b/tests/BenchmarkDotNet.Tests/Reports/SummaryTests.cs @@ -35,7 +35,7 @@ public void SummaryWithFailureReportDoesNotThrowNre() private static IConfig CreateConfig() { // We use runtime as selector later. It is chosen as selector just to be close to initial issue. Nothing particularly special about it. - Job coreJob = new Job(Job.Default).WithRuntime(CoreRuntime.Core20).ApplyAndFreeze(RunMode.Dry); + Job coreJob = new Job(Job.Default).WithRuntime(CoreRuntime.Core80).ApplyAndFreeze(RunMode.Dry); Job clrJob = new Job(Job.Default).WithRuntime(ClrRuntime.Net462).ApplyAndFreeze(RunMode.Dry); return ManualConfig.Create(DefaultConfig.Instance).AddJob(coreJob).AddJob(clrJob); } diff --git a/tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs b/tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs index 4eb72b5d59..4b3e92d95d 100644 --- a/tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs +++ b/tests/BenchmarkDotNet.Tests/Running/BenchmarkConverterTests.cs @@ -135,7 +135,7 @@ public void JobMutatorsApplySettingsToAllNonMutatorJobs() typeof(WithMutator), DefaultConfig.Instance .AddJob(Job.Default.WithRuntime(ClrRuntime.Net462)) - .AddJob(Job.Default.WithRuntime(CoreRuntime.Core21))); + .AddJob(Job.Default.WithRuntime(CoreRuntime.Core80))); Assert.Equal(2, info.BenchmarksCases.Length); Assert.All(info.BenchmarksCases, benchmark => Assert.Equal(int.MaxValue, benchmark.Job.Run.MaxIterationCount)); diff --git a/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs b/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs index 5fa5f47051..876d0ae42e 100644 --- a/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs +++ b/tests/BenchmarkDotNet.Tests/RuntimeVersionDetectionTests.cs @@ -12,12 +12,12 @@ namespace BenchmarkDotNet.Tests public class RuntimeVersionDetectionTests { [Theory] - [InlineData(".NETCoreApp,Version=v2.0", RuntimeMoniker.NetCoreApp20, "netcoreapp2.0")] - [InlineData(".NETCoreApp,Version=v2.1", RuntimeMoniker.NetCoreApp21, "netcoreapp2.1")] - [InlineData(".NETCoreApp,Version=v2.2", RuntimeMoniker.NetCoreApp22, "netcoreapp2.2")] - [InlineData(".NETCoreApp,Version=v3.0", RuntimeMoniker.NetCoreApp30, "netcoreapp3.0")] [InlineData(".NETCoreApp,Version=v3.1", RuntimeMoniker.NetCoreApp31, "netcoreapp3.1")] [InlineData(".NETCoreApp,Version=v5.0", RuntimeMoniker.Net50, "net5.0")] + [InlineData(".NETCoreApp,Version=v6.0", RuntimeMoniker.Net60, "net6.0")] + [InlineData(".NETCoreApp,Version=v7.0", RuntimeMoniker.Net70, "net7.0")] + [InlineData(".NETCoreApp,Version=v8.0", RuntimeMoniker.Net80, "net8.0")] + [InlineData(".NETCoreApp,Version=v9.0", RuntimeMoniker.Net90, "net9.0")] [InlineData(".NETCoreApp,Version=v123.0", RuntimeMoniker.NotRecognized, "net123.0")] public void TryGetVersionFromFrameworkNameHandlesValidInput(string frameworkName, RuntimeMoniker expectedTfm, string expectedMsBuildMoniker) { @@ -40,9 +40,6 @@ public void TryGetVersionFromFrameworkNameHandlesInvalidInput(string? frameworkN } [Theory] - [InlineData(RuntimeMoniker.NetCoreApp21, "netcoreapp2.1", "Microsoft .NET Framework", "4.6.27817.01 @BuiltBy: dlab14-DDVSOWINAGE101 @Branch: release/2.1 @SrcCode: https://github.com/dotnet/coreclr/tree/6f78fbb3f964b4f407a2efb713a186384a167e5c")] - [InlineData(RuntimeMoniker.NetCoreApp22, "netcoreapp2.2", "Microsoft .NET Framework", "4.6.27817.03 @BuiltBy: dlab14-DDVSOWINAGE101 @Branch: release/2.2 @SrcCode: https://github.com/dotnet/coreclr/tree/ce1d090d33b400a25620c0145046471495067cc7")] - [InlineData(RuntimeMoniker.NetCoreApp30, "netcoreapp3.0", "Microsoft .NET Core", "3.0.0-preview8-28379-12")] [InlineData(RuntimeMoniker.NetCoreApp31, "netcoreapp3.1", "Microsoft .NET Core", "3.1.0-something")] [InlineData(RuntimeMoniker.Net50, "net5.0", "Microsoft .NET Core", "5.0.0-alpha1.19415.3")] [InlineData(RuntimeMoniker.NotRecognized, "net123.0", "Microsoft .NET Core", "123.0.0-future")] @@ -70,10 +67,6 @@ public static IEnumerable FromNetCoreAppVersionHandlesValidInputArgume { string directoryPrefix = Path.GetTempPath(); // this test runs on Unix, it can not be hardcoded due to / \ difference - yield return new object[] { Path.Combine(directoryPrefix, "2.0.9") + Path.DirectorySeparatorChar, RuntimeMoniker.NetCoreApp20, "netcoreapp2.0" }; - yield return new object[] { Path.Combine(directoryPrefix, "2.1.12") + Path.DirectorySeparatorChar, RuntimeMoniker.NetCoreApp21, "netcoreapp2.1" }; - yield return new object[] { Path.Combine(directoryPrefix, "2.2.6") + Path.DirectorySeparatorChar, RuntimeMoniker.NetCoreApp22, "netcoreapp2.2" }; - yield return new object[] { Path.Combine(directoryPrefix, "3.0.0-preview8-28379-12") + Path.DirectorySeparatorChar, RuntimeMoniker.NetCoreApp30, "netcoreapp3.0" }; yield return new object[] { Path.Combine(directoryPrefix, "5.0.0-alpha1.19422.13") + Path.DirectorySeparatorChar, RuntimeMoniker.Net50, "net5.0" }; yield return new object[] { Path.Combine(directoryPrefix, "123.0.0") + Path.DirectorySeparatorChar, RuntimeMoniker.NotRecognized, "net123.0" }; } @@ -115,16 +108,12 @@ public void CurrentRuntimeIsProperlyRecognized() Assert.True(runtime is ClrRuntime); else Assert.True(runtime is MonoRuntime); -#elif NETCOREAPP2_1 - Assert.True(runtime is CoreRuntime coreRuntime && coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp21); -#elif NETCOREAPP2_2 - Assert.True(runtime is CoreRuntime coreRuntime && coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp22); -#elif NETCOREAPP3_0 - Assert.True(runtime is CoreRuntime coreRuntime && coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp30); #elif NETCOREAPP3_1 Assert.True(runtime is CoreRuntime coreRuntime && coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp31); #elif NETCOREAPP5_0 Assert.True(runtime is CoreRuntime coreRuntime && coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp50); +#elif NET8_0 + Assert.True(runtime is CoreRuntime coreRuntime && coreRuntime.RuntimeMoniker == RuntimeMoniker.Net80); #endif } } diff --git a/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs b/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs index cb2b1a3878..563513c6f6 100644 --- a/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs +++ b/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs @@ -7,6 +7,5 @@ public enum EnvRequirement NonLinux, FullFrameworkOnly, NonFullFramework, - DotNetCoreOnly, - DotNetCore30Only + DotNetCoreOnly } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs b/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs index e0510b92f6..80ea642d07 100644 --- a/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs +++ b/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs @@ -19,7 +19,6 @@ public static class EnvRequirementChecker EnvRequirement.FullFrameworkOnly => BdnRuntimeInformation.IsFullFramework ? null : "Full .NET Framework-only test", EnvRequirement.NonFullFramework => !BdnRuntimeInformation.IsFullFramework ? null : "Non-Full .NET Framework test", EnvRequirement.DotNetCoreOnly => BdnRuntimeInformation.IsNetCore ? null : ".NET/.NET Core-only test", - EnvRequirement.DotNetCore30Only => IsRuntime(RuntimeMoniker.NetCoreApp30) ? null : ".NET Core 3.0-only test", _ => throw new ArgumentOutOfRangeException(nameof(requirement), requirement, "Unknown value") }; diff --git a/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs b/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs index d3aec8dd96..1ef074112c 100644 --- a/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs +++ b/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using BenchmarkDotNet.Diagnostics.dotMemory; using BenchmarkDotNet.Jobs; using Xunit; @@ -12,6 +13,12 @@ public void AllRuntimeMonikerAreKnown() { var diagnoser = new DotMemoryDiagnoser(); foreach (RuntimeMoniker moniker in Enum.GetValues(typeof(RuntimeMoniker))) - diagnoser.IsSupported(moniker); // Just check that it doesn't throw exceptions + { + // Just check that it doesn't throw exceptions, ignoring deprecated values. + if (typeof(RuntimeMoniker).GetMember(moniker.ToString())[0].GetCustomAttribute() == null) + { + diagnoser.IsSupported(moniker); + } + } } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/dotTrace/DotTraceTests.cs b/tests/BenchmarkDotNet.Tests/dotTrace/DotTraceTests.cs index 751412fd55..f7788f874c 100644 --- a/tests/BenchmarkDotNet.Tests/dotTrace/DotTraceTests.cs +++ b/tests/BenchmarkDotNet.Tests/dotTrace/DotTraceTests.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using BenchmarkDotNet.Diagnostics.dotTrace; using BenchmarkDotNet.Jobs; using Xunit; @@ -12,6 +13,12 @@ public void AllRuntimeMonikerAreKnown() { var diagnoser = new DotTraceDiagnoser(); foreach (RuntimeMoniker moniker in Enum.GetValues(typeof(RuntimeMoniker))) - diagnoser.IsSupported(moniker); // Just check that it doesn't throw exceptions + { + // Just check that it doesn't throw exceptions, ignoring deprecated values. + if (typeof(RuntimeMoniker).GetMember(moniker.ToString())[0].GetCustomAttribute() == null) + { + diagnoser.IsSupported(moniker); + } + } } } \ No newline at end of file diff --git a/tests/runCoreTests.cmd b/tests/runCoreTests.cmd index 93c5c0f258..137ea952d9 100644 --- a/tests/runCoreTests.cmd +++ b/tests/runCoreTests.cmd @@ -12,24 +12,24 @@ if NOT %ERRORLEVEL% == 0 ( ) echo ----------------------------- -echo Running Core 2.1 Unit tests +echo Running Core 8.0 Unit tests echo ----------------------------- -call dotnet test "BenchmarkDotNet.Tests\BenchmarkDotNet.Tests.csproj" --configuration Release --framework netcoreapp2.1 +call dotnet test "BenchmarkDotNet.Tests\BenchmarkDotNet.Tests.csproj" --configuration Release --framework net8.0 if NOT %ERRORLEVEL% == 0 ( - echo CORE 2.1 Unit tests has failed + echo Core 8.0 Unit tests has failed goto end ) echo ----------------------------- -echo Running Core 2.1 Integration tests +echo Running 8.0 Integration tests echo ----------------------------- -call dotnet test "BenchmarkDotNet.IntegrationTests\BenchmarkDotNet.IntegrationTests.csproj" --configuration Release --framework netcoreapp2.1 +call dotnet test "BenchmarkDotNet.IntegrationTests\BenchmarkDotNet.IntegrationTests.csproj" --configuration Release --framework net8.0 if NOT %ERRORLEVEL% == 0 ( - echo CORE 2.1 Integration tests has failed + echo Core 8.0 Integration tests has failed goto end ) diff --git a/tests/runCoreTests.sh b/tests/runCoreTests.sh index 4e3ad59e6a..1b5bb28fa3 100755 --- a/tests/runCoreTests.sh +++ b/tests/runCoreTests.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -dotnet test BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj --configuration Release --framework netcoreapp2.1 2>&1 | tee tests.log -dotnet test BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj --configuration Release --framework netcoreapp2.1 2>&1 | tee integration-tests.log +dotnet test BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj --configuration Release --framework net8.0 2>&1 | tee tests.log +dotnet test BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj --configuration Release --framework net8.0 2>&1 | tee integration-tests.log From adf8e6d3e2ee2c7289622c7c269ce42a8a16ad3b Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:19:03 +0300 Subject: [PATCH 37/57] Add DOTNET_ environment vars (#2643) * Add DOTNET_ environment vars * Update tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs --------- Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> --- .../BenchmarkDotNet.Samples/IntroEnvVars.cs | 9 +++--- .../Extensions/ProcessExtensions.cs | 30 +++++++++++-------- .../MemoryDiagnoserTests.cs | 6 +++- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/samples/BenchmarkDotNet.Samples/IntroEnvVars.cs b/samples/BenchmarkDotNet.Samples/IntroEnvVars.cs index 822f73b6bc..66f5197119 100644 --- a/samples/BenchmarkDotNet.Samples/IntroEnvVars.cs +++ b/samples/BenchmarkDotNet.Samples/IntroEnvVars.cs @@ -10,13 +10,14 @@ public class IntroEnvVars { private class ConfigWithCustomEnvVars : ManualConfig { - private const string JitNoInline = "COMPlus_JitNoInline"; - public ConfigWithCustomEnvVars() { AddJob(Job.Default.WithRuntime(CoreRuntime.Core80).WithId("Inlining enabled")); AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) - .WithEnvironmentVariables(new EnvironmentVariable(JitNoInline, "1")) + .WithEnvironmentVariables([ + new EnvironmentVariable("DOTNET_JitNoInline", "1"), + new EnvironmentVariable("COMPlus_JitNoInline", "1") + ]) .WithId("Inlining disabled")); } } @@ -27,4 +28,4 @@ public void Foo() // Benchmark body } } -} \ No newline at end of file +} diff --git a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs index dd6bb5b55b..4d0854ba03 100644 --- a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs @@ -125,7 +125,7 @@ public static bool TrySetAffinity( internal static void SetEnvironmentVariables(this ProcessStartInfo start, BenchmarkCase benchmarkCase, IResolver resolver) { if (benchmarkCase.Job.Environment.Runtime is ClrRuntime clrRuntime && !string.IsNullOrEmpty(clrRuntime.Version)) - start.EnvironmentVariables["COMPLUS_Version"] = clrRuntime.Version; + SetClrEnvironmentVariables(start, "Version", clrRuntime.Version); if (benchmarkCase.Job.Environment.Runtime is MonoRuntime monoRuntime && !string.IsNullOrEmpty(monoRuntime.MonoBclPath)) start.EnvironmentVariables["MONO_PATH"] = monoRuntime.MonoBclPath; @@ -133,10 +133,10 @@ internal static void SetEnvironmentVariables(this ProcessStartInfo start, Benchm if (benchmarkCase.Config.HasPerfCollectProfiler()) { // enable tracing configuration inside of CoreCLR (https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md#collecting-a-trace) - start.EnvironmentVariables["COMPlus_PerfMapEnabled"] = "1"; - start.EnvironmentVariables["COMPlus_EnableEventLog"] = "1"; + SetClrEnvironmentVariables(start, "PerfMapEnabled", "1"); + SetClrEnvironmentVariables(start, "EnableEventLog", "1"); // enable BDN Event Source (https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md#filtering) - start.EnvironmentVariables["COMPlus_EventSourceFilter"] = EngineEventSource.SourceName; + SetClrEnvironmentVariables(start, "EventSourceFilter", EngineEventSource.SourceName); // workaround for https://github.com/dotnet/runtime/issues/71786, will be solved by next perf version start.EnvironmentVariables["DOTNET_EnableWriteXorExecute"] = "0"; } @@ -258,21 +258,27 @@ private static void SetCoreRunEnvironmentVariables(this ProcessStartInfo start, { var gcMode = benchmarkCase.Job.Environment.Gc; - start.EnvironmentVariables["COMPlus_gcServer"] = gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver) ? "1" : "0"; - start.EnvironmentVariables["COMPlus_gcConcurrent"] = gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver) ? "1" : "0"; + SetClrEnvironmentVariables(start, "gcServer", gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver) ? "1" : "0"); + SetClrEnvironmentVariables(start, "gcConcurrent", gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver) ? "1" : "0"); if (gcMode.HasValue(GcMode.CpuGroupsCharacteristic)) - start.EnvironmentVariables["COMPlus_GCCpuGroup"] = gcMode.ResolveValue(GcMode.CpuGroupsCharacteristic, resolver) ? "1" : "0"; + SetClrEnvironmentVariables(start, "GCCpuGroup", gcMode.ResolveValue(GcMode.CpuGroupsCharacteristic, resolver) ? "1" : "0"); if (gcMode.HasValue(GcMode.AllowVeryLargeObjectsCharacteristic)) - start.EnvironmentVariables["COMPlus_gcAllowVeryLargeObjects"] = gcMode.ResolveValue(GcMode.AllowVeryLargeObjectsCharacteristic, resolver) ? "1" : "0"; + SetClrEnvironmentVariables(start, "gcAllowVeryLargeObjects", gcMode.ResolveValue(GcMode.AllowVeryLargeObjectsCharacteristic, resolver) ? "1" : "0"); if (gcMode.HasValue(GcMode.RetainVmCharacteristic)) - start.EnvironmentVariables["COMPlus_GCRetainVM"] = gcMode.ResolveValue(GcMode.RetainVmCharacteristic, resolver) ? "1" : "0"; + SetClrEnvironmentVariables(start, "GCRetainVM", gcMode.ResolveValue(GcMode.RetainVmCharacteristic, resolver) ? "1" : "0"); if (gcMode.HasValue(GcMode.NoAffinitizeCharacteristic)) - start.EnvironmentVariables["COMPlus_GCNoAffinitize"] = gcMode.ResolveValue(GcMode.NoAffinitizeCharacteristic, resolver) ? "1" : "0"; + SetClrEnvironmentVariables(start, "GCNoAffinitize", gcMode.ResolveValue(GcMode.NoAffinitizeCharacteristic, resolver) ? "1" : "0"); if (gcMode.HasValue(GcMode.HeapAffinitizeMaskCharacteristic)) - start.EnvironmentVariables["COMPlus_GCHeapAffinitizeMask"] = gcMode.HeapAffinitizeMask.ToString("X"); + SetClrEnvironmentVariables(start, "GCHeapAffinitizeMask", gcMode.HeapAffinitizeMask.ToString("X")); if (gcMode.HasValue(GcMode.HeapCountCharacteristic)) - start.EnvironmentVariables["COMPlus_GCHeapCount"] = gcMode.HeapCount.ToString("X"); + SetClrEnvironmentVariables(start, "GCHeapCount", gcMode.HeapCount.ToString("X")); + } + + private static void SetClrEnvironmentVariables(ProcessStartInfo start, string suffix, string value) + { + start.EnvironmentVariables[$"DOTNET_{suffix}"] = value; + start.EnvironmentVariables[$"COMPlus_{suffix}"] = value; } } } diff --git a/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs b/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs index b805af6d26..603926c2a6 100755 --- a/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs @@ -321,7 +321,11 @@ private IConfig CreateConfig(IToolchain toolchain) .WithGcForce(false) .WithGcServer(false) .WithGcConcurrent(false) - .WithEnvironmentVariable("COMPlus_TieredCompilation", "0") // Tiered JIT can allocate some memory on a background thread, let's disable it to make our tests less flaky (#1542) + .WithEnvironmentVariables([ + // Tiered JIT can allocate some memory on a background thread, let's disable it to make our tests less flaky (#1542) + new EnvironmentVariable("DOTNET_TieredCompilation", "0"), + new EnvironmentVariable("COMPlus_TieredCompilation", "0") + ]) .WithToolchain(toolchain)) .AddColumnProvider(DefaultColumnProviders.Instance) .AddDiagnoser(MemoryDiagnoser.Default) From 5fe0c78539fe5664477649e6e3adc7cb4e97df5f Mon Sep 17 00:00:00 2001 From: Cameron Aavik <99771732+caaavik-msft@users.noreply.github.com> Date: Tue, 24 Sep 2024 04:48:30 +1000 Subject: [PATCH 38/57] Change .NET SDK Validator to account for backwards compatibility (#2645) --- .../Validators/DotNetSdkVersionValidator.cs | 117 ++++++++++-------- 1 file changed, 66 insertions(+), 51 deletions(-) diff --git a/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs b/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs index 93a3af80dd..df2c9253ea 100644 --- a/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs +++ b/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs @@ -21,34 +21,28 @@ public static IEnumerable ValidateCoreSdks(string? customDotNet { yield return cliPathError; } - else if (TryGetSdkVersion(benchmark, out string requiredSdkVersion)) + else if (TryGetSdkVersion(benchmark, out Version requiredSdkVersion)) { var installedSdks = GetInstalledDotNetSdks(customDotNetCliPath); - if (!installedSdks.Any(sdk => sdk.StartsWith(requiredSdkVersion))) + if (!installedSdks.Any(sdk => sdk >= requiredSdkVersion)) { - yield return new ValidationError(true, $"The required .NET Core SDK version {requiredSdkVersion} for runtime moniker {benchmark.Job.Environment.Runtime.RuntimeMoniker} is not installed.", benchmark); + yield return new ValidationError(true, $"The required .NET Core SDK version {requiredSdkVersion} or higher for runtime moniker {benchmark.Job.Environment.Runtime.RuntimeMoniker} is not installed.", benchmark); } } } public static IEnumerable ValidateFrameworkSdks(BenchmarkCase benchmark) { - if (!TryGetSdkVersion(benchmark, out string requiredSdkVersionString)) + if (!TryGetSdkVersion(benchmark, out Version requiredSdkVersion)) { yield break; } - if (!Version.TryParse(requiredSdkVersionString, out var requiredSdkVersion)) - { - yield return new ValidationError(true, $"Invalid .NET Framework SDK version format: {requiredSdkVersionString}", benchmark); - yield break; - } - var installedVersionString = cachedFrameworkSdks.Value.FirstOrDefault(); if (installedVersionString == null || Version.TryParse(installedVersionString, out var installedVersion) && installedVersion < requiredSdkVersion) { - yield return new ValidationError(true, $"The required .NET Framework SDK version {requiredSdkVersionString} or higher is not installed.", benchmark); + yield return new ValidationError(true, $"The required .NET Framework SDK version {requiredSdkVersion} or higher is not installed.", benchmark); } } @@ -77,9 +71,9 @@ public static bool IsCliPathInvalid(string customDotNetCliPath, BenchmarkCase be return false; } - private static bool TryGetSdkVersion(BenchmarkCase benchmark, out string sdkVersion) + private static bool TryGetSdkVersion(BenchmarkCase benchmark, out Version sdkVersion) { - sdkVersion = string.Empty; + sdkVersion = default; if (benchmark?.Job?.Environment?.Runtime?.RuntimeMoniker != null) { sdkVersion = GetSdkVersionFromMoniker(benchmark.Job.Environment.Runtime.RuntimeMoniker); @@ -88,7 +82,7 @@ private static bool TryGetSdkVersion(BenchmarkCase benchmark, out string sdkVers return false; } - private static IEnumerable GetInstalledDotNetSdks(string? customDotNetCliPath) + private static IEnumerable GetInstalledDotNetSdks(string? customDotNetCliPath) { string dotnetExecutable = string.IsNullOrEmpty(customDotNetCliPath) ? "dotnet" : customDotNetCliPath; var startInfo = new ProcessStartInfo(dotnetExecutable, "--list-sdks") @@ -104,7 +98,7 @@ private static IEnumerable GetInstalledDotNetSdks(string? customDotNetCl { if (process == null) { - return Enumerable.Empty(); + return Enumerable.Empty(); } process.WaitForExit(); @@ -113,17 +107,38 @@ private static IEnumerable GetInstalledDotNetSdks(string? customDotNetCl { var output = process.StandardOutput.ReadToEnd(); var lines = output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - return lines.Select(line => line.Split(' ')[0]); // The SDK version is the first part of each line. + + var versions = new List(lines.Count()); + foreach (var line in lines) + { + // Each line will start with the SDK version followed by the SDK path. Since we only support + // targeting SDK versions by the major version, we'll just look at extracting the major and + // minor versions. This is done by looking for the first two dots in the line. + var firstDot = line.IndexOf('.'); + if (firstDot < 0) + continue; + + var secondDot = line.IndexOf('.', firstDot + 1); + if (secondDot < 0) + continue; + + if (Version.TryParse(line.Substring(0, secondDot), out var version)) + { + versions.Add(version); + } + } + + return versions; } else { - return Enumerable.Empty(); + return Enumerable.Empty(); } } } catch (Win32Exception) // dotnet CLI is not installed or not found in the path. { - return Enumerable.Empty(); + return Enumerable.Empty(); } } @@ -193,42 +208,42 @@ private static string CheckFor45PlusVersion(int releaseKey) return ""; } - private static string GetSdkVersionFromMoniker(RuntimeMoniker runtimeMoniker) + private static Version GetSdkVersionFromMoniker(RuntimeMoniker runtimeMoniker) { return runtimeMoniker switch { - RuntimeMoniker.Net461 => "4.6.1", - RuntimeMoniker.Net462 => "4.6.2", - RuntimeMoniker.Net47 => "4.7", - RuntimeMoniker.Net471 => "4.7.1", - RuntimeMoniker.Net472 => "4.7.2", - RuntimeMoniker.Net48 => "4.8", - RuntimeMoniker.Net481 => "4.8.1", - RuntimeMoniker.NetCoreApp31 => "3.1", - RuntimeMoniker.Net50 => "5.0", - RuntimeMoniker.Net60 => "6.0", - RuntimeMoniker.Net70 => "7.0", - RuntimeMoniker.Net80 => "8.0", - RuntimeMoniker.Net90 => "9.0", - RuntimeMoniker.NativeAot60 => "6.0", - RuntimeMoniker.NativeAot70 => "7.0", - RuntimeMoniker.NativeAot80 => "8.0", - RuntimeMoniker.NativeAot90 => "9.0", - RuntimeMoniker.Mono60 => "6.0", - RuntimeMoniker.Mono70 => "7.0", - RuntimeMoniker.Mono80 => "8.0", - RuntimeMoniker.Mono90 => "9.0", - RuntimeMoniker.Wasm => Portability.RuntimeInformation.IsNetCore && CoreRuntime.TryGetVersion(out var version) ? $"{version.Major}.{version.Minor}" : "5.0", - RuntimeMoniker.WasmNet50 => "5.0", - RuntimeMoniker.WasmNet60 => "6.0", - RuntimeMoniker.WasmNet70 => "7.0", - RuntimeMoniker.WasmNet80 => "8.0", - RuntimeMoniker.WasmNet90 => "9.0", - RuntimeMoniker.MonoAOTLLVM => Portability.RuntimeInformation.IsNetCore && CoreRuntime.TryGetVersion(out var version) ? $"{version.Major}.{version.Minor}" : "6.0", - RuntimeMoniker.MonoAOTLLVMNet60 => "6.0", - RuntimeMoniker.MonoAOTLLVMNet70 => "7.0", - RuntimeMoniker.MonoAOTLLVMNet80 => "8.0", - RuntimeMoniker.MonoAOTLLVMNet90 => "9.0", + RuntimeMoniker.Net461 => new Version(4, 6, 1), + RuntimeMoniker.Net462 => new Version(4, 6, 2), + RuntimeMoniker.Net47 => new Version(4, 7), + RuntimeMoniker.Net471 => new Version(4, 7, 1), + RuntimeMoniker.Net472 => new Version(4, 7, 2), + RuntimeMoniker.Net48 => new Version(4, 8), + RuntimeMoniker.Net481 => new Version(4, 8, 1), + RuntimeMoniker.NetCoreApp31 => new Version(3, 1), + RuntimeMoniker.Net50 => new Version(5, 0), + RuntimeMoniker.Net60 => new Version(6, 0), + RuntimeMoniker.Net70 => new Version(7, 0), + RuntimeMoniker.Net80 => new Version(8, 0), + RuntimeMoniker.Net90 => new Version(9, 0), + RuntimeMoniker.NativeAot60 => new Version(6, 0), + RuntimeMoniker.NativeAot70 => new Version(7, 0), + RuntimeMoniker.NativeAot80 => new Version(8, 0), + RuntimeMoniker.NativeAot90 => new Version(9, 0), + RuntimeMoniker.Mono60 => new Version(6, 0), + RuntimeMoniker.Mono70 => new Version(7, 0), + RuntimeMoniker.Mono80 => new Version(8, 0), + RuntimeMoniker.Mono90 => new Version(9, 0), + RuntimeMoniker.Wasm => Portability.RuntimeInformation.IsNetCore && CoreRuntime.TryGetVersion(out var version) ? version : new Version(5, 0), + RuntimeMoniker.WasmNet50 => new Version(5, 0), + RuntimeMoniker.WasmNet60 => new Version(6, 0), + RuntimeMoniker.WasmNet70 => new Version(7, 0), + RuntimeMoniker.WasmNet80 => new Version(8, 0), + RuntimeMoniker.WasmNet90 => new Version(9, 0), + RuntimeMoniker.MonoAOTLLVM => Portability.RuntimeInformation.IsNetCore && CoreRuntime.TryGetVersion(out var version) ? version : new Version(6, 0), + RuntimeMoniker.MonoAOTLLVMNet60 => new Version(6, 0), + RuntimeMoniker.MonoAOTLLVMNet70 => new Version(7, 0), + RuntimeMoniker.MonoAOTLLVMNet80 => new Version(8, 0), + RuntimeMoniker.MonoAOTLLVMNet90 => new Version(9, 0), _ => throw new NotImplementedException($"SDK version check not implemented for {runtimeMoniker}") }; } From 52485ecf56dc6933691b368d8645dea947b38eb2 Mon Sep 17 00:00:00 2001 From: Cameron Aavik <99771732+caaavik-msft@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:01:43 +1000 Subject: [PATCH 39/57] Get full parsable version part (#2646) --- .../Environments/Runtimes/CoreRuntime.cs | 2 +- .../Validators/DotNetSdkVersionValidator.cs | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs index 8e4c508285..335bd1a871 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs @@ -194,7 +194,7 @@ internal static bool TryGetVersionFromFrameworkName(string frameworkName, out Ve } // Version.TryParse does not handle thing like 3.0.0-WORD - private static string GetParsableVersionPart(string fullVersionName) => new string(fullVersionName.TakeWhile(c => char.IsDigit(c) || c == '.').ToArray()); + internal static string GetParsableVersionPart(string fullVersionName) => new string(fullVersionName.TakeWhile(c => char.IsDigit(c) || c == '.').ToArray()); private static CoreRuntime GetPlatformSpecific(CoreRuntime fallback) { diff --git a/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs b/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs index df2c9253ea..65474451c0 100644 --- a/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs +++ b/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs @@ -111,18 +111,9 @@ private static IEnumerable GetInstalledDotNetSdks(string? customDotNetC var versions = new List(lines.Count()); foreach (var line in lines) { - // Each line will start with the SDK version followed by the SDK path. Since we only support - // targeting SDK versions by the major version, we'll just look at extracting the major and - // minor versions. This is done by looking for the first two dots in the line. - var firstDot = line.IndexOf('.'); - if (firstDot < 0) - continue; - - var secondDot = line.IndexOf('.', firstDot + 1); - if (secondDot < 0) - continue; - - if (Version.TryParse(line.Substring(0, secondDot), out var version)) + // Version.TryParse does not handle things like 3.0.0-WORD, so this will get just the 3.0.0 part + var parsableVersionPart = CoreRuntime.GetParsableVersionPart(line); + if (Version.TryParse(parsableVersionPart, out var version)) { versions.Add(version); } From 9040e40187f2bbecea4aec724f995fde378f608b Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 25 Sep 2024 16:33:53 +0200 Subject: [PATCH 40/57] add RiscV64 support, fixes #2644 (#2647) --- src/BenchmarkDotNet/Environments/Platform.cs | 5 +++++ src/BenchmarkDotNet/Portability/RuntimeInformation.cs | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Environments/Platform.cs b/src/BenchmarkDotNet/Environments/Platform.cs index bc8f1ad26b..434a84ff98 100644 --- a/src/BenchmarkDotNet/Environments/Platform.cs +++ b/src/BenchmarkDotNet/Environments/Platform.cs @@ -51,5 +51,10 @@ public enum Platform /// A PowerPC 64-bit (little-endian) processor architecture. /// Ppc64le, + + /// + /// A RiscV 64-bit processor architecture. + /// + RiscV64, } } diff --git a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs index ed4d457caa..3c0fe30149 100644 --- a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs +++ b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs @@ -207,12 +207,13 @@ internal static Runtime GetCurrentRuntime() public static Platform GetCurrentPlatform() { // these are not part of .NET Standard 2.0, so we use hardcoded values taken from - // https://github.com/dotnet/runtime/blob/1a37caf773a3b857ccff49a31be3333d4fdc491f/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Architecture.cs#L9 + // https://github.com/dotnet/runtime/blob/080fcae7eaa8367abf7900e08ff2e52e3efea5bf/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Architecture.cs#L9 const Architecture Wasm = (Architecture)4; const Architecture S390x = (Architecture)5; const Architecture LoongArch64 = (Architecture)6; const Architecture Armv6 = (Architecture)7; const Architecture Ppc64le = (Architecture)8; + const Architecture RiscV64 = (Architecture)9; switch (ProcessArchitecture) { @@ -234,6 +235,8 @@ public static Platform GetCurrentPlatform() return Platform.Armv6; case Ppc64le: return Platform.Ppc64le; + case RiscV64: + return Platform.RiscV64; default: throw new ArgumentOutOfRangeException(); } From 9ff49c679575b25079ad6bead72cb73d0e2bf8f5 Mon Sep 17 00:00:00 2001 From: Ketan Pramod Kolte <75748186+ketanpkolte@users.noreply.github.com> Date: Fri, 18 Oct 2024 22:42:36 +0530 Subject: [PATCH 41/57] Fixed GitHub workflow report for failed tests (#2653) * changed target framework to .net 8.0 * Added a failing test * Update run-tests.yaml * try again * changed yaml files * Revert "try again" This reverts commit 1995f60de5d266087e6c88ad31f3b655ec7277ae. * Revert "Added a failing test" This reverts commit 1bcb1a19029d574191436c4627c8449b8b18f525. * Final yaml files * added target framework back for PR --- .github/workflows/report-test-results.yaml | 14 +++++++++++++- .github/workflows/run-tests.yaml | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/.github/workflows/report-test-results.yaml b/.github/workflows/report-test-results.yaml index 005465b329..2e54b831ee 100644 --- a/.github/workflows/report-test-results.yaml +++ b/.github/workflows/report-test-results.yaml @@ -12,12 +12,24 @@ jobs: runs-on: ubuntu-latest permissions: write-all steps: + # Cleanup Old Files + - name: Cleanup Old Files + run: rm -rf $GITHUB_WORKSPACE/*.trx + + # Download the Latest Artifacts with Unique Name - name: Download Artifacts uses: dawidd6/action-download-artifact@v2 with: - workflow: ${{ github.event.workflow_run.workflow_id }} + run_id: ${{ github.event.workflow_run.id }} + + # Display the Structure of Downloaded Files - name: Display structure of downloaded files run: ls -R + + # Display the Contents of .trx Files + - name: Display .trx file contents + run: cat **/*.trx || echo "No .trx files found" + - name: Report tests results uses: AndreyAkinshin/test-reporter@0e2c48ebec2007001dd77dd4bcbcd450b96d5a38 with: diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 698b429514..f42f2596a5 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -18,17 +18,19 @@ jobs: run: Set-MpPreference -DisableRealtimeMonitoring $true shell: powershell - uses: actions/checkout@v4 + # Build and Test - name: Run task 'build' shell: cmd run: ./build.cmd build - name: Run task 'in-tests-core' shell: cmd run: ./build.cmd in-tests-core -e + # Upload Artifacts with Unique Name - name: Upload test results uses: actions/upload-artifact@v3 if: always() with: - name: test-windows-core-trx + name: test-windows-core-trx-${{ github.run_id }} path: "**/*.trx" test-windows-full: @@ -38,23 +40,26 @@ jobs: run: Set-MpPreference -DisableRealtimeMonitoring $true shell: powershell - uses: actions/checkout@v4 + # Build and Test - name: Run task 'build' shell: cmd run: ./build.cmd build - name: Run task 'in-tests-full' shell: cmd run: ./build.cmd in-tests-full -e + # Upload Artifacts with Unique Name - name: Upload test results uses: actions/upload-artifact@v4 if: always() with: - name: test-windows-full-trx + name: test-windows-full-trx-${{ github.run_id }} path: "**/*.trx" test-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + # Set up the environment - name: Set up Clang uses: egor-tensin/setup-clang@v1 with: @@ -70,17 +75,19 @@ jobs: run: npm install jsvu -g && jsvu --os=linux64 --engines=v8 && echo "$HOME/.jsvu/bin" >> $GITHUB_PATH - name: Install wasm-tools workload run: ./build.cmd install-wasm-tools + # Build and Test - name: Run task 'build' run: ./build.cmd build - name: Run task 'unit-tests' run: ./build.cmd unit-tests -e - name: Run task 'in-tests-core' run: ./build.cmd in-tests-core -e + # Upload Artifacts with Unique Name - name: Upload test results uses: actions/upload-artifact@v4 if: always() with: - name: test-linux-trx + name: test-linux-trx-${{ github.run_id }} path: "**/*.trx" test-macos: @@ -95,17 +102,19 @@ jobs: run: npm install jsvu -g && jsvu --os=mac64 --engines=v8 && echo "$HOME/.jsvu/bin" >> $GITHUB_PATH - name: Install wasm-tools workload run: ./build.cmd install-wasm-tools + # Build and Test - name: Run task 'build' run: ./build.cmd build - name: Run task 'unit-tests' run: ./build.cmd unit-tests -e - name: Run task 'in-tests-core' run: ./build.cmd in-tests-core -e + # Upload Artifacts with Unique Name - name: Upload test results uses: actions/upload-artifact@v4 if: always() with: - name: test-macos-trx + name: test-macos-trx-${{ github.run_id }} path: "**/*.trx" test-pack: From 1f7cab13fc0f09d27f6311eee17b6a1cfed48374 Mon Sep 17 00:00:00 2001 From: Saipavan Lingamallu <123063767+5AIPAVAN@users.noreply.github.com> Date: Mon, 21 Oct 2024 02:43:12 +0530 Subject: [PATCH 42/57] Update console-args.md (#2657) Replaced ilcPath with ilcPackages --- docs/articles/guides/console-args.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/guides/console-args.md b/docs/articles/guides/console-args.md index 032cc81da6..31b54a9137 100644 --- a/docs/articles/guides/console-args.md +++ b/docs/articles/guides/console-args.md @@ -121,7 +121,7 @@ The `--runtimes` or just `-r` allows you to run the benchmarks for selected Runt * Mono - it's going to use the Mono from `$Path`, you can override it with `--monoPath`. * net46, net461, net462, net47, net471, net472, net48, net481 - to build and run benchmarks against specific .NET Framework version. * netcoreapp3.1, net5.0, net6.0, net7.0, net8.0 - to build and run benchmarks against specific .NET (Core) version. -* nativeaot5.0, nativeaot6.0, nativeaot7.0, nativeaot8.0 - to build and run benchmarks using NativeAOT. Can be customized with additional options: `--ilcPath`, `--ilCompilerVersion`. +* nativeaot5.0, nativeaot6.0, nativeaot7.0, nativeaot8.0 - to build and run benchmarks using NativeAOT. Can be customized with additional options: `--ilcPackages`, `--ilCompilerVersion`. * mono6.0, mono7.0, mono8.0 - to build and run benchmarks with .Net 6+ using MonoVM. Example: run the benchmarks for .NET 4.7.2 and .NET 8.0: @@ -229,7 +229,7 @@ dotnet run -c Release -- --filter * --runtimes net6.0 net8.0 --statisticalTest 5 * `--cli` path to dotnet cli (optional). * `--packages` the directory to restore packages to (optional). * `--coreRun` path(s) to CoreRun (optional). -* `--ilcPath` path to ILCompiler for NativeAOT. +* `--ilcPackages` path to ILCompiler for NativeAOT. * `--info` prints environment configuration including BenchmarkDotNet, OS, CPU and .NET version * `--stopOnFirstError` stop on first error. * `--help` display this help screen. From 6248e85435eba393ef26c11bf4fa7bd0795925be Mon Sep 17 00:00:00 2001 From: Vishnu Bhagyanath Date: Mon, 21 Oct 2024 02:45:10 +0530 Subject: [PATCH 43/57] improve baseline warning message (#2650) --- src/BenchmarkDotNet/Analysers/BaselineCustomAnalyzer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Analysers/BaselineCustomAnalyzer.cs b/src/BenchmarkDotNet/Analysers/BaselineCustomAnalyzer.cs index 300d520f15..9f2ff0028c 100644 --- a/src/BenchmarkDotNet/Analysers/BaselineCustomAnalyzer.cs +++ b/src/BenchmarkDotNet/Analysers/BaselineCustomAnalyzer.cs @@ -31,7 +31,8 @@ protected override IEnumerable AnalyseSummary(Summary summary) continue; var message = "A question mark '?' symbol indicates that it was not possible to compute the " + - $"({columnNames}) column(s) because the baseline value is too close to zero."; + $"({columnNames}) column(s) because the baseline or benchmark could not be found, or " + + $"the baseline value is too close to zero."; yield return Conclusion.CreateWarning(Id, message); } From af8bde44bc1b108444bdea9ee61c2975b3a2dffd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 21 Oct 2024 23:15:08 +0200 Subject: [PATCH 44/57] Fix known high severity vulnerabilities (#2613) * workaround known high severity vulnerabilities * Reverted added dependencies. Updated dependencies to fix vulnerabilities. Removed netstandard1.0 target from Annotations. --------- Co-authored-by: Tim --- .../BenchmarkDotNet.Samples.FSharp.fsproj | 2 +- .../BenchmarkDotNet.Annotations.csproj | 2 +- ...nchmarkDotNet.Diagnostics.dotMemory.csproj | 2 +- ...enchmarkDotNet.Diagnostics.dotTrace.csproj | 2 +- ...markDotNet.Exporters.Plotting.Tests.csproj | 4 +-- ...ts.ManualRunning.MultipleFrameworks.csproj | 2 +- ...tNet.IntegrationTests.ManualRunning.csproj | 6 ++-- .../BenchmarkDotNet.IntegrationTests.csproj | 4 +-- .../BenchmarkDotNet.Tests.csproj | 12 +++---- .../ConfigParserTests.cs | 32 +++++++++---------- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/samples/BenchmarkDotNet.Samples.FSharp/BenchmarkDotNet.Samples.FSharp.fsproj b/samples/BenchmarkDotNet.Samples.FSharp/BenchmarkDotNet.Samples.FSharp.fsproj index 2ae53f94d8..457b660115 100644 --- a/samples/BenchmarkDotNet.Samples.FSharp/BenchmarkDotNet.Samples.FSharp.fsproj +++ b/samples/BenchmarkDotNet.Samples.FSharp/BenchmarkDotNet.Samples.FSharp.fsproj @@ -13,7 +13,7 @@ - + diff --git a/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj b/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj index 2e7361e163..ccc51bcd9a 100644 --- a/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj +++ b/src/BenchmarkDotNet.Annotations/BenchmarkDotNet.Annotations.csproj @@ -2,7 +2,7 @@ Basic BenchmarkDotNet attributes that can be used to annotate your benchmarks - netstandard1.0;netstandard2.0 + netstandard2.0 $(NoWarn);1701;1702;1705;1591;3005;NU1702;CA1825 BenchmarkDotNet.Annotations BenchmarkDotNet.Annotations diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj index 414d927c0d..baf4c0383e 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj b/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj index 4ea24eeb04..42c838e20f 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/BenchmarkDotNet.Diagnostics.dotTrace.csproj @@ -14,7 +14,7 @@ - + diff --git a/tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj index 0c61fd72af..cb7dd76de2 100644 --- a/tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj +++ b/tests/BenchmarkDotNet.Exporters.Plotting.Tests/BenchmarkDotNet.Exporters.Plotting.Tests.csproj @@ -17,8 +17,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj index 13377c7765..282009baad 100644 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj @@ -39,7 +39,7 @@ - + all diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/BenchmarkDotNet.IntegrationTests.ManualRunning.csproj b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/BenchmarkDotNet.IntegrationTests.ManualRunning.csproj index b3fef215b4..4d8d86600f 100755 --- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/BenchmarkDotNet.IntegrationTests.ManualRunning.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning/BenchmarkDotNet.IntegrationTests.ManualRunning.csproj @@ -1,4 +1,4 @@ - + BenchmarkDotNet.IntegrationTests.ManualRunning @@ -35,8 +35,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj index a928af1424..16f12c41b4 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj @@ -35,8 +35,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj b/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj index d0011e1d64..000a27ca2c 100755 --- a/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj +++ b/tests/BenchmarkDotNet.Tests/BenchmarkDotNet.Tests.csproj @@ -1,5 +1,5 @@  - + BenchmarkDotNet.Tests net8.0;net462 @@ -17,9 +17,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -40,10 +40,10 @@ - + - + diff --git a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs index 73d29d3611..0e7c439ddf 100644 --- a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs @@ -233,8 +233,8 @@ public void UserCanSpecifyMultipleCoreRunPaths() var jobs = config.GetJobs().ToArray(); Assert.Equal(2, jobs.Length); - Assert.Single(jobs.Where(job => job.GetToolchain() is CoreRunToolchain toolchain && toolchain.SourceCoreRun.FullName == fakeCoreRunPath_1)); - Assert.Single(jobs.Where(job => job.GetToolchain() is CoreRunToolchain toolchain && toolchain.SourceCoreRun.FullName == fakeCoreRunPath_2)); + Assert.Single(jobs, job => job.GetToolchain() is CoreRunToolchain toolchain && toolchain.SourceCoreRun.FullName == fakeCoreRunPath_1); + Assert.Single(jobs, job => job.GetToolchain() is CoreRunToolchain toolchain && toolchain.SourceCoreRun.FullName == fakeCoreRunPath_2); } [Fact] @@ -244,7 +244,7 @@ public void MonoPathParsedCorrectly() var config = ConfigParser.Parse(new[] { "-r", "mono", "--monoPath", fakeMonoPath }, new OutputLogger(Output)).config; Assert.Single(config.GetJobs()); - Assert.Single(config.GetJobs().Where(job => job.Environment.Runtime is MonoRuntime mono && mono.CustomPath == fakeMonoPath)); + Assert.Single(config.GetJobs(), job => job.Environment.Runtime is MonoRuntime mono && mono.CustomPath == fakeMonoPath); } [FactEnvSpecific("Testing local builds of Full .NET Framework is supported only on Windows", EnvRequirement.WindowsOnly)] @@ -254,7 +254,7 @@ public void ClrVersionParsedCorrectly() var config = ConfigParser.Parse(new[] { "--clrVersion", clrVersion }, new OutputLogger(Output)).config; Assert.Single(config.GetJobs()); - Assert.Single(config.GetJobs().Where(job => job.Environment.Runtime is ClrRuntime clr && clr.Version == clrVersion)); + Assert.Single(config.GetJobs(), job => job.Environment.Runtime is ClrRuntime clr && clr.Version == clrVersion); } [Fact] @@ -418,20 +418,20 @@ public void CanCompareFewDifferentRuntimes() new OutputLogger(Output)).config; Assert.True(config.GetJobs().First().Meta.Baseline); // when the user provides multiple runtimes the first one should be marked as baseline - Assert.Single(config.GetJobs().Where(job => job.Environment.Runtime is ClrRuntime clrRuntime && clrRuntime.MsBuildMoniker == "net462")); - Assert.Single(config.GetJobs().Where(job => job.Environment.Runtime is MonoRuntime)); - Assert.Single(config.GetJobs().Where(job => + Assert.Single(config.GetJobs(), job => job.Environment.Runtime is ClrRuntime clrRuntime && clrRuntime.MsBuildMoniker == "net462"); + Assert.Single(config.GetJobs(), job => job.Environment.Runtime is MonoRuntime); + Assert.Single(config.GetJobs(), job => job.Environment.Runtime is CoreRuntime coreRuntime && coreRuntime.MsBuildMoniker == "netcoreapp3.1" && - coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp31)); - Assert.Single(config.GetJobs().Where(job => + coreRuntime.RuntimeMoniker == RuntimeMoniker.NetCoreApp31); + Assert.Single(config.GetJobs(), job => job.Environment.Runtime is NativeAotRuntime nativeAot && nativeAot.MsBuildMoniker == "net6.0" && - nativeAot.RuntimeMoniker == RuntimeMoniker.NativeAot60)); - Assert.Single(config.GetJobs().Where(job => + nativeAot.RuntimeMoniker == RuntimeMoniker.NativeAot60); + Assert.Single(config.GetJobs(), job => job.Environment.Runtime is NativeAotRuntime nativeAot && nativeAot.MsBuildMoniker == "net7.0" && - nativeAot.RuntimeMoniker == RuntimeMoniker.NativeAot70)); - Assert.Single(config.GetJobs().Where(job => + nativeAot.RuntimeMoniker == RuntimeMoniker.NativeAot70); + Assert.Single(config.GetJobs(), job => job.Environment.Runtime is NativeAotRuntime nativeAot && nativeAot.MsBuildMoniker == "net8.0" && - nativeAot.RuntimeMoniker == RuntimeMoniker.NativeAot80)); + nativeAot.RuntimeMoniker == RuntimeMoniker.NativeAot80); } [Theory] @@ -469,8 +469,8 @@ public void CanParseHardwareCounters() new OutputLogger(Output)).config; Assert.Equal(2, config.GetHardwareCounters().Count()); - Assert.Single(config.GetHardwareCounters().Where(counter => counter == HardwareCounter.CacheMisses)); - Assert.Single(config.GetHardwareCounters().Where(counter => counter == HardwareCounter.InstructionRetired)); + Assert.Single(config.GetHardwareCounters(), counter => counter == HardwareCounter.CacheMisses); + Assert.Single(config.GetHardwareCounters(), counter => counter == HardwareCounter.InstructionRetired); } [Fact] From 346bbab62a508fbce8179965ba05452e7a361367 Mon Sep 17 00:00:00 2001 From: Ketan Pramod Kolte <75748186+ketanpkolte@users.noreply.github.com> Date: Sun, 27 Oct 2024 11:33:51 +0530 Subject: [PATCH 45/57] Fixed: Add validation warning for sealed classes containing benchmarks (#2660) * Add validation for sealed classes containing benchmarks * Moved public class validation to CompilationValidator Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> --- .../Extensions/ReflectionExtensions.cs | 5 +- .../Running/BenchmarkConverter.cs | 11 +--- .../Validators/CompilationValidator.cs | 33 +++++++++- .../Validators/CompilationValidatorTests.cs | 61 ++++++++++++++++++- 4 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs b/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs index cf1f71a166..0a1a7c22fc 100644 --- a/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs @@ -155,10 +155,7 @@ internal static bool ContainsRunnableBenchmarks(this Type type) { var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsAbstract - || typeInfo.IsSealed - || typeInfo.IsNotPublic - || typeInfo.IsGenericType && !IsRunnableGenericType(typeInfo)) + if (typeInfo.IsAbstract || typeInfo.IsGenericType && !IsRunnableGenericType(typeInfo)) return false; return typeInfo.GetBenchmarks().Any(); diff --git a/src/BenchmarkDotNet/Running/BenchmarkConverter.cs b/src/BenchmarkDotNet/Running/BenchmarkConverter.cs index dbe350b2c8..69837feec8 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkConverter.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkConverter.cs @@ -263,16 +263,7 @@ private static void AssertMethodIsAccessible(string methodType, MethodInfo metho { if (!methodInfo.IsPublic) throw new InvalidBenchmarkDeclarationException($"{methodType} method {methodInfo.Name} has incorrect access modifiers.\nMethod must be public."); - - var declaringType = methodInfo.DeclaringType; - - while (declaringType != null) - { - if (!declaringType.GetTypeInfo().IsPublic && !declaringType.GetTypeInfo().IsNestedPublic) - throw new InvalidBenchmarkDeclarationException($"{declaringType.FullName} containing {methodType} method {methodInfo.Name} has incorrect access modifiers.\nDeclaring type must be public."); - - declaringType = declaringType.DeclaringType; - } + /* Moved the code that verifies if DeclaringType of a given MethodInfo (a method) is publicly accessible to CompilationValidator */ } private static void AssertMethodIsNotGeneric(string methodType, MethodInfo methodInfo) diff --git a/src/BenchmarkDotNet/Validators/CompilationValidator.cs b/src/BenchmarkDotNet/Validators/CompilationValidator.cs index d8c2ec6c5b..b0bb87681d 100644 --- a/src/BenchmarkDotNet/Validators/CompilationValidator.cs +++ b/src/BenchmarkDotNet/Validators/CompilationValidator.cs @@ -7,6 +7,7 @@ using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains; using Microsoft.CodeAnalysis.CSharp; +using BenchmarkDotNet.Attributes; namespace BenchmarkDotNet.Validators { @@ -25,8 +26,38 @@ private CompilationValidator() { } public IEnumerable Validate(ValidationParameters validationParameters) => ValidateCSharpNaming(validationParameters.Benchmarks) .Union(ValidateNamingConflicts(validationParameters.Benchmarks)) + .Union(ValidateClassModifiers((validationParameters.Benchmarks)) .Union(ValidateAccessModifiers(validationParameters.Benchmarks)) - .Union(ValidateBindingModifiers(validationParameters.Benchmarks)); + .Union(ValidateBindingModifiers(validationParameters.Benchmarks)) + ); + + private static IEnumerable ValidateClassModifiers(IEnumerable benchmarks) + { + return benchmarks + .Distinct(BenchmarkMethodEqualityComparer.Instance) + .SelectMany(benchmark => + { + var type = benchmark.Descriptor.Type; + var errors = new List(); + + if (type.IsSealed) + { + errors.Add(new ValidationError( + true, + $"Benchmarked method `{benchmark.Descriptor.WorkloadMethod.Name}` is within a sealed class, Declaring type must be unsealed.", + benchmark)); + } + if (!type.IsVisible) + { + errors.Add(new ValidationError( + true, + $"Benchmarked method `{benchmark.Descriptor.WorkloadMethod.Name}` is within a non-visible class, all declaring types must be public.", + benchmark)); + } + // TODO: Generics validation + return errors; + }); + } private static IEnumerable ValidateCSharpNaming(IEnumerable benchmarks) => benchmarks diff --git a/tests/BenchmarkDotNet.Tests/Validators/CompilationValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/CompilationValidatorTests.cs index 632875d52b..683e258bbe 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/CompilationValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/CompilationValidatorTests.cs @@ -7,6 +7,7 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Validators; using Xunit; +using System.Reflection; namespace BenchmarkDotNet.Tests.Validators { @@ -59,6 +60,29 @@ public void BenchmarkedMethodNameMustNotUseCsharpKeywords() "Benchmarked method `typeof` contains illegal character(s) or uses C# keyword. Please use `[]` to set custom display name.")); } + [Theory] + /* BenchmarkDotNet can only benchmark public unsealed classes*/ + [InlineData(typeof(BenchMarkPublicClass), false)] + [InlineData(typeof(BenchMarkPublicClass.PublicNestedClass), false)] + [InlineData(typeof(SealedClass.PublicNestedClass), false)] + [InlineData(typeof(OuterClass.PublicNestedClass), true)] + [InlineData(typeof(SealedClass), true)] + [InlineData(typeof(MyPrivateClass), true)] + [InlineData(typeof(MyPublicProtectedClass), true)] + [InlineData(typeof(MyPrivateProtectedClass), true)] + [InlineData(typeof(MyProtectedInternalClass), true)] + [InlineData(typeof(MyInternalClass), true)] + [InlineData(typeof(OuterClass), true)] + [InlineData(typeof(OuterClass.InternalNestedClass), true)] + [InlineData(typeof(BenchMarkPublicClass.InternalNestedClass), true)] + /* Generics Remaining */ + public void Benchmark_Class_Modifers_Must_Be_Public(Type type, bool hasErrors) + { + var validationErrors = CompilationValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(type)); + + Assert.Equal(hasErrors, validationErrors.Any()); + } + [Theory] [InlineData(typeof(BenchmarkClassWithStaticMethod), true)] [InlineData(typeof(BenchmarkClass), false)] @@ -115,7 +139,17 @@ protected internal class ProtectedInternalClass { protected internal class ProtectedInternalNestedClass { } } - } + + private class MyPrivateClass{ [Benchmark] public void PublicMethod(){} } + + protected class MyPublicProtectedClass{ [Benchmark] public void PublicMethod(){} } + + private protected class MyPrivateProtectedClass{ [Benchmark] public void PublicMethod(){} } + + internal class MyInternalClass{ [Benchmark] public void PublicMethod(){} } + + protected internal class MyProtectedInternalClass{ [Benchmark] public void PublicMethod() { } } + } public class BenchmarkClassWithStaticMethod { @@ -138,4 +172,29 @@ internal class InternalClass { internal class InternalNestedClass { } } + + public sealed class SealedClass + { + [Benchmark] public void PublicMethod() { } + + public class PublicNestedClass { [Benchmark] public void PublicMethod() { } } + } + + internal class OuterClass + { + [Benchmark] public void PublicMethod(){} + + internal class InternalNestedClass { [Benchmark] public void PublicMethod() { } } + + public class PublicNestedClass { [Benchmark] public void PublicMethod() { } } + } + + public class BenchMarkPublicClass + { + [Benchmark] public void PublicMethod(){} + + public class PublicNestedClass { [Benchmark] public void PublicMethod() { } } + + internal class InternalNestedClass { [Benchmark] public void PublicMethod() { } } + } } From b9d69a439382aecab4f87f3ecec232bd5f907ace Mon Sep 17 00:00:00 2001 From: leonvandermeer <64834803+leonvandermeer@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:46:34 +0100 Subject: [PATCH 46/57] Prevent premature end of the Benchmark process at Ctrl-C, fixes #2483 (#2661) * Ensure revert of system state at Benchmark Process termination, fixes #2483 * PowerManagementApplier and ConsoleTitler system state is now reverted at Process termination. * To prevent code duplication, DisposeAtProcessTermination class is introduced. * Apply suggestions from code review and add documentation --- .../EtwDiagnoser.cs | 22 +++---- .../EtwProfiler.cs | 19 ++----- .../Sessions.cs | 23 ++------ .../Helpers/DisposeAtProcessTermination.cs | 57 +++++++++++++++++++ .../Helpers/TaskbarProgress.cs | 15 ++--- src/BenchmarkDotNet/Running/ConsoleTitler.cs | 7 ++- .../Running/PowerManagementApplier.cs | 9 ++- 7 files changed, 89 insertions(+), 63 deletions(-) create mode 100644 src/BenchmarkDotNet/Helpers/DisposeAtProcessTermination.cs diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/EtwDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.Windows/EtwDiagnoser.cs index 80423d72cf..4205195dc5 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/EtwDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/EtwDiagnoser.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Loggers; using Microsoft.Diagnostics.Tracing; using Microsoft.Diagnostics.Tracing.Parsers; @@ -15,7 +16,7 @@ namespace BenchmarkDotNet.Diagnostics.Windows { - public abstract class EtwDiagnoser where TStats : new() + public abstract class EtwDiagnoser : DisposeAtProcessTermination where TStats : new() { internal readonly LogCapture Logger = new LogCapture(); protected readonly Dictionary BenchmarkToProcess = new Dictionary(); @@ -39,11 +40,6 @@ protected void Start(DiagnoserActionParameters parameters) BenchmarkToProcess.Add(parameters.BenchmarkCase, parameters.Process.Id); StatsPerProcess.TryAdd(parameters.Process.Id, GetInitializedStats(parameters)); - // Important: Must wire-up clean-up events prior to acquiring IDisposable instance (Session property) - // This is in effect the inverted sequence of actions in the Stop() method. - Console.CancelKeyPress += OnConsoleCancelKeyPress; - AppDomain.CurrentDomain.ProcessExit += OnProcessExit; - Session = CreateSession(parameters.BenchmarkCase); EnableProvider(); @@ -80,11 +76,13 @@ protected virtual void EnableProvider() protected void Stop() { WaitForDelayedEvents(); + Dispose(); + } - Session.Dispose(); - - Console.CancelKeyPress -= OnConsoleCancelKeyPress; - AppDomain.CurrentDomain.ProcessExit -= OnProcessExit; + public override void Dispose() + { + Session?.Dispose(); + base.Dispose(); } private void Clear() @@ -93,10 +91,6 @@ private void Clear() StatsPerProcess.Clear(); } - private void OnConsoleCancelKeyPress(object sender, ConsoleCancelEventArgs e) => Session?.Dispose(); - - private void OnProcessExit(object sender, EventArgs e) => Session?.Dispose(); - private static string GetSessionName(string prefix, BenchmarkCase benchmarkCase, ParameterInstances? parameters = null) { if (parameters != null && parameters.Items.Count > 0) diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfiler.cs b/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfiler.cs index 6641dd5a45..57f57d9e1a 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfiler.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfiler.cs @@ -120,21 +120,10 @@ private void Start(DiagnoserActionParameters parameters) private void Stop(DiagnoserActionParameters parameters) { WaitForDelayedEvents(); - string userSessionFile; - try - { - kernelSession.Stop(); - heapSession?.Stop(); - userSession.Stop(); - - userSessionFile = userSession.FilePath; - } - finally - { - kernelSession.Dispose(); - heapSession?.Dispose(); - userSession.Dispose(); - } + string userSessionFile = userSession.FilePath; + kernelSession.Dispose(); + heapSession?.Dispose(); + userSession.Dispose(); // Merge the 'primary' etl file X.etl (userSession) with any files that match .clr*.etl .user*.etl. and .kernel.etl. TraceEventSession.MergeInPlace(userSessionFile, TextWriter.Null); diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/Sessions.cs b/src/BenchmarkDotNet.Diagnostics.Windows/Sessions.cs index cf0b6fa88f..f0f5dcc475 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/Sessions.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/Sessions.cs @@ -5,13 +5,13 @@ using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Running; using Microsoft.Diagnostics.Tracing; using Microsoft.Diagnostics.Tracing.Parsers; using Microsoft.Diagnostics.Tracing.Session; -using BenchmarkDotNet.Running; namespace BenchmarkDotNet.Diagnostics.Windows { @@ -90,7 +90,7 @@ internal override Session EnableProviders() } } - internal abstract class Session : IDisposable + internal abstract class Session : DisposeAtProcessTermination { private const int MaxSessionNameLength = 128; @@ -114,27 +114,16 @@ protected Session(string sessionName, DiagnoserActionParameters details, EtwProf BufferSizeMB = config.BufferSizeInMb, CpuSampleIntervalMSec = config.CpuSampleIntervalInMilliseconds, }; - - Console.CancelKeyPress += OnConsoleCancelKeyPress; - AppDomain.CurrentDomain.ProcessExit += OnProcessExit; } - public void Dispose() => TraceEventSession.Dispose(); - - internal void Stop() + public override void Dispose() { - TraceEventSession.Stop(); - - Console.CancelKeyPress -= OnConsoleCancelKeyPress; - AppDomain.CurrentDomain.ProcessExit -= OnProcessExit; + TraceEventSession.Dispose(); + base.Dispose(); } internal abstract Session EnableProviders(); - private void OnConsoleCancelKeyPress(object sender, ConsoleCancelEventArgs e) => Stop(); - - private void OnProcessExit(object sender, EventArgs e) => Stop(); - protected static string GetSessionName(BenchmarkCase benchmarkCase) { string benchmarkName = FullNameProvider.GetBenchmarkName(benchmarkCase); diff --git a/src/BenchmarkDotNet/Helpers/DisposeAtProcessTermination.cs b/src/BenchmarkDotNet/Helpers/DisposeAtProcessTermination.cs new file mode 100644 index 0000000000..12aa89a6fa --- /dev/null +++ b/src/BenchmarkDotNet/Helpers/DisposeAtProcessTermination.cs @@ -0,0 +1,57 @@ +using System; +using System.Runtime.InteropServices; + +namespace BenchmarkDotNet.Helpers +{ + /// + /// Ensures that explicit Dispose is called at termination of the Process. + /// + /// + /// + /// This class exists to help in reverting system state where C#'s using statement does not + /// suffice. I.e. when Benchmark's process is aborted via Ctrl-C, Ctrl-Break or via click on the + /// X in the upper right of Window. + /// + /// + /// Usage: Derive your clas that changes system state of this class. Revert system state in + /// override of implementation. + /// Use your class in C#'s using statement, to ensure system state is reverted in normal situations. + /// This class ensures your override is also called at process 'abort'. + /// + /// + /// Note: This class is explicitly not responsible for cleanup of Native resources. Of course, + /// derived classes can cleanup their Native resources (usually managed via + /// derived classes), by delegating explicit Disposal to their + /// fields. + /// + /// + public abstract class DisposeAtProcessTermination : IDisposable + { + public DisposeAtProcessTermination() + { + Console.CancelKeyPress += OnCancelKeyPress; + AppDomain.CurrentDomain.ProcessExit += OnProcessExit; + // It does not make sense to include a Finalizer. We do not manage any native resource and: + // as we are subscribed to static events, it would never be called. + } + + /// + /// Called when the user presses Ctrl-C or Ctrl-Break. + /// + private void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e) + { + if (!e.Cancel) { Dispose(); } + } + + /// + /// Called when the user clicks on the X in the upper right corner to close the Benchmark's Window. + /// + private void OnProcessExit(object? sender, EventArgs e) => Dispose(); + + public virtual void Dispose() + { + Console.CancelKeyPress -= OnCancelKeyPress; + AppDomain.CurrentDomain.ProcessExit -= OnProcessExit; + } + } +} diff --git a/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs b/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs index 94afb75311..e3910a0924 100644 --- a/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs +++ b/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs @@ -14,7 +14,7 @@ internal enum TaskbarProgressState Warning = Paused } - internal class TaskbarProgress : IDisposable + internal class TaskbarProgress : DisposeAtProcessTermination { private static readonly bool OsVersionIsSupported = OsDetector.IsWindows() // Must be windows 7 or greater @@ -31,10 +31,6 @@ internal TaskbarProgress(TaskbarProgressState initialTaskbarState) { com = Com.MaybeCreateInstanceAndSetInitialState(initialTaskbarState); terminal = Terminal.MaybeCreateInstanceAndSetInitialState(initialTaskbarState); - if (IsEnabled) - { - Console.CancelKeyPress += OnConsoleCancelEvent; - } } } @@ -51,23 +47,20 @@ internal void SetProgress(float progressValue) { throw new ArgumentOutOfRangeException(nameof(progressValue), "progressValue must be between 0 and 1 inclusive."); } - uint value = (uint) (progressValue * 100); + uint value = (uint)(progressValue * 100); com?.SetValue(value); terminal?.SetValue(value); } - private void OnConsoleCancelEvent(object sender, ConsoleCancelEventArgs e) - => Dispose(); - - public void Dispose() + public override void Dispose() { if (IsEnabled) { SetState(TaskbarProgressState.NoProgress); com = null; terminal = null; - Console.CancelKeyPress -= OnConsoleCancelEvent; } + base.Dispose(); } private sealed class Com diff --git a/src/BenchmarkDotNet/Running/ConsoleTitler.cs b/src/BenchmarkDotNet/Running/ConsoleTitler.cs index 7458388c42..d045e498aa 100644 --- a/src/BenchmarkDotNet/Running/ConsoleTitler.cs +++ b/src/BenchmarkDotNet/Running/ConsoleTitler.cs @@ -1,7 +1,7 @@ using System; using System.IO; using BenchmarkDotNet.Detectors; -using BenchmarkDotNet.Portability; +using BenchmarkDotNet.Helpers; namespace BenchmarkDotNet.Running { @@ -9,7 +9,7 @@ namespace BenchmarkDotNet.Running /// Updates Console.Title, subject to platform capabilities and Console availability. /// Restores the original (or fallback) title upon disposal. /// - internal class ConsoleTitler : IDisposable + internal class ConsoleTitler : DisposeAtProcessTermination { /// /// Whether this instance has any effect. This will be false if the platform doesn't support Console retitling, @@ -76,13 +76,14 @@ public void UpdateTitle(string title) } } - public void Dispose() + public override void Dispose() { if (IsEnabled) { Console.Title = oldConsoleTitle; IsEnabled = false; } + base.Dispose(); } } } diff --git a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs index f240c1a209..f48d95d61e 100644 --- a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs +++ b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs @@ -4,11 +4,10 @@ using BenchmarkDotNet.Environments; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Portability; namespace BenchmarkDotNet.Running { - internal class PowerManagementApplier : IDisposable + internal class PowerManagementApplier : DisposeAtProcessTermination { private static readonly Guid UserPowerPlan = new Guid("67b4a053-3646-4532-affd-0535c9ea82a7"); @@ -28,7 +27,11 @@ internal class PowerManagementApplier : IDisposable internal PowerManagementApplier(ILogger logger) => this.logger = logger; - public void Dispose() => ApplyUserPowerPlan(); + public override void Dispose() + { + ApplyUserPowerPlan(); + base.Dispose(); + } internal static Guid Map(PowerPlan value) => PowerPlansDict[value]; From 25308bfc1d3d53782a1a1cfbe974ce5e22bd474e Mon Sep 17 00:00:00 2001 From: Keegan Date: Fri, 15 Nov 2024 10:02:06 -0800 Subject: [PATCH 47/57] Native AOT projects should also copy SettingsWeWantToCopy (#2665) Co-authored-by: Keegan Caruso --- .../Toolchains/NativeAot/Generator.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs index b3adfc3e3c..061b64a9a8 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Xml; using BenchmarkDotNet.ConsoleArguments; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Detectors.Cpu; @@ -155,8 +156,20 @@ private string GenerateProjectForNuGetBuild(BuildPartition buildPartition, Artif {string.Join(Environment.NewLine, GetRdXmlFiles(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger).Select(file => $""))} +{GetCustomProperties(buildPartition, logger)} "; + private string GetCustomProperties(BuildPartition buildPartition, ILogger logger) + { + var projectFile = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger); + var xmlDoc = new XmlDocument(); + xmlDoc.Load(projectFile.FullName); + + (string customProperties, _) = GetSettingsThatNeedToBeCopied(xmlDoc, projectFile); + return customProperties; + } + + private string GetILCompilerPackageReference() => string.IsNullOrEmpty(ilCompilerVersion) ? "" : $@""; From c7ed714a34a4de04f7320b6d971a51b04545bb1c Mon Sep 17 00:00:00 2001 From: Keegan Date: Fri, 15 Nov 2024 14:21:34 -0800 Subject: [PATCH 48/57] Remove obsolete API usage in articles (#2667) * Remove obsolete API usage in articles * Apply suggestions from code review Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> --------- Co-authored-by: Keegan Caruso Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> --- docs/articles/configs/diagnosers.md | 10 +++++----- docs/articles/configs/jobs.md | 26 ++++++++++++++------------ docs/articles/configs/toolchains.md | 21 ++++++++++----------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/docs/articles/configs/diagnosers.md b/docs/articles/configs/diagnosers.md index cb0facc3a9..d50d6215b2 100644 --- a/docs/articles/configs/diagnosers.md +++ b/docs/articles/configs/diagnosers.md @@ -59,11 +59,11 @@ private class Config : ManualConfig { public Config() { - Add(MemoryDiagnoser.Default); - Add(new InliningDiagnoser()); - Add(new EtwProfiler()); - Add(ThreadingDiagnoser.Default); - Add(ExceptionDiagnoser.Default); + AddDiagnoser(MemoryDiagnoser.Default); + AddDiagnoser(new InliningDiagnoser()); + AddDiagnoser(new EtwProfiler()); + AddDiagnoser(ThreadingDiagnoser.Default); + AddDiagnoser(ExceptionDiagnoser.Default); } } ``` diff --git a/docs/articles/configs/jobs.md b/docs/articles/configs/jobs.md index a6085e0bdb..ad0cf4559c 100644 --- a/docs/articles/configs/jobs.md +++ b/docs/articles/configs/jobs.md @@ -98,20 +98,20 @@ public class MyBenchmarks { public Config() { - Add( - new Job("MySuperJob", RunMode.Dry, EnvMode.RyuJitX64) + AddJob( + new Job("MySuperJob", RunMode.Dry, EnvironmentMode.RyuJitX64) { - Env = { Runtime = Runtime.Core }, + Environment = { Runtime = CoreRuntime.Core90 }, Run = { LaunchCount = 5, IterationTime = TimeInterval.Millisecond * 200 }, - Accuracy = { MaxStdErrRelative = 0.01 } + Accuracy = { MaxRelativeError = 0.01 } }); // The same, using the .With() factory methods: - Add( + AddJob( Job.Dry .WithPlatform(Platform.X64) .WithJit(Jit.RyuJit) - .WithRuntime(Runtime.Core) + .WithRuntime(CoreRuntime.Core90) .WithLaunchCount(5) .WithIterationTime(TimeInterval.Millisecond * 200) .WithMaxRelativeError(0.01) @@ -122,26 +122,26 @@ public class MyBenchmarks } ``` -Basically, it's a good idea to start with predefined values (e.g. `EnvMode.RyuJitX64` and `RunMode.Dry` passed as constructor args) and modify rest of the properties using property setters or with help of object initializer syntax. +Basically, it's a good idea to start with predefined values (e.g. `EnvironmentMode.RyuJitX64` and `RunMode.Dry` passed as constructor args) and modify rest of the properties using property setters or with help of object initializer syntax. Note that the job cannot be modified after it's added into config. Trying to set a value on property of the frozen job will throw an `InvalidOperationException`. Use the `Job.Frozen` property to determine if the code properties can be altered. If you do want to create a new job based on frozen one (all predefined job values are frozen) you can use the `.With()` extension method ```cs - var newJob = Job.Dry.With(Platform.X64); + var newJob = Job.Dry.WithPlatform(Platform.X64); ``` or pass the frozen value as a constructor argument ```c# - var newJob = new Job(Job.Dry) { Env = { Platform = Platform.X64 } }; + var newJob = new Job(Job.Dry) { Environment = { Platform = Platform.X64 } }; ``` or use the `.Apply()` method on unfrozen job ```c# - var newJob = new Job() { Env = { Platform = Platform.X64 } }.Apply(Job.Dry); + var newJob = new Job() { Environment = { Platform = Platform.X64 } }.Apply(Job.Dry); ``` in any case the Id property will not be transfered and you must pass it explicitly (using the .ctor id argument or the `.WithId()` extension method). @@ -152,7 +152,9 @@ You can also add new jobs via attributes. Examples: ```cs [DryJob] -[ClrJob, CoreJob, MonoJob] +[MonoJob] +[SimpleJob(RuntimeMoniker.Net90)] +[SimpleJob(RuntimeMoniker.NetCoreApp31)] [LegacyJitX86Job, LegacyJitX64Job, RyuJitX64Job] [SimpleJob(RunStrategy.ColdStart, launchCount: 1, warmupCount: 5, iterationCount: 5, id: "FastAndDirtyJob")] public class MyBenchmarkClass @@ -212,7 +214,7 @@ public class MySuperJobAttribute : Attribute, IConfigSource { var job = new Job("MySuperJob", RunMode.Core); job.Env.Platform = Platform.X64; - Config = ManualConfig.CreateEmpty().With(job); + Config = ManualConfig.CreateEmpty().AddJob(job); } public IConfig Config { get; } diff --git a/docs/articles/configs/toolchains.md b/docs/articles/configs/toolchains.md index 538891d601..b47e994a94 100644 --- a/docs/articles/configs/toolchains.md +++ b/docs/articles/configs/toolchains.md @@ -87,10 +87,9 @@ namespace BenchmarkDotNet.Samples static void Main(string[] args) { var config = DefaultConfig.Instance - .With(Job.Default.With(CoreRuntime.Core21)) - .With(Job.Default.With(CoreRuntime.Core30)) - .With(Job.Default.With(ClrRuntime.Net48)) - .With(Job.Default.With(MonoRuntime.Default)); + .AddJob(Job.Default.WithRuntime(CoreRuntime.Core80)) + .AddJob(Job.Default.WithRuntime(ClrRuntime.Net48)) + .AddJob(Job.Default.WithRuntime(MonoRuntime.Default)); BenchmarkSwitcher .FromAssembly(typeof(Program).Assembly) @@ -130,8 +129,8 @@ It's possible to benchmark a private build of .NET Runtime. All you need to do i BenchmarkSwitcher .FromAssembly(typeof(Program).Assembly) .Run(args, - DefaultConfig.Instance.With( - Job.ShortRun.With(ClrRuntime.CreateForLocalFullNetFrameworkBuild(version: "4.0")))); + DefaultConfig.Instance.AddJob( + Job.ShortRun.WithRuntime(ClrRuntime.CreateForLocalFullNetFrameworkBuild(version: "4.0")))); ``` This sends the provided version as a `COMPLUS_Version` env var to the benchmarked process. @@ -208,7 +207,7 @@ or: ```cs var config = DefaultConfig.Instance - .With(Job.Default.With(NativeAotRuntime.Net70)); // compiles the benchmarks as net7.0 and uses the latest NativeAOT to build a native app + .AddJob(Job.Default.WithRuntime(NativeAotRuntime.Net70)); // compiles the benchmarks as net7.0 and uses the latest NativeAOT to build a native app BenchmarkSwitcher .FromAssembly(typeof(Program).Assembly) @@ -231,8 +230,8 @@ If you want to benchmark some particular version of NativeAOT (or from a differe ```cs var config = DefaultConfig.Instance - .With(Job.ShortRun - .With(NativeAotToolchain.CreateBuilder() + .AddJob(Job.ShortRun + .WithToolchain(NativeAotToolchain.CreateBuilder() .UseNuGet( microsoftDotNetILCompilerVersion: "7.0.0-*", // the version goes here nuGetFeedUrl: "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json") // this address might change over time @@ -338,8 +337,8 @@ or explicitly in the code: ```cs var config = DefaultConfig.Instance - .With(Job.ShortRun - .With(NativeAotToolchain.CreateBuilder() + .AddJob(Job.ShortRun + .WithToolchain(NativeAotToolchain.CreateBuilder() .UseLocalBuild(@"C:\Projects\runtime\artifacts\packages\Release\Shipping\") .DisplayName("NativeAOT local build") .TargetFrameworkMoniker("net7.0") From 5f0c47bfd15700e63f47870742282d417258c988 Mon Sep 17 00:00:00 2001 From: Tim Cassell <35501420+timcassell@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:54:10 -0500 Subject: [PATCH 49/57] =?UTF-8?q?=EF=BB=BFFix=20async=20GlobalSetup/Global?= =?UTF-8?q?Cleanup=20not=20being=20awaited=20with=20InProcessEmit=20toolch?= =?UTF-8?q?ain.=20(#2109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Emitters/RunnableEmitter.cs | 91 +++++++++++--- .../InProcessEmitTest.cs | 116 ++++++++++++++++-- 2 files changed, 180 insertions(+), 27 deletions(-) diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs index e0618cb642..0e9d386a24 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs @@ -247,6 +247,10 @@ private static void EmitNoArgsMethodCallPopReturn( private TypeBuilder runnableBuilder; private ConsumableTypeInfo consumableInfo; private ConsumeEmitter consumeEmitter; + private ConsumableTypeInfo globalSetupReturnInfo; + private ConsumableTypeInfo globalCleanupReturnInfo; + private ConsumableTypeInfo iterationSetupReturnInfo; + private ConsumableTypeInfo iterationCleanupReturnInfo; private FieldBuilder globalSetupActionField; private FieldBuilder globalCleanupActionField; @@ -358,6 +362,10 @@ private void InitForEmitRunnable(BenchmarkBuildInfo newBenchmark) consumableInfo = new ConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.WorkloadMethod.ReturnType); consumeEmitter = ConsumeEmitter.GetConsumeEmitter(consumableInfo); + globalSetupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.GlobalSetupMethod?.ReturnType); + globalCleanupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.GlobalCleanupMethod?.ReturnType); + iterationSetupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.IterationSetupMethod?.ReturnType); + iterationCleanupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.IterationCleanupMethod?.ReturnType); // Init types runnableBuilder = DefineRunnableTypeBuilder(benchmark, moduleBuilder); @@ -365,6 +373,11 @@ private void InitForEmitRunnable(BenchmarkBuildInfo newBenchmark) workloadDelegateType = EmitWorkloadDelegateType(); } + private static ConsumableTypeInfo GetConsumableTypeInfo(Type methodReturnType) + { + return methodReturnType == null ? null : new ConsumableTypeInfo(methodReturnType); + } + private Type EmitOverheadDelegateType() { // .class public auto ansi sealed BenchmarkDotNet.Autogenerated.Runnable_0OverheadDelegate @@ -890,34 +903,84 @@ private void EmitSetupCleanupMethods() { // Emit Setup/Cleanup methods // We emit empty method instead of EmptyAction = "() => { }" - globalSetupMethod = EmitWrapperMethod( - GlobalSetupMethodName, - Descriptor.GlobalSetupMethod); - globalCleanupMethod = EmitWrapperMethod( - GlobalCleanupMethodName, - Descriptor.GlobalCleanupMethod); - iterationSetupMethod = EmitWrapperMethod( - IterationSetupMethodName, - Descriptor.IterationSetupMethod); - iterationCleanupMethod = EmitWrapperMethod( - IterationCleanupMethodName, - Descriptor.IterationCleanupMethod); + globalSetupMethod = EmitWrapperMethod(GlobalSetupMethodName, Descriptor.GlobalSetupMethod, globalSetupReturnInfo); + globalCleanupMethod = EmitWrapperMethod(GlobalCleanupMethodName, Descriptor.GlobalCleanupMethod, globalCleanupReturnInfo); + iterationSetupMethod = EmitWrapperMethod(IterationSetupMethodName, Descriptor.IterationSetupMethod, iterationSetupReturnInfo); + iterationCleanupMethod = EmitWrapperMethod(IterationCleanupMethodName, Descriptor.IterationCleanupMethod, iterationCleanupReturnInfo); } - private MethodBuilder EmitWrapperMethod(string methodName, MethodInfo optionalTargetMethod) + private MethodBuilder EmitWrapperMethod(string methodName, MethodInfo optionalTargetMethod, ConsumableTypeInfo returnTypeInfo) { var methodBuilder = runnableBuilder.DefinePrivateVoidInstanceMethod(methodName); var ilBuilder = methodBuilder.GetILGenerator(); if (optionalTargetMethod != null) - EmitNoArgsMethodCallPopReturn(methodBuilder, optionalTargetMethod, ilBuilder, forceDirectCall: true); + { + if (returnTypeInfo?.IsAwaitable == true) + { + EmitAwaitableSetupTeardown(methodBuilder, optionalTargetMethod, ilBuilder, returnTypeInfo); + } + else + { + EmitNoArgsMethodCallPopReturn(methodBuilder, optionalTargetMethod, ilBuilder, forceDirectCall: true); + } + } ilBuilder.EmitVoidReturn(methodBuilder); return methodBuilder; } + private void EmitAwaitableSetupTeardown( + MethodBuilder methodBuilder, + MethodInfo targetMethod, + ILGenerator ilBuilder, + ConsumableTypeInfo returnTypeInfo) + { + if (targetMethod == null) + throw new ArgumentNullException(nameof(targetMethod)); + + if (returnTypeInfo.WorkloadMethodReturnType == typeof(void)) + { + ilBuilder.Emit(OpCodes.Ldarg_0); + } + /* + // call for instance + // GlobalSetup(); + IL_0006: ldarg.0 + IL_0007: call instance void [BenchmarkDotNet]BenchmarkDotNet.Samples.SampleBenchmark::GlobalSetup() + */ + /* + // call for static + // GlobalSetup(); + IL_0006: call string [BenchmarkDotNet]BenchmarkDotNet.Samples.SampleBenchmark::GlobalCleanup() + */ + if (targetMethod.IsStatic) + { + ilBuilder.Emit(OpCodes.Call, targetMethod); + + } + else if (methodBuilder.IsStatic) + { + throw new InvalidOperationException( + $"[BUG] Static method {methodBuilder.Name} tries to call instance member {targetMethod.Name}"); + } + else + { + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Call, targetMethod); + } + + /* + // BenchmarkDotNet.Helpers.AwaitHelper.GetResult(...); + IL_000e: call !!0 BenchmarkDotNet.Helpers.AwaitHelper::GetResult(valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1) + */ + + ilBuilder.Emit(OpCodes.Call, returnTypeInfo.GetResultMethod); + ilBuilder.Emit(OpCodes.Pop); + } + private void EmitCtorBody() { var ilBuilder = ctorMethod.GetILGenerator(); diff --git a/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs b/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs index a48315c2ef..6ae599e0b6 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs @@ -239,35 +239,125 @@ public static ValueTask InvokeOnceStaticValueTaskOfT() } } - [Fact] - public void InProcessEmitToolchainSupportsIterationSetupAndCleanup() + [Theory] + [InlineData(typeof(IterationSetupCleanup))] + [InlineData(typeof(GlobalSetupCleanupTask))] + [InlineData(typeof(GlobalSetupCleanupValueTask))] + [InlineData(typeof(GlobalSetupCleanupValueTaskSource))] + public void InProcessEmitToolchainSupportsSetupAndCleanup(Type benchmarkType) { var logger = new OutputLogger(Output); var config = CreateInProcessConfig(logger); - WithIterationSetupAndCleanup.SetupCounter = 0; - WithIterationSetupAndCleanup.BenchmarkCounter = 0; - WithIterationSetupAndCleanup.CleanupCounter = 0; + Counters.SetupCounter = 0; + Counters.BenchmarkCounter = 0; + Counters.CleanupCounter = 0; - var summary = CanExecute(config); + var summary = CanExecute(benchmarkType, config); - Assert.Equal(1, WithIterationSetupAndCleanup.SetupCounter); - Assert.Equal(16, WithIterationSetupAndCleanup.BenchmarkCounter); - Assert.Equal(1, WithIterationSetupAndCleanup.CleanupCounter); + Assert.Equal(1, Counters.SetupCounter); + Assert.Equal(16, Counters.BenchmarkCounter); + Assert.Equal(1, Counters.CleanupCounter); } - public class WithIterationSetupAndCleanup + private static class Counters { public static int SetupCounter, BenchmarkCounter, CleanupCounter; + } + public class IterationSetupCleanup + { [IterationSetup] - public void Setup() => Interlocked.Increment(ref SetupCounter); + public void Setup() => Interlocked.Increment(ref Counters.SetupCounter); [Benchmark] - public void Benchmark() => Interlocked.Increment(ref BenchmarkCounter); + public void Benchmark() => Interlocked.Increment(ref Counters.BenchmarkCounter); [IterationCleanup] - public void Cleanup() => Interlocked.Increment(ref CleanupCounter); + public void Cleanup() => Interlocked.Increment(ref Counters.CleanupCounter); + } + + public class GlobalSetupCleanupTask + { + [GlobalSetup] + public static async Task GlobalSetup() + { + await Task.Yield(); + Interlocked.Increment(ref Counters.SetupCounter); + } + + [GlobalCleanup] + public async Task GlobalCleanup() + { + await Task.Yield(); + Interlocked.Increment(ref Counters.CleanupCounter); + return 42; + } + + [Benchmark] + public void InvokeOnceVoid() + { + Interlocked.Increment(ref Counters.BenchmarkCounter); + } + } + + public class GlobalSetupCleanupValueTask + { + [GlobalSetup] + public static async ValueTask GlobalSetup() + { + await Task.Yield(); + Interlocked.Increment(ref Counters.SetupCounter); + } + + [GlobalCleanup] + public async ValueTask GlobalCleanup() + { + await Task.Yield(); + Interlocked.Increment(ref Counters.CleanupCounter); + return 42; + } + + [Benchmark] + public void InvokeOnceVoid() + { + Interlocked.Increment(ref Counters.BenchmarkCounter); + } + } + + public class GlobalSetupCleanupValueTaskSource + { + private readonly static ValueTaskSource valueTaskSource = new (); + + [GlobalSetup] + public static ValueTask GlobalSetup() + { + valueTaskSource.Reset(); + Task.Delay(1).ContinueWith(_ => + { + Interlocked.Increment(ref Counters.SetupCounter); + valueTaskSource.SetResult(42); + }); + return new ValueTask(valueTaskSource, valueTaskSource.Token); + } + + [GlobalCleanup] + public ValueTask GlobalCleanup() + { + valueTaskSource.Reset(); + Task.Delay(1).ContinueWith(_ => + { + Interlocked.Increment(ref Counters.CleanupCounter); + valueTaskSource.SetResult(42); + }); + return new ValueTask(valueTaskSource, valueTaskSource.Token); + } + + [Benchmark] + public void InvokeOnceVoid() + { + Interlocked.Increment(ref Counters.BenchmarkCounter); + } } } } \ No newline at end of file From 6367ad84177e51a11e93f93237ab1e13b673f55f Mon Sep 17 00:00:00 2001 From: Nik Karpinsky Date: Mon, 9 Dec 2024 06:01:16 -0500 Subject: [PATCH 50/57] Add documentation for VS Profiler (#2672) * Add documentation for VS Profiler - This change adds documentation for the Visual Studio profiler. There is a new document under features that explains how to use the nuget package and a new sample under IntroVisualStudioDiagnoser. A new package reference has also been added to the samples project to pull in the Visual Studio specific diagnosers for the sample project. * Addressing PR feedback --------- Co-authored-by: Nik Karpinsky --- build/cSpell.json | 2 + docs/articles/features/toc.yml | 2 + docs/articles/features/vsprofiler.md | 71 ++++++++++++++++++ .../samples/IntroVisualStudioProfiler.md | 23 ++++++ docs/articles/samples/toc.yml | 2 + docs/images/vs-profiler-demo.png | Bin 0 -> 60860 bytes .../BenchmarkDotNet.Samples.csproj | 2 + .../IntroVisualStudioDiagnoser.cs | 23 ++++++ 8 files changed, 125 insertions(+) create mode 100644 docs/articles/features/vsprofiler.md create mode 100644 docs/articles/samples/IntroVisualStudioProfiler.md create mode 100644 docs/images/vs-profiler-demo.png create mode 100644 samples/BenchmarkDotNet.Samples/IntroVisualStudioDiagnoser.cs diff --git a/build/cSpell.json b/build/cSpell.json index 6bdc2226cc..42795c7a70 100644 --- a/build/cSpell.json +++ b/build/cSpell.json @@ -12,6 +12,7 @@ "Cygwin", "Diagnoser", "diagnosers", + "diagsession", "disassemblers", "disassm", "Jits", @@ -29,6 +30,7 @@ "Pseudocode", "runtimes", "Serilog", + "vsprofiler", "vstest", "Tailcall", "toolchains", diff --git a/docs/articles/features/toc.yml b/docs/articles/features/toc.yml index ac29397743..61e3a2cd54 100644 --- a/docs/articles/features/toc.yml +++ b/docs/articles/features/toc.yml @@ -12,5 +12,7 @@ href: etwprofiler.md - name: EventPipeProfiler href: event-pipe-profiler.md +- name: VSProfiler + href: vsprofiler.md - name: VSTest href: vstest.md \ No newline at end of file diff --git a/docs/articles/features/vsprofiler.md b/docs/articles/features/vsprofiler.md new file mode 100644 index 0000000000..9e8f52712d --- /dev/null +++ b/docs/articles/features/vsprofiler.md @@ -0,0 +1,71 @@ +--- +uid: docs.vsprofiler +name: VS Profiler +--- + +# Running with Visual Studio profiler +Visual Studio supports [profiler integration with BenchmarkDotNet](https://learn.microsoft.com/visualstudio/profiling/profiling-with-benchmark-dotnet) on Windows through its [Microsoft.VisualStudio.BenchmarkDotNetDiagnosers](https://www.nuget.org/packages/Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers) NuGet package. Once installed, Visual Studio specific diagnosers will capture performance data in runs and automatically open traces if launched through Visual Studio + +![](../../images/vs-profiler-demo.png) + +## How it works + +First, install the [Microsoft.VisualStudio.BenchmarkDotNetDiagnosers](https://www.nuget.org/packages/Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers) NuGet package in your benchmarking project. Next add one or more of the Visual Studio diagnosers to your benchmark to capture the relevant profiling information while benchmarking. Lastly, run your benchmarks and a diagsession will be generated. If run from Visual Studio the diagsession will automatically be opened. + +## Available Diagnosers + +* `[CPUUsageDiagnoser]` - Enables the [CPU Usage tool](https://learn.microsoft.com/visualstudio/profiling/cpu-usage). +* `[DatabaseDiagnoser]` - Enables the [Database tool](https://learn.microsoft.com/visualstudio/profiling/analyze-database) +* `[DotNetCountersDiagnoser]` - Enables the [.NET Counters tool](https://learn.microsoft.com/visualstudio/profiling/dotnet-counters-tool) +* `[DotNetObjectAllocDiagnoser]` - Enables the [.NET Object Allocation tool](https://learn.microsoft.com/visualstudio/profiling/dotnet-alloc-tool). When using this tool, you must also specify `[DotNetObjectAllocJobConfiguration]` on the benchmark. If this is missing the run will fail and you will receive an error indicating you need to add it. +* `[EventsDiagnoser]` - Enables the [Events tool](https://learn.microsoft.com/visualstudio/profiling/events-viewer) +* `[FileIODiagnoser]` - Enables the [File IO tool](https://learn.microsoft.com/visualstudio/profiling/use-file-io) + +## How to use it? + +After installing the [Microsoft.VisualStudio.BenchmarkDotNetDiagnosers](https://www.nuget.org/packages/Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers) NuGet package add the following code as a benchmark: + +```cs +using System; +using System.Security.Cryptography; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using Microsoft.VSDiagnostics; + +namespace MyBenchmarks +{ + [CPUUsageDiagnoser] + public class Md5VsSha256 + { + private const int N = 10000; + private readonly byte[] data; + + private readonly SHA256 sha256 = SHA256.Create(); + private readonly MD5 md5 = MD5.Create(); + + public Md5VsSha256() + { + data = new byte[N]; + new Random(42).NextBytes(data); + } + + [Benchmark] + public byte[] Sha256() => sha256.ComputeHash(data); + + [Benchmark] + public byte[] Md5() => md5.ComputeHash(data); + } + + public class Program + { + public static void Main(string[] args) + { + var summary = BenchmarkRunner.Run(typeof(Program).Assembly); + } + } +} +``` + +In this case we have added the `[CpuUsageDiagnoser]` to capture a CPU sampling trace. From here run the benchmark in Visual Studio (Ctrl+F5), and after the benchmark run the resulting diagsession will be displayed. Double clicking on one of the benchmark rows shown under the Benchmarks tab will filter the time selection to the specific benchmark allowing you to better isolate and investigate. + +![](../../images/vs-profiler-demo.png) \ No newline at end of file diff --git a/docs/articles/samples/IntroVisualStudioProfiler.md b/docs/articles/samples/IntroVisualStudioProfiler.md new file mode 100644 index 0000000000..8d35498f21 --- /dev/null +++ b/docs/articles/samples/IntroVisualStudioProfiler.md @@ -0,0 +1,23 @@ +--- +uid: BenchmarkDotNet.Samples.IntroVisualStudioProfiler +--- + +## Sample: Visual Studio Profiler + +Using the [Microsoft.VisualStudio.BenchmarkDotNetDiagnosers](https://www.nuget.org/packages/Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers) NuGet package you can capture performance profiles of your benchmarks that can be opened in Visual Studio. + +### Source code + +[!code-csharp[IntroVisualStudioDiagnoser.cs](../../../samples/BenchmarkDotNet.Samples/IntroVisualStudioDiagnoser.cs)] + +### Output +The output will contain a path to the collected diagsession and automatically open in Visual Studio when launched from it. + +```markdown +// * Diagnostic Output - VSDiagnosticsDiagnoser * +Collection result moved to 'C:\Work\BenchmarkDotNet\samples\BenchmarkDotNet.Samples\bin\Release\net8.0\BenchmarkDotNet.Artifacts\BenchmarkDotNet_IntroVisualStudioProfiler_20241205_192056.diagsession'. +Session : {d54ebddb-2d6d-404f-b1da-10acbc89635f} + Stopped +Exported diagsession file: C:\Work\BenchmarkDotNet\samples\BenchmarkDotNet.Samples\bin\Release\net8.0\BenchmarkDotNet.Artifacts\BenchmarkDotNet_IntroVisualStudioProfiler_20241205_192056.diagsession. +Opening diagsession in VisualStudio: 15296 +``` \ No newline at end of file diff --git a/docs/articles/samples/toc.yml b/docs/articles/samples/toc.yml index 7e4c7671e1..b9a1c45bf1 100644 --- a/docs/articles/samples/toc.yml +++ b/docs/articles/samples/toc.yml @@ -124,6 +124,8 @@ href: IntroTagColumn.md - name: IntroTailcall href: IntroTailcall.md +- name: IntroVisualStudioProfiler + href: IntroVisualStudioProfiler.md - name: IntroWasm href: IntroWasm.md - name: IntroUnicode diff --git a/docs/images/vs-profiler-demo.png b/docs/images/vs-profiler-demo.png new file mode 100644 index 0000000000000000000000000000000000000000..928f012df19624017debcc3a74b7c29cb31fbc2b GIT binary patch literal 60860 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 + + diff --git a/samples/BenchmarkDotNet.Samples/IntroVisualStudioDiagnoser.cs b/samples/BenchmarkDotNet.Samples/IntroVisualStudioDiagnoser.cs new file mode 100644 index 0000000000..e513f39320 --- /dev/null +++ b/samples/BenchmarkDotNet.Samples/IntroVisualStudioDiagnoser.cs @@ -0,0 +1,23 @@ +using System; +using BenchmarkDotNet.Attributes; +using Microsoft.VSDiagnostics; + +namespace BenchmarkDotNet.Samples +{ + // Enables profiling with the CPU Usage tool + // See: https://learn.microsoft.com/visualstudio/profiling/profiling-with-benchmark-dotnet + [CPUUsageDiagnoser] + public class IntroVisualStudioProfiler + { + private readonly Random rand = new Random(42); + + [Benchmark] + public void BurnCPU() + { + for (int i = 0; i < 100000; ++i) + { + rand.Next(1, 100); + } + } + } +} \ No newline at end of file From fe5b2f5ba6a4ef6e554579043b770be219391a43 Mon Sep 17 00:00:00 2001 From: Tim Cassell <35501420+timcassell@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:45:23 -0500 Subject: [PATCH 51/57] Fix builds when --keepFiles is specified (#2423) * Include guid in build artifacts directory when --keepFiles is specified. * Use an auto-incremented id instead of guid. Always use same ProgramName regardless of --keepFiles. Include benchmark assembly name in ProgramName. * Fix InProcessBenchmarkEmitsSameIL tests. * Added comment from PR feedback. --- src/BenchmarkDotNet/Running/BuildPartition.cs | 11 ++++++++++- .../InProcessEmitTest.cs | 10 +++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/BenchmarkDotNet/Running/BuildPartition.cs b/src/BenchmarkDotNet/Running/BuildPartition.cs index 4ffc48d6c8..d7fb9bc8a8 100644 --- a/src/BenchmarkDotNet/Running/BuildPartition.cs +++ b/src/BenchmarkDotNet/Running/BuildPartition.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Reflection; +using System.Threading; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Environments; @@ -18,12 +19,20 @@ namespace BenchmarkDotNet.Running { public class BuildPartition { + // We use an auto-increment global counter instead of Guid to guarantee uniqueness per benchmark run (Guid has a small chance to collide), + // assuming there are fewer than 4 billion build partitions (a safe assumption). + internal static int s_partitionCounter; + public BuildPartition(BenchmarkBuildInfo[] benchmarks, IResolver resolver) { Resolver = resolver; RepresentativeBenchmarkCase = benchmarks[0].BenchmarkCase; Benchmarks = benchmarks; - ProgramName = benchmarks[0].Config.Options.IsSet(ConfigOptions.KeepBenchmarkFiles) ? RepresentativeBenchmarkCase.Job.FolderInfo : Guid.NewGuid().ToString(); + // Combine the benchmark's assembly name, folder info, and build partition id. + string benchmarkAssemblyName = RepresentativeBenchmarkCase.Descriptor.Type.Assembly.GetName().Name; + string folderInfo = RepresentativeBenchmarkCase.Job.FolderInfo; + int id = Interlocked.Increment(ref s_partitionCounter); + ProgramName = $"{benchmarkAssemblyName}-{folderInfo}-{id}"; LogBuildOutput = benchmarks[0].Config.Options.IsSet(ConfigOptions.LogBuildOutput); GenerateMSBuildBinLog = benchmarks[0].Config.Options.IsSet(ConfigOptions.GenerateMSBuildBinLog); } diff --git a/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs b/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs index 6ae599e0b6..76a665d4de 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs @@ -10,6 +10,7 @@ using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; using BenchmarkDotNet.Tests.Loggers; using BenchmarkDotNet.Tests.XUnit; using BenchmarkDotNet.Toolchains.InProcess.Emit; @@ -68,10 +69,13 @@ private void DiffEmit(Summary summary) if (!Portability.RuntimeInformation.IsFullFramework) return; - var caseName = summary.BenchmarksCases.First().Job.ToString(); + var benchmarkCase = summary.BenchmarksCases.First(); + var caseName = $"{benchmarkCase.Descriptor.Type.Assembly.GetName().Name}-{benchmarkCase.Job.FolderInfo}"; + // The benchmark config built jobs with 2 toolchains, 1 InProcessEmit and 1 Roslyn, + // so we need to subtract 1 from the partition counter to obtain the emit output. NaiveRunnableEmitDiff.RunDiff( - $@"{caseName}.exe", - $@"{caseName}Emitted.dll", + $@"{caseName}-{BuildPartition.s_partitionCounter}.exe", + $@"{caseName}-{BuildPartition.s_partitionCounter - 1}Emitted.dll", ConsoleLogger.Default); } From cd50f7b77139882eba179e96bf6e3afa334aa5fb Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 13 Dec 2024 09:35:20 +0100 Subject: [PATCH 52/57] add HostSignal.AfterProcessStart to allow the users to obtain ID of a process that was started suspended (#2674) --- src/BenchmarkDotNet/Engines/HostSignal.cs | 5 +++++ src/BenchmarkDotNet/Loggers/Broker.cs | 12 +++++++----- .../Toolchains/DotNetCli/DotNetCliExecutor.cs | 5 ++++- src/BenchmarkDotNet/Toolchains/Executor.cs | 2 ++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/HostSignal.cs b/src/BenchmarkDotNet/Engines/HostSignal.cs index f4a2f75a22..cf423d2268 100644 --- a/src/BenchmarkDotNet/Engines/HostSignal.cs +++ b/src/BenchmarkDotNet/Engines/HostSignal.cs @@ -7,6 +7,11 @@ public enum HostSignal ///

BeforeProcessStart, + /// + /// right after we start the benchmarking process + /// + AfterProcessStart, + /// /// before jitting, warmup /// diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index cec11a3091..dcec4c0d56 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -15,9 +15,7 @@ internal class Broker { private readonly ILogger logger; private readonly Process process; - private readonly IDiagnoser diagnoser; private readonly AnonymousPipeServerStream inputFromBenchmark, acknowledgments; - private readonly DiagnoserActionParameters diagnoserActionParameters; private readonly ManualResetEvent finished; public Broker(ILogger logger, Process process, IDiagnoser diagnoser, @@ -25,10 +23,10 @@ public Broker(ILogger logger, Process process, IDiagnoser diagnoser, { this.logger = logger; this.process = process; - this.diagnoser = diagnoser; + this.Diagnoser = diagnoser; this.inputFromBenchmark = inputFromBenchmark; this.acknowledgments = acknowledgments; - diagnoserActionParameters = new DiagnoserActionParameters(process, benchmarkCase, benchmarkId); + DiagnoserActionParameters = new DiagnoserActionParameters(process, benchmarkCase, benchmarkId); finished = new ManualResetEvent(false); Results = new List(); @@ -38,6 +36,10 @@ public Broker(ILogger logger, Process process, IDiagnoser diagnoser, process.Exited += OnProcessExited; } + internal IDiagnoser Diagnoser { get; } + + internal DiagnoserActionParameters DiagnoserActionParameters { get; } + internal List Results { get; } internal List PrefixedOutput { get; } @@ -90,7 +92,7 @@ private void ProcessDataBlocking() } else if (Engine.Signals.TryGetSignal(line, out var signal)) { - diagnoser?.Handle(signal, diagnoserActionParameters); + Diagnoser?.Handle(signal, DiagnoserActionParameters); writer.WriteLine(Engine.Signals.Acknowledgment); diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs index 55815ed005..f8bea8236e 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs @@ -83,9 +83,12 @@ private ExecuteResult Execute(BenchmarkCase benchmarkCase, logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); - diagnoser?.Handle(HostSignal.BeforeProcessStart, new DiagnoserActionParameters(process, benchmarkCase, benchmarkId)); + diagnoser?.Handle(HostSignal.BeforeProcessStart, broker.DiagnoserActionParameters); process.Start(); + + diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); + processOutputReader.BeginRead(); process.EnsureHighPriority(logger); diff --git a/src/BenchmarkDotNet/Toolchains/Executor.cs b/src/BenchmarkDotNet/Toolchains/Executor.cs index 145e527439..8fbce03516 100644 --- a/src/BenchmarkDotNet/Toolchains/Executor.cs +++ b/src/BenchmarkDotNet/Toolchains/Executor.cs @@ -79,6 +79,8 @@ private static ExecuteResult Execute(Process process, BenchmarkCase benchmarkCas return new ExecuteResult(true, null, null, Array.Empty(), Array.Empty(), Array.Empty(), launchIndex); } + broker.Diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); + processOutputReader.BeginRead(); process.EnsureHighPriority(logger); From cac4f6e7342eb28e73f59d21ca87f48cfce7b6ba Mon Sep 17 00:00:00 2001 From: Avishai Dotan <108017307+AvishaiDotan@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:23:46 +0200 Subject: [PATCH 53/57] Feature Request: Add ability to automatically hide metric columns if value is not set (#2673) * Hide the columns described in the issue & add samples for test * Polish sample names * remove irrelevant commit from pr * remove leftovers * Added some helper classes to inject the configuration into the descriptor handler. Added attribute parameters instead of checking the metric.Value directly. * change the injection of configuration * add tests * return the singleton pattern * change private descriptors into internal Implement descriptorConfigInjector base class to keep the code dry * remove DescriptorConfigInjector * remove samples --- .../Attributes/ExceptionDiagnoserAttribute.cs | 6 +- .../Attributes/ExceptionDiagnoserConfig.cs | 20 ++ .../Attributes/ThreadingDiagnoserAttribute.cs | 12 +- .../Diagnosers/ExceptionDiagnoser.cs | 27 +- .../Diagnosers/ThreadingDiagnoser.cs | 42 ++- .../Diagnosers/ThreadingDiagnoserConfig.cs | 23 ++ .../Reports/SummaryTableTests.cs | 308 ++++++++++++++++++ 7 files changed, 421 insertions(+), 17 deletions(-) create mode 100644 src/BenchmarkDotNet/Attributes/ExceptionDiagnoserConfig.cs create mode 100644 src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoserConfig.cs diff --git a/src/BenchmarkDotNet/Attributes/ExceptionDiagnoserAttribute.cs b/src/BenchmarkDotNet/Attributes/ExceptionDiagnoserAttribute.cs index bff956e968..21f2903124 100644 --- a/src/BenchmarkDotNet/Attributes/ExceptionDiagnoserAttribute.cs +++ b/src/BenchmarkDotNet/Attributes/ExceptionDiagnoserAttribute.cs @@ -9,6 +9,10 @@ public class ExceptionDiagnoserAttribute : Attribute, IConfigSource { public IConfig Config { get; } - public ExceptionDiagnoserAttribute() => Config = ManualConfig.CreateEmpty().AddDiagnoser(ExceptionDiagnoser.Default); + /// Display Exceptions column. True by default. + public ExceptionDiagnoserAttribute(bool displayExceptionsIfZeroValue = true) + { + Config = ManualConfig.CreateEmpty().AddDiagnoser(new ExceptionDiagnoser(new ExceptionDiagnoserConfig(displayExceptionsIfZeroValue))); + } } } diff --git a/src/BenchmarkDotNet/Attributes/ExceptionDiagnoserConfig.cs b/src/BenchmarkDotNet/Attributes/ExceptionDiagnoserConfig.cs new file mode 100644 index 0000000000..86f8d99be4 --- /dev/null +++ b/src/BenchmarkDotNet/Attributes/ExceptionDiagnoserConfig.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BenchmarkDotNet.Attributes +{ + public class ExceptionDiagnoserConfig + { + /// Determines whether the Exceptions column is displayed when its value is not calculated. True by default. + + [PublicAPI] + public ExceptionDiagnoserConfig(bool displayExceptionsIfZeroValue = true) + { + DisplayExceptionsIfZeroValue = displayExceptionsIfZeroValue; + } + + public bool DisplayExceptionsIfZeroValue { get; } + } +} diff --git a/src/BenchmarkDotNet/Attributes/ThreadingDiagnoserAttribute.cs b/src/BenchmarkDotNet/Attributes/ThreadingDiagnoserAttribute.cs index 7627170b8b..4ad7651bfc 100644 --- a/src/BenchmarkDotNet/Attributes/ThreadingDiagnoserAttribute.cs +++ b/src/BenchmarkDotNet/Attributes/ThreadingDiagnoserAttribute.cs @@ -1,6 +1,7 @@ using System; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; +using JetBrains.Annotations; namespace BenchmarkDotNet.Attributes { @@ -9,6 +10,15 @@ public class ThreadingDiagnoserAttribute : Attribute, IConfigSource { public IConfig Config { get; } - public ThreadingDiagnoserAttribute() => Config = ManualConfig.CreateEmpty().AddDiagnoser(ThreadingDiagnoser.Default); + //public ThreadingDiagnoserAttribute() => Config = ManualConfig.CreateEmpty().AddDiagnoser(ThreadingDiagnoser.Default); + + /// Display configuration for 'LockContentionCount' when it is empty. True (displayed) by default. + /// Display configuration for 'CompletedWorkItemCount' when it is empty. True (displayed) by default. + + [PublicAPI] + public ThreadingDiagnoserAttribute(bool displayLockContentionWhenZero = true, bool displayCompletedWorkItemCountWhenZero = true) + { + Config = ManualConfig.CreateEmpty().AddDiagnoser(new ThreadingDiagnoser(new ThreadingDiagnoserConfig(displayLockContentionWhenZero, displayCompletedWorkItemCountWhenZero))); + } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Diagnosers/ExceptionDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/ExceptionDiagnoser.cs index 782e895d3e..574d1e54f1 100644 --- a/src/BenchmarkDotNet/Diagnosers/ExceptionDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/ExceptionDiagnoser.cs @@ -1,4 +1,5 @@ using BenchmarkDotNet.Analysers; +using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Exporters; @@ -14,9 +15,11 @@ namespace BenchmarkDotNet.Diagnosers { public class ExceptionDiagnoser : IDiagnoser { - public static readonly ExceptionDiagnoser Default = new ExceptionDiagnoser(); + public static readonly ExceptionDiagnoser Default = new ExceptionDiagnoser(new ExceptionDiagnoserConfig(displayExceptionsIfZeroValue: true)); - private ExceptionDiagnoser() { } + public ExceptionDiagnoser(ExceptionDiagnoserConfig config) => Config = config; + + public ExceptionDiagnoserConfig Config { get; } public IEnumerable Ids => new[] { nameof(ExceptionDiagnoser) }; @@ -32,14 +35,18 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) { } public IEnumerable ProcessResults(DiagnoserResults results) { - yield return new Metric(ExceptionsFrequencyMetricDescriptor.Instance, results.ExceptionFrequency); + yield return new Metric(new ExceptionsFrequencyMetricDescriptor(Config), results.ExceptionFrequency); } public IEnumerable Validate(ValidationParameters validationParameters) => Enumerable.Empty(); - private class ExceptionsFrequencyMetricDescriptor : IMetricDescriptor + internal class ExceptionsFrequencyMetricDescriptor : IMetricDescriptor { - internal static readonly IMetricDescriptor Instance = new ExceptionsFrequencyMetricDescriptor(); + public ExceptionDiagnoserConfig Config { get; } + public ExceptionsFrequencyMetricDescriptor(ExceptionDiagnoserConfig config = null) + { + Config = config; + } public string Id => "ExceptionFrequency"; public string DisplayName => Column.Exceptions; @@ -49,7 +56,13 @@ private class ExceptionsFrequencyMetricDescriptor : IMetricDescriptor public string Unit => "Count"; public bool TheGreaterTheBetter => false; public int PriorityInCategory => 0; - public bool GetIsAvailable(Metric metric) => true; + public bool GetIsAvailable(Metric metric) + { + if (Config == null) + return metric.Value > 0; + else + return Config.DisplayExceptionsIfZeroValue || metric.Value > 0; + } } } -} +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs index dd2a60efca..52059ca21c 100644 --- a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs @@ -15,9 +15,10 @@ namespace BenchmarkDotNet.Diagnosers { public class ThreadingDiagnoser : IDiagnoser { - public static readonly ThreadingDiagnoser Default = new ThreadingDiagnoser(); + public static readonly ThreadingDiagnoser Default = new ThreadingDiagnoser(new ThreadingDiagnoserConfig(displayCompletedWorkItemCountWhenZero: true, displayLockContentionWhenZero: true)); - private ThreadingDiagnoser() { } + public ThreadingDiagnoser(ThreadingDiagnoserConfig config) => Config = config; + public ThreadingDiagnoserConfig Config { get; } public IEnumerable Ids => new[] { nameof(ThreadingDiagnoser) }; @@ -33,8 +34,9 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) { } public IEnumerable ProcessResults(DiagnoserResults results) { - yield return new Metric(CompletedWorkItemCountMetricDescriptor.Instance, results.ThreadingStats.CompletedWorkItemCount / (double)results.ThreadingStats.TotalOperations); - yield return new Metric(LockContentionCountMetricDescriptor.Instance, results.ThreadingStats.LockContentionCount / (double)results.ThreadingStats.TotalOperations); + + yield return new Metric(new CompletedWorkItemCountMetricDescriptor(Config), results.ThreadingStats.CompletedWorkItemCount / (double)results.ThreadingStats.TotalOperations); + yield return new Metric(new LockContentionCountMetricDescriptor(Config), results.ThreadingStats.LockContentionCount / (double)results.ThreadingStats.TotalOperations); } public IEnumerable Validate(ValidationParameters validationParameters) @@ -50,10 +52,15 @@ public IEnumerable Validate(ValidationParameters validationPara } } - private class CompletedWorkItemCountMetricDescriptor : IMetricDescriptor + internal class CompletedWorkItemCountMetricDescriptor : IMetricDescriptor { internal static readonly IMetricDescriptor Instance = new CompletedWorkItemCountMetricDescriptor(); + private ThreadingDiagnoserConfig Config { get; } + public CompletedWorkItemCountMetricDescriptor(ThreadingDiagnoserConfig config = null) + { + Config = config; + } public string Id => "CompletedWorkItemCount"; public string DisplayName => Column.CompletedWorkItems; public string Legend => "The number of work items that have been processed in ThreadPool (per single operation)"; @@ -62,13 +69,26 @@ private class CompletedWorkItemCountMetricDescriptor : IMetricDescriptor public string Unit => "Count"; public bool TheGreaterTheBetter => false; public int PriorityInCategory => 0; - public bool GetIsAvailable(Metric metric) => true; + public bool GetIsAvailable(Metric metric) + { + if (Config == null) + return metric.Value > 0; + else + return Config.DisplayCompletedWorkItemCountWhenZero || metric.Value > 0; + } } - private class LockContentionCountMetricDescriptor : IMetricDescriptor + internal class LockContentionCountMetricDescriptor : IMetricDescriptor { internal static readonly IMetricDescriptor Instance = new LockContentionCountMetricDescriptor(); + private ThreadingDiagnoserConfig Config { get; } + + public LockContentionCountMetricDescriptor(ThreadingDiagnoserConfig config = null) + { + Config = config; + } + public string Id => "LockContentionCount"; public string DisplayName => Column.LockContentions; public string Legend => "The number of times there was contention upon trying to take a Monitor's lock (per single operation)"; @@ -77,7 +97,13 @@ private class LockContentionCountMetricDescriptor : IMetricDescriptor public string Unit => "Count"; public bool TheGreaterTheBetter => false; public int PriorityInCategory => 0; - public bool GetIsAvailable(Metric metric) => true; + public bool GetIsAvailable(Metric metric) + { + if (Config == null) + return metric.Value > 0; + else + return Config.DisplayLockContentionWhenZero || metric.Value > 0; + } } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoserConfig.cs b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoserConfig.cs new file mode 100644 index 0000000000..6af3da25ac --- /dev/null +++ b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoserConfig.cs @@ -0,0 +1,23 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BenchmarkDotNet.Diagnosers +{ + public class ThreadingDiagnoserConfig + { + /// Display configuration for 'LockContentionCount' when it is empty. True (displayed) by default. + /// Display configuration for 'CompletedWorkItemCount' when it is empty. True (displayed) by default. + + [PublicAPI] + public ThreadingDiagnoserConfig(bool displayLockContentionWhenZero = true, bool displayCompletedWorkItemCountWhenZero = true) + { + DisplayLockContentionWhenZero = displayLockContentionWhenZero; + DisplayCompletedWorkItemCountWhenZero = displayCompletedWorkItemCountWhenZero; + } + + public bool DisplayLockContentionWhenZero { get; } + public bool DisplayCompletedWorkItemCountWhenZero { get; } + } +} diff --git a/tests/BenchmarkDotNet.Tests/Reports/SummaryTableTests.cs b/tests/BenchmarkDotNet.Tests/Reports/SummaryTableTests.cs index ebea88b5ad..fb15149aa3 100644 --- a/tests/BenchmarkDotNet.Tests/Reports/SummaryTableTests.cs +++ b/tests/BenchmarkDotNet.Tests/Reports/SummaryTableTests.cs @@ -1,6 +1,8 @@ using System.Linq; +using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; @@ -171,5 +173,311 @@ public void MissingValueInMetricColumnIsNA() // assert Assert.Equal(new[] { "-", "NA" }, actual); } + + #region Issue #2673 + [Fact] + public void DefaultExceptionDiagnoserConfig_WhenExceptionsIsNotZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var exceptionConfig = new ExceptionDiagnoserConfig(); + + var exceptionsFrequencyMetricDescriptor = new ExceptionDiagnoser.ExceptionsFrequencyMetricDescriptor(exceptionConfig); + + var metric = new Metric(exceptionsFrequencyMetricDescriptor, 5); + var metrics = new[] { metric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + var actual = table.Columns.First(c => c.Header == "Exceptions").Content; + + // assert + Assert.Equal(new[] { "5.0000", "5.0000" }, actual); + } + + [Fact] + public void DefaultExceptionDiagnoserConfig_WhenExceptionsIsZero() + { + + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var exceptionConfig = new ExceptionDiagnoserConfig(); + + var exceptionsFrequencyMetricDescriptor = new ExceptionDiagnoser.ExceptionsFrequencyMetricDescriptor(exceptionConfig); + + var metric = new Metric(exceptionsFrequencyMetricDescriptor, 0); + var metrics = new[] { metric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + var actual = table.Columns.First(c => c.Header == "Exceptions").Content; + + // assert + Assert.Equal(new[] { "-", "-" }, actual); + } + + [Fact] + public void HideExceptionDiagnoserConfig_WhenExceptionsIsNotZero() + { + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var exceptionConfig = new ExceptionDiagnoserConfig(displayExceptionsIfZeroValue: false); + + var exceptionsFrequencyMetricDescriptor = new ExceptionDiagnoser.ExceptionsFrequencyMetricDescriptor(exceptionConfig); + + var metric = new Metric(exceptionsFrequencyMetricDescriptor, 5); + var metrics = new[] { metric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + var actual = table.Columns.First(c => c.Header == "Exceptions").Content; + + // assert + Assert.Equal(new[] { "5.0000", "5.0000" }, actual); + } + + [Fact] + public void HideExceptionDiagnoserConfig_WhenExceptionsIsZero() + { + + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var exceptionConfig = new ExceptionDiagnoserConfig(displayExceptionsIfZeroValue: false); + + var exceptionsFrequencyMetricDescriptor = new ExceptionDiagnoser.ExceptionsFrequencyMetricDescriptor(exceptionConfig); + + var metric = new Metric(exceptionsFrequencyMetricDescriptor, 0); + var metrics = new[] { metric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + var isExist = table.Columns.Any(c => c.Header == "Exceptions"); + + // assert + Assert.False(isExist); + } + + [Fact] + public void DefaultThreadingDiagnoserConfig_WhenDescriptorValuesAreNotZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var threadingConfig = new ThreadingDiagnoserConfig(); + + var lockContentionCountMetricDescriptor = new ThreadingDiagnoser.LockContentionCountMetricDescriptor(threadingConfig); + var completedWorkItemCountMetricDescriptor = new ThreadingDiagnoser.CompletedWorkItemCountMetricDescriptor(threadingConfig); + + var lockContentionCountMetric = new Metric(lockContentionCountMetricDescriptor, 5); + var completedWorkItemMetric = new Metric(completedWorkItemCountMetricDescriptor, 5); + var metrics = new[] { lockContentionCountMetric, completedWorkItemMetric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + + var lockContentionCount = table.Columns.FirstOrDefault(c => c.Header == "Lock Contentions").Content; + var completedWorkItemCount = table.Columns.FirstOrDefault(c => c.Header == "Completed Work Items").Content; + + // assert + Assert.Equal(new[] { "5.0000", "5.0000" }, lockContentionCount); + Assert.Equal(new[] { "5.0000", "5.0000" }, completedWorkItemCount); + } + + [Fact] + public void DefaultThreadingDiagnoserConfig_WhenDescriptorValuesAreZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var threadingConfig = new ThreadingDiagnoserConfig(); + + var lockContentionCountMetricDescriptor = new ThreadingDiagnoser.LockContentionCountMetricDescriptor(threadingConfig); + var completedWorkItemCountMetricDescriptor = new ThreadingDiagnoser.CompletedWorkItemCountMetricDescriptor(threadingConfig); + + var lockContentionCountMetric = new Metric(lockContentionCountMetricDescriptor, 0); + var completedWorkItemMetric = new Metric(completedWorkItemCountMetricDescriptor, 0); + var metrics = new[] { lockContentionCountMetric, completedWorkItemMetric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + + var lockContentionCount = table.Columns.FirstOrDefault(c => c.Header == "Lock Contentions").Content; + var completedWorkItemCount = table.Columns.FirstOrDefault(c => c.Header == "Completed Work Items").Content; + + // assert + Assert.Equal(new[] { "-", "-" }, lockContentionCount); + Assert.Equal(new[] { "-", "-" }, completedWorkItemCount); + } + + [Fact] + public void HideLockContentionCountThreadingDiagnoserConfig_WhenDescriptorValuesAreZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var threadingConfig = new ThreadingDiagnoserConfig(displayLockContentionWhenZero: false); + + var lockContentionCountMetricDescriptor = new ThreadingDiagnoser.LockContentionCountMetricDescriptor(threadingConfig); + var completedWorkItemCountMetricDescriptor = new ThreadingDiagnoser.CompletedWorkItemCountMetricDescriptor(threadingConfig); + + var lockContentionCountMetric = new Metric(lockContentionCountMetricDescriptor, 0); + var completedWorkItemMetric = new Metric(completedWorkItemCountMetricDescriptor, 0); + var metrics = new[] { lockContentionCountMetric, completedWorkItemMetric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + + string[]? lockContentionCount = table.Columns?.FirstOrDefault(c => c.Header == "Lock Contentions")?.Content ?? null; + string[]? completedWorkItemCount = table.Columns?.FirstOrDefault(c => c.Header == "Completed Work Items")?.Content ?? null; + + // assert + Assert.Null(lockContentionCount); + Assert.Equal(new[] { "-", "-" }, completedWorkItemCount); + } + + [Fact] + public void HideLockContentionCountThreadingDiagnoserConfig_WhenDescriptorValuesAreNotZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var threadingConfig = new ThreadingDiagnoserConfig(displayLockContentionWhenZero: false); + + var lockContentionCountMetricDescriptor = new ThreadingDiagnoser.LockContentionCountMetricDescriptor(threadingConfig); + var completedWorkItemCountMetricDescriptor = new ThreadingDiagnoser.CompletedWorkItemCountMetricDescriptor(threadingConfig); + + var lockContentionCountMetric = new Metric(lockContentionCountMetricDescriptor, 5); + var completedWorkItemMetric = new Metric(completedWorkItemCountMetricDescriptor, 5); + var metrics = new[] { lockContentionCountMetric, completedWorkItemMetric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + + string[]? lockContentionCount = table.Columns?.FirstOrDefault(c => c.Header == "Lock Contentions")?.Content ?? null; + string[]? completedWorkItemCount = table.Columns?.FirstOrDefault(c => c.Header == "Completed Work Items")?.Content ?? null; + + // assert + Assert.Equal(new[] { "5.0000", "5.0000" }, lockContentionCount); + Assert.Equal(new[] { "5.0000", "5.0000" }, completedWorkItemCount); + } + + [Fact] + public void HideCompletedWorkItemCountThreadingDiagnoserConfig_WhenDescriptorValuesAreZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var threadingConfig = new ThreadingDiagnoserConfig(displayCompletedWorkItemCountWhenZero: false); + + var lockContentionCountMetricDescriptor = new ThreadingDiagnoser.LockContentionCountMetricDescriptor(threadingConfig); + var completedWorkItemCountMetricDescriptor = new ThreadingDiagnoser.CompletedWorkItemCountMetricDescriptor(threadingConfig); + + var lockContentionCountMetric = new Metric(lockContentionCountMetricDescriptor, 0); + var completedWorkItemMetric = new Metric(completedWorkItemCountMetricDescriptor, 0); + var metrics = new[] { lockContentionCountMetric, completedWorkItemMetric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + + string[]? lockContentionCount = table.Columns?.FirstOrDefault(c => c.Header == "Lock Contentions")?.Content ?? null; + string[]? completedWorkItemCount = table.Columns?.FirstOrDefault(c => c.Header == "Completed Work Items")?.Content ?? null; + + // assert + Assert.Null(completedWorkItemCount); + Assert.Equal(new[] { "-", "-" }, lockContentionCount); + } + + [Fact] + public void HideCompletedWorkItemCountThreadingDiagnoserConfig_WhenDescriptorValuesAreNotZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var threadingConfig = new ThreadingDiagnoserConfig(displayCompletedWorkItemCountWhenZero: false); + + var lockContentionCountMetricDescriptor = new ThreadingDiagnoser.LockContentionCountMetricDescriptor(threadingConfig); + var completedWorkItemCountMetricDescriptor = new ThreadingDiagnoser.CompletedWorkItemCountMetricDescriptor(threadingConfig); + + var lockContentionCountMetric = new Metric(lockContentionCountMetricDescriptor, 5); + var completedWorkItemMetric = new Metric(completedWorkItemCountMetricDescriptor, 5); + var metrics = new[] { lockContentionCountMetric, completedWorkItemMetric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + + string[]? lockContentionCount = table.Columns?.FirstOrDefault(c => c.Header == "Lock Contentions")?.Content ?? null; + string[]? completedWorkItemCount = table.Columns?.FirstOrDefault(c => c.Header == "Completed Work Items")?.Content ?? null; + + // assert + Assert.Equal(new[] { "5.0000", "5.0000" }, lockContentionCount); + Assert.Equal(new[] { "5.0000", "5.0000" }, completedWorkItemCount); + } + + [Fact] + public void HideThreadingDiagnoserConfigs_WhenDescriptorValuesAreZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var threadingConfig = new ThreadingDiagnoserConfig(displayCompletedWorkItemCountWhenZero: false, displayLockContentionWhenZero: false); + + var lockContentionCountMetricDescriptor = new ThreadingDiagnoser.LockContentionCountMetricDescriptor(threadingConfig); + var completedWorkItemCountMetricDescriptor = new ThreadingDiagnoser.CompletedWorkItemCountMetricDescriptor(threadingConfig); + + var lockContentionCountMetric = new Metric(lockContentionCountMetricDescriptor, 0); + var completedWorkItemMetric = new Metric(completedWorkItemCountMetricDescriptor, 0); + var metrics = new[] { lockContentionCountMetric, completedWorkItemMetric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + + string[]? lockContentionCount = table.Columns?.FirstOrDefault(c => c.Header == "Lock Contentions")?.Content ?? null; + string[]? completedWorkItemCount = table.Columns?.FirstOrDefault(c => c.Header == "Completed Work Items")?.Content ?? null; + + // assert + Assert.Null(lockContentionCount); + Assert.Null(completedWorkItemCount); + } + + [Fact] + public void DisplayThreadingDiagnoserConfigs_WhenDescriptorValuesAreZero() + { + + // arrange + var config = ManualConfig.Create(DefaultConfig.Instance); + var threadingConfig = new ThreadingDiagnoserConfig(displayCompletedWorkItemCountWhenZero: true, displayLockContentionWhenZero: true); + var lockContentionCountMetricDescriptor = new ThreadingDiagnoser.LockContentionCountMetricDescriptor(threadingConfig); + var completedWorkItemCountMetricDescriptor = new ThreadingDiagnoser.CompletedWorkItemCountMetricDescriptor(threadingConfig); + + var lockContentionCountMetric = new Metric(lockContentionCountMetricDescriptor, 0); + var completedWorkItemMetric = new Metric(completedWorkItemCountMetricDescriptor, 0); + var metrics = new[] { lockContentionCountMetric, completedWorkItemMetric }; + + // act + var summary = MockFactory.CreateSummary(config, hugeSd: false, metrics); + var table = new SummaryTable(summary); + + string[]? lockContentionCount = table.Columns?.FirstOrDefault(c => c.Header == "Lock Contentions")?.Content ?? null; + string[]? completedWorkItemCount = table.Columns?.FirstOrDefault(c => c.Header == "Completed Work Items")?.Content ?? null; + + // assert + Assert.Equal(new[] { "-", "-" }, lockContentionCount); + Assert.Equal(new[] { "-", "-" }, completedWorkItemCount); + } + #endregion } } \ No newline at end of file From 3337a0936c7f44503aa740a9c53e7594fe264967 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:08:21 +0200 Subject: [PATCH 54/57] Add .NET 10 support (#2642) * Add .NET 10 support * Use _ --------- Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> Co-authored-by: Adam Sitnik --- NuGet.Config | 1 + .../Jobs/RuntimeMoniker.cs | 25 +++++++++++++++++++ .../DotMemoryDiagnoser.cs | 7 +++++- .../DotTraceDiagnoser.cs | 7 +++++- .../ConsoleArguments/ConfigParser.cs | 13 ++++++++++ .../Environments/Runtimes/CoreRuntime.cs | 4 ++- .../Environments/Runtimes/MonoRuntime.cs | 1 + .../Environments/Runtimes/NativeAotRuntime.cs | 5 ++++ .../Extensions/RuntimeMonikerExtensions.cs | 10 +++++--- .../Toolchains/CsProj/CsProjCoreToolchain.cs | 1 + .../DotNetCli/MsBuildErrorMapper.cs | 2 ++ .../DotNetCli/NetCoreAppSettings.cs | 1 + .../Toolchains/Mono/MonoToolchain.cs | 1 + .../NativeAot/NativeAotToolchain.cs | 8 ++++++ .../NativeAot/NativeAotToolchainBuilder.cs | 4 +-- .../Toolchains/ToolchainExtensions.cs | 10 ++++++++ .../Validators/DotNetSdkVersionValidator.cs | 5 ++++ .../.template.config/template.json | 8 ++++++ .../.template.config/template.json | 8 ++++++ .../.template.config/template.json | 8 ++++++ .../ConfigParserTests.cs | 3 ++- 21 files changed, 122 insertions(+), 10 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index 855812b5a5..7507704b8b 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -13,5 +13,6 @@ + diff --git a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs index 78541a763f..4ad24192e2 100644 --- a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs +++ b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs @@ -115,6 +115,11 @@ public enum RuntimeMoniker /// Net90, + /// + /// .NET 10.0 + /// + Net10_0, + /// /// NativeAOT compiled as net6.0 /// @@ -135,6 +140,11 @@ public enum RuntimeMoniker /// NativeAot90, + /// + /// NativeAOT compiled as net10.0 + /// + NativeAot10_0, + /// /// WebAssembly with default .Net version /// @@ -165,6 +175,11 @@ public enum RuntimeMoniker /// WasmNet90, + /// + /// WebAssembly with net10.0 + /// + WasmNet10_0, + /// /// Mono with the Ahead of Time LLVM Compiler backend /// @@ -190,6 +205,11 @@ public enum RuntimeMoniker /// MonoAOTLLVMNet90, + /// + /// Mono with the Ahead of Time LLVM Compiler backend and net10.0 + /// + MonoAOTLLVMNet10_0, + /// /// .NET 6 using MonoVM (not CLR which is the default) /// @@ -209,5 +229,10 @@ public enum RuntimeMoniker /// .NET 9 using MonoVM (not CLR which is the default) /// Mono90, + + /// + /// .NET 10 using MonoVM (not CLR which is the default) + /// + Mono10_0, } } diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs index c61f308fcc..b7d23e9275 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs @@ -81,6 +81,7 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.Net70: case RuntimeMoniker.Net80: case RuntimeMoniker.Net90: + case RuntimeMoniker.Net10_0: return true; case RuntimeMoniker.NotRecognized: case RuntimeMoniker.Mono: @@ -88,21 +89,25 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.NativeAot70: case RuntimeMoniker.NativeAot80: case RuntimeMoniker.NativeAot90: + case RuntimeMoniker.NativeAot10_0: case RuntimeMoniker.Wasm: case RuntimeMoniker.WasmNet50: case RuntimeMoniker.WasmNet60: case RuntimeMoniker.WasmNet70: case RuntimeMoniker.WasmNet80: case RuntimeMoniker.WasmNet90: + case RuntimeMoniker.WasmNet10_0: case RuntimeMoniker.MonoAOTLLVM: case RuntimeMoniker.MonoAOTLLVMNet60: case RuntimeMoniker.MonoAOTLLVMNet70: case RuntimeMoniker.MonoAOTLLVMNet80: case RuntimeMoniker.MonoAOTLLVMNet90: + case RuntimeMoniker.MonoAOTLLVMNet10_0: case RuntimeMoniker.Mono60: case RuntimeMoniker.Mono70: case RuntimeMoniker.Mono80: case RuntimeMoniker.Mono90: + case RuntimeMoniker.Mono10_0: #pragma warning disable CS0618 // Type or member is obsolete case RuntimeMoniker.NetCoreApp50: #pragma warning restore CS0618 // Type or member is obsolete @@ -113,4 +118,4 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported"); } } -} \ No newline at end of file +} diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs index b650ab84fa..0dded47443 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs @@ -84,6 +84,7 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.Net70: case RuntimeMoniker.Net80: case RuntimeMoniker.Net90: + case RuntimeMoniker.Net10_0: return true; case RuntimeMoniker.NotRecognized: case RuntimeMoniker.Mono: @@ -91,21 +92,25 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.NativeAot70: case RuntimeMoniker.NativeAot80: case RuntimeMoniker.NativeAot90: + case RuntimeMoniker.NativeAot10_0: case RuntimeMoniker.Wasm: case RuntimeMoniker.WasmNet50: case RuntimeMoniker.WasmNet60: case RuntimeMoniker.WasmNet70: case RuntimeMoniker.WasmNet80: case RuntimeMoniker.WasmNet90: + case RuntimeMoniker.WasmNet10_0: case RuntimeMoniker.MonoAOTLLVM: case RuntimeMoniker.MonoAOTLLVMNet60: case RuntimeMoniker.MonoAOTLLVMNet70: case RuntimeMoniker.MonoAOTLLVMNet80: case RuntimeMoniker.MonoAOTLLVMNet90: + case RuntimeMoniker.MonoAOTLLVMNet10_0: case RuntimeMoniker.Mono60: case RuntimeMoniker.Mono70: case RuntimeMoniker.Mono80: case RuntimeMoniker.Mono90: + case RuntimeMoniker.Mono10_0: #pragma warning disable CS0618 // Type or member is obsolete case RuntimeMoniker.NetCoreApp50: #pragma warning restore CS0618 // Type or member is obsolete @@ -116,4 +121,4 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported"); } } -} \ No newline at end of file +} diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs index 9ec37c39eb..a49d95f1fe 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs @@ -528,6 +528,7 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma case RuntimeMoniker.Net70: case RuntimeMoniker.Net80: case RuntimeMoniker.Net90: + case RuntimeMoniker.Net10_0: return baseJob .WithRuntime(runtimeMoniker.GetRuntime()) .WithToolchain(CsProjCoreToolchain.From(new NetCoreAppSettings(runtimeId, null, runtimeId, options.CliPath?.FullName, options.RestorePath?.FullName))); @@ -547,6 +548,9 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma case RuntimeMoniker.NativeAot90: return CreateAotJob(baseJob, options, runtimeMoniker, "", "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json"); + case RuntimeMoniker.NativeAot10_0: + return CreateAotJob(baseJob, options, runtimeMoniker, "", "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/index.json"); + case RuntimeMoniker.Wasm: return MakeWasmJob(baseJob, options, RuntimeInformation.IsNetCore ? CoreRuntime.GetCurrentVersion().MsBuildMoniker : "net5.0", runtimeMoniker); @@ -565,6 +569,9 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma case RuntimeMoniker.WasmNet90: return MakeWasmJob(baseJob, options, "net9.0", runtimeMoniker); + case RuntimeMoniker.WasmNet10_0: + return MakeWasmJob(baseJob, options, "net10.0", runtimeMoniker); + case RuntimeMoniker.MonoAOTLLVM: return MakeMonoAOTLLVMJob(baseJob, options, RuntimeInformation.IsNetCore ? CoreRuntime.GetCurrentVersion().MsBuildMoniker : "net6.0", runtimeMoniker); @@ -580,6 +587,9 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma case RuntimeMoniker.MonoAOTLLVMNet90: return MakeMonoAOTLLVMJob(baseJob, options, "net9.0", runtimeMoniker); + case RuntimeMoniker.MonoAOTLLVMNet10_0: + return MakeMonoAOTLLVMJob(baseJob, options, "net10.0", runtimeMoniker); + case RuntimeMoniker.Mono60: return MakeMonoJob(baseJob, options, MonoRuntime.Mono60); @@ -592,6 +602,9 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma case RuntimeMoniker.Mono90: return MakeMonoJob(baseJob, options, MonoRuntime.Mono90); + case RuntimeMoniker.Mono10_0: + return MakeMonoJob(baseJob, options, MonoRuntime.Mono10_0); + default: throw new NotSupportedException($"Runtime {runtimeId} is not supported"); } diff --git a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs index 335bd1a871..66f918338c 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs @@ -25,8 +25,9 @@ public class CoreRuntime : Runtime public static readonly CoreRuntime Core70 = new (RuntimeMoniker.Net70, "net7.0", ".NET 7.0"); public static readonly CoreRuntime Core80 = new (RuntimeMoniker.Net80, "net8.0", ".NET 8.0"); public static readonly CoreRuntime Core90 = new (RuntimeMoniker.Net90, "net9.0", ".NET 9.0"); + public static readonly CoreRuntime Core10_0 = new (RuntimeMoniker.Net10_0, "net10.0", ".NET 10.0"); - public static CoreRuntime Latest => Core90; // when dotnet/runtime branches for 10.0, this will need to get updated + public static CoreRuntime Latest => Core10_0; // when dotnet/runtime branches for 11.0, this will need to get updated private CoreRuntime(RuntimeMoniker runtimeMoniker, string msBuildMoniker, string displayName) : base(runtimeMoniker, msBuildMoniker, displayName) @@ -74,6 +75,7 @@ internal static CoreRuntime FromVersion(Version version) case Version v when v.Major == 7 && v.Minor == 0: return GetPlatformSpecific(Core70); case Version v when v.Major == 8 && v.Minor == 0: return GetPlatformSpecific(Core80); case Version v when v.Major == 9 && v.Minor == 0: return GetPlatformSpecific(Core90); + case Version v when v.Major == 10 && v.Minor == 0: return GetPlatformSpecific(Core10_0); default: return version >= new Version(3, 1) ? CreateForNewVersion($"net{version.Major}.{version.Minor}", $".NET {version.Major}.{version.Minor}") diff --git a/src/BenchmarkDotNet/Environments/Runtimes/MonoRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/MonoRuntime.cs index 5862d1390c..15b5a02669 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/MonoRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/MonoRuntime.cs @@ -10,6 +10,7 @@ public class MonoRuntime : Runtime, IEquatable public static readonly MonoRuntime Mono70 = new ("Mono with .NET 7.0", RuntimeMoniker.Mono70, "net7.0", isDotNetBuiltIn: true); public static readonly MonoRuntime Mono80 = new ("Mono with .NET 8.0", RuntimeMoniker.Mono80, "net8.0", isDotNetBuiltIn: true); public static readonly MonoRuntime Mono90 = new ("Mono with .NET 9.0", RuntimeMoniker.Mono90, "net9.0", isDotNetBuiltIn: true); + public static readonly MonoRuntime Mono10_0 = new ("Mono with .NET 10.0", RuntimeMoniker.Mono10_0, "net10.0", isDotNetBuiltIn: true); public string CustomPath { get; } diff --git a/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs index f1f49c5dca..bff018558e 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs @@ -22,6 +22,10 @@ public class NativeAotRuntime : Runtime /// NativeAOT compiled as net9.0 /// public static readonly NativeAotRuntime Net90 = new NativeAotRuntime(RuntimeMoniker.NativeAot90, "net9.0", "NativeAOT 9.0"); + /// + /// NativeAOT compiled as net10.0 + /// + public static readonly NativeAotRuntime Net10_0 = new NativeAotRuntime(RuntimeMoniker.NativeAot10_0, "net10.0", "NativeAOT 10.0"); public override bool IsAOT => true; @@ -48,6 +52,7 @@ public static NativeAotRuntime GetCurrentVersion() case Version v when v.Major == 7 && v.Minor == 0: return Net70; case Version v when v.Major == 8 && v.Minor == 0: return Net80; case Version v when v.Major == 9 && v.Minor == 0: return Net90; + case Version v when v.Major == 10 && v.Minor == 0: return Net10_0; default: return new NativeAotRuntime(RuntimeMoniker.NotRecognized, $"net{version.Major}.{version.Minor}", $"NativeAOT {version.Major}.{version.Minor}"); } diff --git a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs index 03d44c1ba7..ab905bd21e 100644 --- a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs @@ -39,6 +39,8 @@ internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker) return CoreRuntime.Core80; case RuntimeMoniker.Net90: return CoreRuntime.Core90; + case RuntimeMoniker.Net10_0: + return CoreRuntime.Core10_0; case RuntimeMoniker.Mono: return MonoRuntime.Default; case RuntimeMoniker.NativeAot60: @@ -47,16 +49,16 @@ internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker) return NativeAotRuntime.Net70; case RuntimeMoniker.NativeAot80: return NativeAotRuntime.Net80; - case RuntimeMoniker.NativeAot90: - return NativeAotRuntime.Net90; + case RuntimeMoniker.NativeAot10_0: + return NativeAotRuntime.Net10_0; case RuntimeMoniker.Mono60: return MonoRuntime.Mono60; case RuntimeMoniker.Mono70: return MonoRuntime.Mono70; case RuntimeMoniker.Mono80: return MonoRuntime.Mono80; - case RuntimeMoniker.Mono90: - return MonoRuntime.Mono90; + case RuntimeMoniker.Mono10_0: + return MonoRuntime.Mono10_0; default: throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, "Runtime Moniker not supported"); } diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs index 65fa9239d2..5e88641187 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs @@ -29,6 +29,7 @@ public class CsProjCoreToolchain : Toolchain, IEquatable [PublicAPI] public static readonly IToolchain NetCoreApp70 = From(NetCoreAppSettings.NetCoreApp70); [PublicAPI] public static readonly IToolchain NetCoreApp80 = From(NetCoreAppSettings.NetCoreApp80); [PublicAPI] public static readonly IToolchain NetCoreApp90 = From(NetCoreAppSettings.NetCoreApp90); + [PublicAPI] public static readonly IToolchain NetCoreApp10_0 = From(NetCoreAppSettings.NetCoreApp10_0); internal CsProjCoreToolchain(string name, IGenerator generator, IBuilder builder, IExecutor executor, string customDotNetCliPath) : base(name, generator, builder, executor) diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/MsBuildErrorMapper.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/MsBuildErrorMapper.cs index f8f159ff96..9c95be192f 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/MsBuildErrorMapper.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/MsBuildErrorMapper.cs @@ -91,6 +91,8 @@ private static string Map(Capture capture) return "net8.0"; case ".NETCoreApp,Version=v9.0": return "net9.0"; + case ".NETCoreApp,Version=v10.0": + return "net10.0"; default: return capture.Value; // we don't want to throw for future versions of .NET } diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs index 9da421396f..7427b167d1 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs @@ -24,6 +24,7 @@ public class NetCoreAppSettings [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp70 = new ("net7.0", null, ".NET 7.0"); [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp80 = new ("net8.0", null, ".NET 8.0"); [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp90 = new ("net9.0", null, ".NET 9.0"); + [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp10_0 = new ("net10.0", null, ".NET 10.0"); /// /// diff --git a/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs b/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs index 56f2ebaa56..222eb2111b 100644 --- a/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs @@ -12,6 +12,7 @@ public class MonoToolchain : CsProjCoreToolchain, IEquatable [PublicAPI] public static readonly IToolchain Mono70 = From(new NetCoreAppSettings("net7.0", null, "mono70")); [PublicAPI] public static readonly IToolchain Mono80 = From(new NetCoreAppSettings("net8.0", null, "mono80")); [PublicAPI] public static readonly IToolchain Mono90 = From(new NetCoreAppSettings("net9.0", null, "mono90")); + [PublicAPI] public static readonly IToolchain Mono10_0 = From(new NetCoreAppSettings("net10.0", null, "mono10_0")); private MonoToolchain(string name, IGenerator generator, IBuilder builder, IExecutor executor, string customDotNetCliPath) : base(name, generator, builder, executor, customDotNetCliPath) diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs index 2ef6dd8a23..9d476bf0e0 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs @@ -40,6 +40,14 @@ public class NativeAotToolchain : Toolchain .TargetFrameworkMoniker("net9.0") .ToToolchain(); + /// + /// compiled as net10.0, targets latest NativeAOT build from the .NET 10 feed: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/index.json + /// + public static readonly IToolchain Net10_0 = CreateBuilder() + .UseNuGet("", "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/index.json") + .TargetFrameworkMoniker("net10.0") + .ToToolchain(); + internal NativeAotToolchain(string displayName, string ilCompilerVersion, string runtimeFrameworkVersion, string targetFrameworkMoniker, string runtimeIdentifier, diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchainBuilder.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchainBuilder.cs index fc352ea50d..1bd248e5eb 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchainBuilder.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchainBuilder.cs @@ -53,8 +53,8 @@ public NativeAotToolchainBuilder UseLocalBuild(DirectoryInfo ilcPackages) if (!ilcPackages.Exists) throw new DirectoryNotFoundException($"{ilcPackages} provided as {nameof(ilcPackages)} does NOT exist"); Feeds["local"] = ilcPackages.FullName; - ilCompilerVersion = "9.0.0-dev"; - Feeds["dotnet9"] = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json"; + ilCompilerVersion = "10.0.0-dev"; + Feeds["dotnet10"] = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/index.json"; useTempFolderForRestore = true; DisplayName("local ILCompiler build"); diff --git a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs index 75d2898d0a..ba8b3c6863 100644 --- a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs +++ b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs @@ -66,6 +66,7 @@ internal static IToolchain GetToolchain(this Runtime runtime, Descriptor? descri RuntimeMoniker.Mono70 => GetToolchain(RuntimeMoniker.Net70), RuntimeMoniker.Mono80 => GetToolchain(RuntimeMoniker.Net80), RuntimeMoniker.Mono90 => GetToolchain(RuntimeMoniker.Net90), + RuntimeMoniker.Mono10_0 => GetToolchain(RuntimeMoniker.Net10_0), _ => CsProjCoreToolchain.From(new NetCoreAppSettings(mono.MsBuildMoniker, null, mono.Name)) }; } @@ -143,6 +144,9 @@ private static IToolchain GetToolchain(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.Net90: return CsProjCoreToolchain.NetCoreApp90; + case RuntimeMoniker.Net10_0: + return CsProjCoreToolchain.NetCoreApp10_0; + case RuntimeMoniker.NativeAot60: return NativeAotToolchain.Net60; @@ -155,6 +159,9 @@ private static IToolchain GetToolchain(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.NativeAot90: return NativeAotToolchain.Net90; + case RuntimeMoniker.NativeAot10_0: + return NativeAotToolchain.Net10_0; + case RuntimeMoniker.Mono60: return MonoToolchain.Mono60; @@ -167,6 +174,9 @@ private static IToolchain GetToolchain(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.Mono90: return MonoToolchain.Mono90; + case RuntimeMoniker.Mono10_0: + return MonoToolchain.Mono10_0; + default: throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, "RuntimeMoniker not supported"); } diff --git a/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs b/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs index 65474451c0..14965a436d 100644 --- a/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs +++ b/src/BenchmarkDotNet/Validators/DotNetSdkVersionValidator.cs @@ -216,25 +216,30 @@ private static Version GetSdkVersionFromMoniker(RuntimeMoniker runtimeMoniker) RuntimeMoniker.Net70 => new Version(7, 0), RuntimeMoniker.Net80 => new Version(8, 0), RuntimeMoniker.Net90 => new Version(9, 0), + RuntimeMoniker.Net10_0 => new Version(10, 0), RuntimeMoniker.NativeAot60 => new Version(6, 0), RuntimeMoniker.NativeAot70 => new Version(7, 0), RuntimeMoniker.NativeAot80 => new Version(8, 0), RuntimeMoniker.NativeAot90 => new Version(9, 0), + RuntimeMoniker.NativeAot10_0 => new Version(10, 0), RuntimeMoniker.Mono60 => new Version(6, 0), RuntimeMoniker.Mono70 => new Version(7, 0), RuntimeMoniker.Mono80 => new Version(8, 0), RuntimeMoniker.Mono90 => new Version(9, 0), + RuntimeMoniker.Mono10_0 => new Version(10, 0), RuntimeMoniker.Wasm => Portability.RuntimeInformation.IsNetCore && CoreRuntime.TryGetVersion(out var version) ? version : new Version(5, 0), RuntimeMoniker.WasmNet50 => new Version(5, 0), RuntimeMoniker.WasmNet60 => new Version(6, 0), RuntimeMoniker.WasmNet70 => new Version(7, 0), RuntimeMoniker.WasmNet80 => new Version(8, 0), RuntimeMoniker.WasmNet90 => new Version(9, 0), + RuntimeMoniker.WasmNet10_0 => new Version(10, 0), RuntimeMoniker.MonoAOTLLVM => Portability.RuntimeInformation.IsNetCore && CoreRuntime.TryGetVersion(out var version) ? version : new Version(6, 0), RuntimeMoniker.MonoAOTLLVMNet60 => new Version(6, 0), RuntimeMoniker.MonoAOTLLVMNet70 => new Version(7, 0), RuntimeMoniker.MonoAOTLLVMNet80 => new Version(8, 0), RuntimeMoniker.MonoAOTLLVMNet90 => new Version(9, 0), + RuntimeMoniker.MonoAOTLLVMNet10_0 => new Version(10, 0), _ => throw new NotImplementedException($"SDK version check not implemented for {runtimeMoniker}") }; } diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json index 12a8c08785..108d1248cb 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json @@ -34,6 +34,14 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ + { + "choice": "net10.0", + "description": ".NET 10" + }, + { + "choice": "net9.0", + "description": ".NET 9" + }, { "choice": "net8.0", "description": ".NET 8" diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json index ebaaaa1073..27270458e2 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json @@ -34,6 +34,14 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ + { + "choice": "net10.0", + "description": ".NET 10" + }, + { + "choice": "net9.0", + "description": ".NET 9" + }, { "choice": "net8.0", "description": ".NET 8" diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json index bc3989e97d..07ac31147e 100644 --- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json +++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json @@ -34,6 +34,14 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ + { + "choice": "net10.0", + "description": ".NET 10" + }, + { + "choice": "net9.0", + "description": ".NET 9" + }, { "choice": "net8.0", "description": ".NET 8" diff --git a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs index 0e7c439ddf..540c622977 100644 --- a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs @@ -162,7 +162,7 @@ public void SpecifyingCoreRunWithFullFrameworkTargetsMostRecentTfm() CoreRunToolchain coreRunToolchain = (CoreRunToolchain)coreRunJob.GetToolchain(); DotNetCliGenerator generator = (DotNetCliGenerator)coreRunToolchain.Generator; - Assert.Equal("net9.0", generator.TargetFrameworkMoniker); + Assert.Equal("net10.0", generator.TargetFrameworkMoniker); } [FactEnvSpecific("It's impossible to determine TFM for CoreRunToolchain if host process is not .NET (Core) process", EnvRequirement.DotNetCoreOnly)] @@ -388,6 +388,7 @@ public void NetFrameworkMonikerParsedCorrectly(string tfm) [InlineData("net70")] [InlineData("net80")] [InlineData("net90")] + [InlineData("net10_0")] public void NetMonikersAreRecognizedAsNetCoreMonikers(string tfm) { var config = ConfigParser.Parse(new[] { "-r", tfm }, new OutputLogger(Output)).config; From 1aab1c0d7e8398a5e7bb3649f45a5ecab1c8a76f Mon Sep 17 00:00:00 2001 From: Ian Qvist Date: Tue, 7 Jan 2025 16:22:33 +0100 Subject: [PATCH 55/57] Add support for user-supplied project file detection (#2684) * Add support for user-supplied project file detection * Make ProjectLocator fall back to other locators if enabled * Implement most of the suggestion * Use fallback logic instead * Add a few docs and use default code style * Include file locator paths in exception message and rename LocatorArgs to FileLocatorArgs * Update src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> * Create integration test * Support .NET Framework csproj tool chain as well --------- Co-authored-by: Tim Cassell <35501420+timcassell@users.noreply.github.com> --- BenchmarkDotNet.sln | 7 +++ src/BenchmarkDotNet/Configs/DebugConfig.cs | 2 + src/BenchmarkDotNet/Configs/DefaultConfig.cs | 3 ++ src/BenchmarkDotNet/Configs/IConfig.cs | 2 + .../Configs/ImmutableConfig.cs | 7 ++- .../Configs/ImmutableConfigBuilder.cs | 4 +- src/BenchmarkDotNet/Configs/ManualConfig.cs | 12 ++++- .../Locators/FileLocatorArgs.cs | 16 ++++++ .../Locators/FileLocatorType.cs | 6 +++ src/BenchmarkDotNet/Locators/IFileLocator.cs | 22 ++++++++ .../Toolchains/CsProj/CsProjGenerator.cs | 42 ++++++++++++--- .../MonoAotLLVM/MonoAotLLVMGenerator.cs | 2 +- .../Toolchains/MonoWasm/WasmGenerator.cs | 2 +- .../Toolchains/NativeAot/Generator.cs | 10 ++-- .../AssemblyNameIsSetBenchmarks.cs | 13 +++++ ...otNet.IntegrationTests.FileLocators.csproj | 14 +++++ .../BenchmarkDotNet.IntegrationTests.csproj | 2 + .../FileLocatorTests.cs | 54 +++++++++++++++++++ .../Configs/ImmutableConfigTests.cs | 40 ++++++++++++++ 19 files changed, 243 insertions(+), 17 deletions(-) create mode 100644 src/BenchmarkDotNet/Locators/FileLocatorArgs.cs create mode 100644 src/BenchmarkDotNet/Locators/FileLocatorType.cs create mode 100644 src/BenchmarkDotNet/Locators/IFileLocator.cs create mode 100644 tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs create mode 100644 tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj create mode 100644 tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs diff --git a/BenchmarkDotNet.sln b/BenchmarkDotNet.sln index 1df6c0aabd..f47fde8160 100644 --- a/BenchmarkDotNet.sln +++ b/BenchmarkDotNet.sln @@ -59,6 +59,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.P EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.Plotting.Tests", "tests\BenchmarkDotNet.Exporters.Plotting.Tests\BenchmarkDotNet.Exporters.Plotting.Tests.csproj", "{199AC83E-30BD-40CD-87CE-0C838AC0320D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.IntegrationTests.FileLocators", "tests\BenchmarkDotNet.IntegrationTests.FileLocators\BenchmarkDotNet.IntegrationTests.FileLocators.csproj", "{7AD9FCF9-69B5-4984-93AC-D6E30344DADC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -161,6 +163,10 @@ Global {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Debug|Any CPU.Build.0 = Debug|Any CPU {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.ActiveCfg = Release|Any CPU {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.Build.0 = Release|Any CPU + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -190,6 +196,7 @@ Global {2E2283A3-6DA6-4482-8518-99D6D9F689AB} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} {B92ECCEF-7C27-4012-9E19-679F3C40A6A6} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} {199AC83E-30BD-40CD-87CE-0C838AC0320D} = {14195214-591A-45B7-851A-19D3BA2413F9} + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC} = {14195214-591A-45B7-851A-19D3BA2413F9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4D9AF12B-1F7F-45A7-9E8C-E4E46ADCBD1F} diff --git a/src/BenchmarkDotNet/Configs/DebugConfig.cs b/src/BenchmarkDotNet/Configs/DebugConfig.cs index 0fdda7b08d..820fa423c4 100644 --- a/src/BenchmarkDotNet/Configs/DebugConfig.cs +++ b/src/BenchmarkDotNet/Configs/DebugConfig.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -58,6 +59,7 @@ public abstract class DebugConfig : IConfig public IEnumerable GetValidators() => Array.Empty(); public IEnumerable GetColumnProviders() => DefaultColumnProviders.Instance; public IEnumerable GetExporters() => Array.Empty(); + public IEnumerable GetFileLocators() => Array.Empty(); public IEnumerable GetLoggers() => new[] { ConsoleLogger.Default }; public IEnumerable GetDiagnosers() => Array.Empty(); public IEnumerable GetAnalysers() => Array.Empty(); diff --git a/src/BenchmarkDotNet/Configs/DefaultConfig.cs b/src/BenchmarkDotNet/Configs/DefaultConfig.cs index b70333cc1d..6092c05114 100644 --- a/src/BenchmarkDotNet/Configs/DefaultConfig.cs +++ b/src/BenchmarkDotNet/Configs/DefaultConfig.cs @@ -11,6 +11,7 @@ using BenchmarkDotNet.Exporters.Csv; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Portability; @@ -40,6 +41,8 @@ public IEnumerable GetExporters() yield return HtmlExporter.Default; } + public IEnumerable GetFileLocators() => Array.Empty(); + public IEnumerable GetLoggers() { if (LinqPadLogger.IsAvailable) diff --git a/src/BenchmarkDotNet/Configs/IConfig.cs b/src/BenchmarkDotNet/Configs/IConfig.cs index b311c235f5..7335641534 100644 --- a/src/BenchmarkDotNet/Configs/IConfig.cs +++ b/src/BenchmarkDotNet/Configs/IConfig.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -20,6 +21,7 @@ public interface IConfig { IEnumerable GetColumnProviders(); IEnumerable GetExporters(); + IEnumerable GetFileLocators(); IEnumerable GetLoggers(); IEnumerable GetDiagnosers(); IEnumerable GetAnalysers(); diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs index b6e03126fd..1526001fbf 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; @@ -10,6 +10,7 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -24,6 +25,7 @@ public sealed class ImmutableConfig : IConfig // if something is an array here instead of hashset it means it must have a guaranteed order of elements private readonly ImmutableArray columnProviders; private readonly ImmutableArray exporters; + private readonly ImmutableArray fileLocators; private readonly ImmutableHashSet loggers; private readonly ImmutableHashSet diagnosers; private readonly ImmutableHashSet analysers; @@ -41,6 +43,7 @@ internal ImmutableConfig( ImmutableHashSet uniqueHardwareCounters, ImmutableHashSet uniqueDiagnosers, ImmutableArray uniqueExporters, + ImmutableArray uniqueFileLocators, ImmutableHashSet uniqueAnalyzers, ImmutableHashSet uniqueValidators, ImmutableHashSet uniqueFilters, @@ -63,6 +66,7 @@ internal ImmutableConfig( hardwareCounters = uniqueHardwareCounters; diagnosers = uniqueDiagnosers; exporters = uniqueExporters; + fileLocators = uniqueFileLocators; analysers = uniqueAnalyzers; validators = uniqueValidators; filters = uniqueFilters; @@ -92,6 +96,7 @@ internal ImmutableConfig( public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; + public IEnumerable GetFileLocators() => fileLocators; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; public IEnumerable GetAnalysers() => analysers; diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs index d7c0b5eb0f..6394c0e999 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using BenchmarkDotNet.Analysers; @@ -44,6 +44,7 @@ public static ImmutableConfig Create(IConfig source) var uniqueHardwareCounters = source.GetHardwareCounters().Where(counter => counter != HardwareCounter.NotSet).ToImmutableHashSet(); var uniqueDiagnosers = GetDiagnosers(source.GetDiagnosers(), uniqueHardwareCounters); var uniqueExporters = GetExporters(source.GetExporters(), uniqueDiagnosers, configAnalyse); + var uniqueFileLocators = source.GetFileLocators().ToImmutableArray(); var uniqueAnalyzers = GetAnalysers(source.GetAnalysers(), uniqueDiagnosers); var uniqueValidators = GetValidators(source.GetValidators(), MandatoryValidators, source.Options); @@ -61,6 +62,7 @@ public static ImmutableConfig Create(IConfig source) uniqueHardwareCounters, uniqueDiagnosers, uniqueExporters, + uniqueFileLocators, uniqueAnalyzers, uniqueValidators, uniqueFilters, diff --git a/src/BenchmarkDotNet/Configs/ManualConfig.cs b/src/BenchmarkDotNet/Configs/ManualConfig.cs index 5ea1be24e9..c05b5c45db 100644 --- a/src/BenchmarkDotNet/Configs/ManualConfig.cs +++ b/src/BenchmarkDotNet/Configs/ManualConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -11,6 +11,7 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -26,6 +27,7 @@ public class ManualConfig : IConfig private readonly List columnProviders = new List(); private readonly List exporters = new List(); + private readonly List locators = new List(); private readonly List loggers = new List(); private readonly List diagnosers = new List(); private readonly List analysers = new List(); @@ -39,6 +41,7 @@ public class ManualConfig : IConfig public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; + public IEnumerable GetFileLocators() => locators; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; public IEnumerable GetAnalysers() => analysers; @@ -139,6 +142,12 @@ public ManualConfig AddExporter(params IExporter[] newExporters) return this; } + public ManualConfig AddFileLocator(params IFileLocator[] newLocators) + { + locators.AddRange(newLocators); + return this; + } + [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("This method will soon be removed, please start using .AddLogger() instead.")] public void Add(params ILogger[] newLoggers) => AddLogger(newLoggers); @@ -256,6 +265,7 @@ public void Add(IConfig config) { columnProviders.AddRange(config.GetColumnProviders()); exporters.AddRange(config.GetExporters()); + locators.AddRange(config.GetFileLocators()); loggers.AddRange(config.GetLoggers()); diagnosers.AddRange(config.GetDiagnosers()); analysers.AddRange(config.GetAnalysers()); diff --git a/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs b/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs new file mode 100644 index 0000000000..ddbf31d229 --- /dev/null +++ b/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs @@ -0,0 +1,16 @@ +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Running; + +namespace BenchmarkDotNet.Locators; + +public class FileLocatorArgs +{ + public FileLocatorArgs(BenchmarkCase benchmarkCase, ILogger logger) + { + BenchmarkCase = benchmarkCase; + Logger = logger; + } + + public BenchmarkCase BenchmarkCase { get; } + public ILogger Logger { get; } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Locators/FileLocatorType.cs b/src/BenchmarkDotNet/Locators/FileLocatorType.cs new file mode 100644 index 0000000000..5242edfc6c --- /dev/null +++ b/src/BenchmarkDotNet/Locators/FileLocatorType.cs @@ -0,0 +1,6 @@ +namespace BenchmarkDotNet.Locators; + +public enum FileLocatorType +{ + Project +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Locators/IFileLocator.cs b/src/BenchmarkDotNet/Locators/IFileLocator.cs new file mode 100644 index 0000000000..4dd32417e5 --- /dev/null +++ b/src/BenchmarkDotNet/Locators/IFileLocator.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace BenchmarkDotNet.Locators; + +/// +/// Locators can be used to extend the default behavior of finding files +/// +public interface IFileLocator +{ + /// + /// The type of locator + /// + FileLocatorType LocatorType { get; } + + /// + /// Tries to locate a file + /// + /// The arguments such as benchmark and logger + /// The file is provided by the implementation + /// True when a file was successfully found, False otherwise. + bool TryLocate(FileLocatorArgs fileLocatorArgs, out FileInfo fileInfo); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs index 7e91c36ff4..e0684ac9bf 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -11,6 +11,7 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.DotNetCli; @@ -71,7 +72,7 @@ protected override string GetIntermediateDirectoryPath(string buildArtifactsDire protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { var benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); + var projectFile = GetProjectFilePath(benchmark, logger); var xmlDoc = new XmlDocument(); xmlDoc.Load(projectFile.FullName); @@ -246,8 +247,29 @@ private static string GetIndentedXmlString(XmlDocument doc) /// returns a path to the project file which defines the benchmarks /// [PublicAPI] - protected virtual FileInfo GetProjectFilePath(Type benchmarkTarget, ILogger logger) + protected virtual FileInfo GetProjectFilePath(BenchmarkCase benchmark, ILogger logger) { + var args = new FileLocatorArgs(benchmark, logger); + + // Try locators first. Logic is provided by the user for uses-cases such as they have set AssemblyName to a custom value. + var notFound = new List(); + foreach (var locator in benchmark.Config.GetFileLocators()) + { + if (locator.LocatorType != FileLocatorType.Project) + { + continue; + } + + if (locator.TryLocate(args, out var fileInfo)) + { + if (fileInfo.Exists) + return fileInfo; + + notFound.Add(fileInfo.FullName); + } + } + + // Fall back to default project detection logic if (!GetSolutionRootDirectory(out var rootDirectory) && !GetProjectRootDirectory(out rootDirectory)) { logger.WriteLineError( @@ -256,7 +278,7 @@ protected virtual FileInfo GetProjectFilePath(Type benchmarkTarget, ILogger logg } // important assumption! project's file name === output dll name - string projectName = benchmarkTarget.GetTypeInfo().Assembly.GetName().Name; + string projectName = benchmark.Descriptor.Type.GetTypeInfo().Assembly.GetName().Name; var possibleNames = new HashSet { $"{projectName}.csproj", $"{projectName}.fsproj", $"{projectName}.vbproj" }; var projectFiles = rootDirectory @@ -266,12 +288,18 @@ protected virtual FileInfo GetProjectFilePath(Type benchmarkTarget, ILogger logg if (projectFiles.Length == 0) { - throw new NotSupportedException( - $"Unable to find {projectName} in {rootDirectory.FullName} and its subfolders. Most probably the name of output exe is different than the name of the .(c/f)sproj"); + string message; + + if (notFound.Count > 0) + message = $"Unable to find {projectName} in any of the paths: {string.Join(", ", notFound)} or in {rootDirectory.FullName} and its subfolders"; + else + message = $"Unable to find {projectName} in {rootDirectory.FullName} and its subfolders. Most probably the name of output exe is different than the name of the .(c/f)sproj. You can add an IFileLocator to the config if this is on purpose."; + + throw new FileNotFoundException(message); } else if (projectFiles.Length > 1) { - throw new NotSupportedException( + throw new InvalidOperationException( $"Found more than one matching project file for {projectName} in {rootDirectory.FullName} and its subfolders: {string.Join(",", projectFiles.Select(pf => $"'{pf.FullName}'"))}. Benchmark project names needs to be unique."); } diff --git a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs index de3cef53d3..906735a425 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs @@ -28,7 +28,7 @@ public MonoAotLLVMGenerator(string targetFrameworkMoniker, string cliPath, strin protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { BenchmarkCase benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); + var projectFile = GetProjectFilePath(benchmark, logger); string useLLVM = AotCompilerMode == MonoAotCompilerMode.llvm ? "true" : "false"; diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index 7c9aa8826f..e5e4262429 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -41,7 +41,7 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, bool aot, ILogger logger) { BenchmarkCase benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); + var projectFile = GetProjectFilePath(benchmark, logger); WasmRuntime runtime = (WasmRuntime) buildPartition.Runtime; diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs index 061b64a9a8..3c97bab265 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs @@ -151,17 +151,17 @@ private string GenerateProjectForNuGetBuild(BuildPartition buildPartition, Artif
{GetILCompilerPackageReference()} - + - {string.Join(Environment.NewLine, GetRdXmlFiles(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger).Select(file => $""))} + {string.Join(Environment.NewLine, GetRdXmlFiles(buildPartition.RepresentativeBenchmarkCase, logger).Select(file => $""))} {GetCustomProperties(buildPartition, logger)}
"; private string GetCustomProperties(BuildPartition buildPartition, ILogger logger) { - var projectFile = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger); + var projectFile = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase, logger); var xmlDoc = new XmlDocument(); xmlDoc.Load(projectFile.FullName); @@ -186,11 +186,11 @@ private string GetInstructionSetSettings(BuildPartition buildPartition) return !string.IsNullOrEmpty(instructionSet) ? $"{instructionSet}" : ""; } - public IEnumerable GetRdXmlFiles(Type benchmarkTarget, ILogger logger) + public IEnumerable GetRdXmlFiles(BenchmarkCase benchmark, ILogger logger) { yield return GeneratedRdXmlFileName; - var projectFile = GetProjectFilePath(benchmarkTarget, logger); + var projectFile = GetProjectFilePath(benchmark, logger); var projectFileFolder = projectFile.DirectoryName; var rdXml = Path.Combine(projectFileFolder, "rd.xml"); if (File.Exists(rdXml)) diff --git a/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs new file mode 100644 index 0000000000..0dc62277d9 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs @@ -0,0 +1,13 @@ +using BenchmarkDotNet.Attributes; + +namespace BenchmarkDotNet.IntegrationTests.FileLocators +{ + public class AssemblyNameIsSetBenchmarks + { + [Benchmark] + public string Benchmark() + { + return "This will only run when a FileLocator is set due to in the csproj"; + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj new file mode 100644 index 0000000000..3cfa0494e4 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj @@ -0,0 +1,14 @@ + + + + net462;net8.0 + + + MyCustomName + false + false + + + + + \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj index 16f12c41b4..5a08fe5a24 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj @@ -26,11 +26,13 @@ +
+ diff --git a/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs b/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs new file mode 100644 index 0000000000..d41fbf1341 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using System.Linq; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.IntegrationTests.FileLocators; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; +using BenchmarkDotNet.Toolchains.CsProj; +using Xunit; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.IntegrationTests +{ + public class FileLocatorTests : BenchmarkTestExecutor + { + public FileLocatorTests(ITestOutputHelper output) : base(output) { } + + [Fact] + public void ExecutionWithoutFileLocatorShouldFail() + { + var config = ManualConfig.CreateMinimumViable() + .AddJob(Job.Dry + .WithToolchain(CsProjClassicNetToolchain.Net462) + .WithToolchain(CsProjCoreToolchain.NetCoreApp80)); + + var summary = CanExecute(config, false); + Assert.True(summary.Reports.All(r => !r.BuildResult.IsBuildSuccess)); + } + + [Fact] + public void ExecutionWithFileLocatorShouldSucceed() + { + var config = ManualConfig.CreateMinimumViable() + .AddJob(Job.Dry + .WithToolchain(CsProjClassicNetToolchain.Net462) + .WithToolchain(CsProjCoreToolchain.NetCoreApp80)) + .AddFileLocator(new CustomFileLocator()); + + CanExecute(config); + } + + private class CustomFileLocator : IFileLocator + { + public FileLocatorType LocatorType => FileLocatorType.Project; + + public bool TryLocate(FileLocatorArgs args, out FileInfo fileInfo) + { + // We manually locate the csproj file, since the default logic of using the AssemblyName does not work + fileInfo = new FileInfo(Path.Combine(Environment.CurrentDirectory, "../../../../BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj")); + return true; + } + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs index 2393cb737c..d7e17701e6 100644 --- a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs +++ b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Columns; @@ -8,6 +9,7 @@ using BenchmarkDotNet.Environments; using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -452,5 +454,43 @@ public void GenerateWarningWhenExporterDependencyAlreadyExistInConfig() } } + + [Fact] + public void LocatorsAreAddedCorrectly() + { + var mutable = ManualConfig.CreateEmpty(); + + var expected = new TestFileLocator(); + mutable.AddFileLocator(expected); + + var final = ImmutableConfigBuilder.Create(mutable); + + var actual = Assert.Single(final.GetFileLocators()); + Assert.Same(expected, actual); + } + + [Fact] + public void DuplicateLocatorsAreAllowed() + { + var mutable = ManualConfig.CreateEmpty(); + + var locator = new TestFileLocator(); + mutable.AddFileLocator(locator); + mutable.AddFileLocator(locator); + + var final = ImmutableConfigBuilder.Create(mutable); + + Assert.Equal(2, final.GetFileLocators().Count()); + } + + private class TestFileLocator : IFileLocator + { + public FileLocatorType LocatorType => FileLocatorType.Project; + public bool TryLocate(FileLocatorArgs fileLocatorArgs, out FileInfo fileInfo) + { + fileInfo = new FileInfo(""); + return true; + } + } } } From 390463172e86d56a09495ec717d78921082c7394 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 7 Jan 2025 17:28:31 +0100 Subject: [PATCH 56/57] Some minor fixes before new version release (#2686) * Updated per feedback. Updated the ConfigParser TryParse method to take into account _s in net10.0 and beyond enum names, and updated the ConfigParserTests to take this update into account. * update Microsoft.CodeAnalysis.CSharp to 4.12.0 to stop referencing an old version of System.Collections.Immutable * add new test cases, don't remove existing ones --------- Co-authored-by: Parker Bibus --- build/common.props | 2 ++ src/BenchmarkDotNet/BenchmarkDotNet.csproj | 2 +- .../ConsoleArguments/ConfigParser.cs | 13 ++++++++++--- tests/BenchmarkDotNet.Tests/ConfigParserTests.cs | 6 ++++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/build/common.props b/build/common.props index d59d37e8a1..ccae1753c3 100644 --- a/build/common.props +++ b/build/common.props @@ -23,6 +23,8 @@ true annotations + + true diff --git a/src/BenchmarkDotNet/BenchmarkDotNet.csproj b/src/BenchmarkDotNet/BenchmarkDotNet.csproj index d18f3a1ea5..739ecbd85d 100644 --- a/src/BenchmarkDotNet/BenchmarkDotNet.csproj +++ b/src/BenchmarkDotNet/BenchmarkDotNet.csproj @@ -22,9 +22,9 @@ + - diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs index a49d95f1fe..b59836e395 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs @@ -774,10 +774,17 @@ private static string GetCoreRunToolchainDisplayName(IReadOnlyList pat internal static bool TryParse(string runtime, out RuntimeMoniker runtimeMoniker) { int index = runtime.IndexOf('-'); + if (index >= 0) + { + runtime = runtime.Substring(0, index); + } - return index < 0 - ? Enum.TryParse(runtime.Replace(".", string.Empty), ignoreCase: true, out runtimeMoniker) - : Enum.TryParse(runtime.Substring(0, index).Replace(".", string.Empty), ignoreCase: true, out runtimeMoniker); + // Monikers older than Net 10 don't use any version delimiter, newer monikers use _ delimiter. + if (Enum.TryParse(runtime.Replace(".", string.Empty), ignoreCase: true, out runtimeMoniker)) + { + return true; + } + return Enum.TryParse(runtime.Replace('.', '_'), ignoreCase: true, out runtimeMoniker); } } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs index 540c622977..2a4373eef0 100644 --- a/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs +++ b/tests/BenchmarkDotNet.Tests/ConfigParserTests.cs @@ -384,11 +384,17 @@ public void NetFrameworkMonikerParsedCorrectly(string tfm) [Theory] [InlineData("net50")] + [InlineData("net5.0")] [InlineData("net60")] + [InlineData("net6.0")] [InlineData("net70")] + [InlineData("net7.0")] [InlineData("net80")] + [InlineData("net8.0")] [InlineData("net90")] + [InlineData("net9.0")] [InlineData("net10_0")] + [InlineData("net10.0")] public void NetMonikersAreRecognizedAsNetCoreMonikers(string tfm) { var config = ConfigParser.Parse(new[] { "-r", tfm }, new OutputLogger(Output)).config; From 804482dea148cceaacef0ae341f09d872e640b37 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 7 Jan 2025 20:02:44 +0100 Subject: [PATCH 57/57] Revert "Add support for user-supplied project file detection (#2684)" (#2687) This reverts commit 1aab1c0d7e8398a5e7bb3649f45a5ecab1c8a76f. --- BenchmarkDotNet.sln | 7 --- src/BenchmarkDotNet/Configs/DebugConfig.cs | 2 - src/BenchmarkDotNet/Configs/DefaultConfig.cs | 3 -- src/BenchmarkDotNet/Configs/IConfig.cs | 2 - .../Configs/ImmutableConfig.cs | 7 +-- .../Configs/ImmutableConfigBuilder.cs | 4 +- src/BenchmarkDotNet/Configs/ManualConfig.cs | 12 +---- .../Locators/FileLocatorArgs.cs | 16 ------ .../Locators/FileLocatorType.cs | 6 --- src/BenchmarkDotNet/Locators/IFileLocator.cs | 22 -------- .../Toolchains/CsProj/CsProjGenerator.cs | 42 +++------------ .../MonoAotLLVM/MonoAotLLVMGenerator.cs | 2 +- .../Toolchains/MonoWasm/WasmGenerator.cs | 2 +- .../Toolchains/NativeAot/Generator.cs | 10 ++-- .../AssemblyNameIsSetBenchmarks.cs | 13 ----- ...otNet.IntegrationTests.FileLocators.csproj | 14 ----- .../BenchmarkDotNet.IntegrationTests.csproj | 2 - .../FileLocatorTests.cs | 54 ------------------- .../Configs/ImmutableConfigTests.cs | 40 -------------- 19 files changed, 17 insertions(+), 243 deletions(-) delete mode 100644 src/BenchmarkDotNet/Locators/FileLocatorArgs.cs delete mode 100644 src/BenchmarkDotNet/Locators/FileLocatorType.cs delete mode 100644 src/BenchmarkDotNet/Locators/IFileLocator.cs delete mode 100644 tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs delete mode 100644 tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj delete mode 100644 tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs diff --git a/BenchmarkDotNet.sln b/BenchmarkDotNet.sln index f47fde8160..1df6c0aabd 100644 --- a/BenchmarkDotNet.sln +++ b/BenchmarkDotNet.sln @@ -59,8 +59,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.P EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.Plotting.Tests", "tests\BenchmarkDotNet.Exporters.Plotting.Tests\BenchmarkDotNet.Exporters.Plotting.Tests.csproj", "{199AC83E-30BD-40CD-87CE-0C838AC0320D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.IntegrationTests.FileLocators", "tests\BenchmarkDotNet.IntegrationTests.FileLocators\BenchmarkDotNet.IntegrationTests.FileLocators.csproj", "{7AD9FCF9-69B5-4984-93AC-D6E30344DADC}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -163,10 +161,6 @@ Global {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Debug|Any CPU.Build.0 = Debug|Any CPU {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.ActiveCfg = Release|Any CPU {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.Build.0 = Release|Any CPU - {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -196,7 +190,6 @@ Global {2E2283A3-6DA6-4482-8518-99D6D9F689AB} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} {B92ECCEF-7C27-4012-9E19-679F3C40A6A6} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} {199AC83E-30BD-40CD-87CE-0C838AC0320D} = {14195214-591A-45B7-851A-19D3BA2413F9} - {7AD9FCF9-69B5-4984-93AC-D6E30344DADC} = {14195214-591A-45B7-851A-19D3BA2413F9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4D9AF12B-1F7F-45A7-9E8C-E4E46ADCBD1F} diff --git a/src/BenchmarkDotNet/Configs/DebugConfig.cs b/src/BenchmarkDotNet/Configs/DebugConfig.cs index 820fa423c4..0fdda7b08d 100644 --- a/src/BenchmarkDotNet/Configs/DebugConfig.cs +++ b/src/BenchmarkDotNet/Configs/DebugConfig.cs @@ -8,7 +8,6 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -59,7 +58,6 @@ public abstract class DebugConfig : IConfig public IEnumerable GetValidators() => Array.Empty(); public IEnumerable GetColumnProviders() => DefaultColumnProviders.Instance; public IEnumerable GetExporters() => Array.Empty(); - public IEnumerable GetFileLocators() => Array.Empty(); public IEnumerable GetLoggers() => new[] { ConsoleLogger.Default }; public IEnumerable GetDiagnosers() => Array.Empty(); public IEnumerable GetAnalysers() => Array.Empty(); diff --git a/src/BenchmarkDotNet/Configs/DefaultConfig.cs b/src/BenchmarkDotNet/Configs/DefaultConfig.cs index 6092c05114..b70333cc1d 100644 --- a/src/BenchmarkDotNet/Configs/DefaultConfig.cs +++ b/src/BenchmarkDotNet/Configs/DefaultConfig.cs @@ -11,7 +11,6 @@ using BenchmarkDotNet.Exporters.Csv; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Portability; @@ -41,8 +40,6 @@ public IEnumerable GetExporters() yield return HtmlExporter.Default; } - public IEnumerable GetFileLocators() => Array.Empty(); - public IEnumerable GetLoggers() { if (LinqPadLogger.IsAvailable) diff --git a/src/BenchmarkDotNet/Configs/IConfig.cs b/src/BenchmarkDotNet/Configs/IConfig.cs index 7335641534..b311c235f5 100644 --- a/src/BenchmarkDotNet/Configs/IConfig.cs +++ b/src/BenchmarkDotNet/Configs/IConfig.cs @@ -8,7 +8,6 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -21,7 +20,6 @@ public interface IConfig { IEnumerable GetColumnProviders(); IEnumerable GetExporters(); - IEnumerable GetFileLocators(); IEnumerable GetLoggers(); IEnumerable GetDiagnosers(); IEnumerable GetAnalysers(); diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs index 1526001fbf..b6e03126fd 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; @@ -10,7 +10,6 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -25,7 +24,6 @@ public sealed class ImmutableConfig : IConfig // if something is an array here instead of hashset it means it must have a guaranteed order of elements private readonly ImmutableArray columnProviders; private readonly ImmutableArray exporters; - private readonly ImmutableArray fileLocators; private readonly ImmutableHashSet loggers; private readonly ImmutableHashSet diagnosers; private readonly ImmutableHashSet analysers; @@ -43,7 +41,6 @@ internal ImmutableConfig( ImmutableHashSet uniqueHardwareCounters, ImmutableHashSet uniqueDiagnosers, ImmutableArray uniqueExporters, - ImmutableArray uniqueFileLocators, ImmutableHashSet uniqueAnalyzers, ImmutableHashSet uniqueValidators, ImmutableHashSet uniqueFilters, @@ -66,7 +63,6 @@ internal ImmutableConfig( hardwareCounters = uniqueHardwareCounters; diagnosers = uniqueDiagnosers; exporters = uniqueExporters; - fileLocators = uniqueFileLocators; analysers = uniqueAnalyzers; validators = uniqueValidators; filters = uniqueFilters; @@ -96,7 +92,6 @@ internal ImmutableConfig( public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; - public IEnumerable GetFileLocators() => fileLocators; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; public IEnumerable GetAnalysers() => analysers; diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs index 6394c0e999..d7c0b5eb0f 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using BenchmarkDotNet.Analysers; @@ -44,7 +44,6 @@ public static ImmutableConfig Create(IConfig source) var uniqueHardwareCounters = source.GetHardwareCounters().Where(counter => counter != HardwareCounter.NotSet).ToImmutableHashSet(); var uniqueDiagnosers = GetDiagnosers(source.GetDiagnosers(), uniqueHardwareCounters); var uniqueExporters = GetExporters(source.GetExporters(), uniqueDiagnosers, configAnalyse); - var uniqueFileLocators = source.GetFileLocators().ToImmutableArray(); var uniqueAnalyzers = GetAnalysers(source.GetAnalysers(), uniqueDiagnosers); var uniqueValidators = GetValidators(source.GetValidators(), MandatoryValidators, source.Options); @@ -62,7 +61,6 @@ public static ImmutableConfig Create(IConfig source) uniqueHardwareCounters, uniqueDiagnosers, uniqueExporters, - uniqueFileLocators, uniqueAnalyzers, uniqueValidators, uniqueFilters, diff --git a/src/BenchmarkDotNet/Configs/ManualConfig.cs b/src/BenchmarkDotNet/Configs/ManualConfig.cs index c05b5c45db..5ea1be24e9 100644 --- a/src/BenchmarkDotNet/Configs/ManualConfig.cs +++ b/src/BenchmarkDotNet/Configs/ManualConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -11,7 +11,6 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -27,7 +26,6 @@ public class ManualConfig : IConfig private readonly List columnProviders = new List(); private readonly List exporters = new List(); - private readonly List locators = new List(); private readonly List loggers = new List(); private readonly List diagnosers = new List(); private readonly List analysers = new List(); @@ -41,7 +39,6 @@ public class ManualConfig : IConfig public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; - public IEnumerable GetFileLocators() => locators; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; public IEnumerable GetAnalysers() => analysers; @@ -142,12 +139,6 @@ public ManualConfig AddExporter(params IExporter[] newExporters) return this; } - public ManualConfig AddFileLocator(params IFileLocator[] newLocators) - { - locators.AddRange(newLocators); - return this; - } - [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("This method will soon be removed, please start using .AddLogger() instead.")] public void Add(params ILogger[] newLoggers) => AddLogger(newLoggers); @@ -265,7 +256,6 @@ public void Add(IConfig config) { columnProviders.AddRange(config.GetColumnProviders()); exporters.AddRange(config.GetExporters()); - locators.AddRange(config.GetFileLocators()); loggers.AddRange(config.GetLoggers()); diagnosers.AddRange(config.GetDiagnosers()); analysers.AddRange(config.GetAnalysers()); diff --git a/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs b/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs deleted file mode 100644 index ddbf31d229..0000000000 --- a/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Running; - -namespace BenchmarkDotNet.Locators; - -public class FileLocatorArgs -{ - public FileLocatorArgs(BenchmarkCase benchmarkCase, ILogger logger) - { - BenchmarkCase = benchmarkCase; - Logger = logger; - } - - public BenchmarkCase BenchmarkCase { get; } - public ILogger Logger { get; } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Locators/FileLocatorType.cs b/src/BenchmarkDotNet/Locators/FileLocatorType.cs deleted file mode 100644 index 5242edfc6c..0000000000 --- a/src/BenchmarkDotNet/Locators/FileLocatorType.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace BenchmarkDotNet.Locators; - -public enum FileLocatorType -{ - Project -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Locators/IFileLocator.cs b/src/BenchmarkDotNet/Locators/IFileLocator.cs deleted file mode 100644 index 4dd32417e5..0000000000 --- a/src/BenchmarkDotNet/Locators/IFileLocator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.IO; - -namespace BenchmarkDotNet.Locators; - -/// -/// Locators can be used to extend the default behavior of finding files -/// -public interface IFileLocator -{ - /// - /// The type of locator - /// - FileLocatorType LocatorType { get; } - - /// - /// Tries to locate a file - /// - /// The arguments such as benchmark and logger - /// The file is provided by the implementation - /// True when a file was successfully found, False otherwise. - bool TryLocate(FileLocatorArgs fileLocatorArgs, out FileInfo fileInfo); -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs index e0684ac9bf..7e91c36ff4 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -11,7 +11,6 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.DotNetCli; @@ -72,7 +71,7 @@ protected override string GetIntermediateDirectoryPath(string buildArtifactsDire protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { var benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark, logger); + var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); var xmlDoc = new XmlDocument(); xmlDoc.Load(projectFile.FullName); @@ -247,29 +246,8 @@ private static string GetIndentedXmlString(XmlDocument doc) /// returns a path to the project file which defines the benchmarks /// [PublicAPI] - protected virtual FileInfo GetProjectFilePath(BenchmarkCase benchmark, ILogger logger) + protected virtual FileInfo GetProjectFilePath(Type benchmarkTarget, ILogger logger) { - var args = new FileLocatorArgs(benchmark, logger); - - // Try locators first. Logic is provided by the user for uses-cases such as they have set AssemblyName to a custom value. - var notFound = new List(); - foreach (var locator in benchmark.Config.GetFileLocators()) - { - if (locator.LocatorType != FileLocatorType.Project) - { - continue; - } - - if (locator.TryLocate(args, out var fileInfo)) - { - if (fileInfo.Exists) - return fileInfo; - - notFound.Add(fileInfo.FullName); - } - } - - // Fall back to default project detection logic if (!GetSolutionRootDirectory(out var rootDirectory) && !GetProjectRootDirectory(out rootDirectory)) { logger.WriteLineError( @@ -278,7 +256,7 @@ protected virtual FileInfo GetProjectFilePath(BenchmarkCase benchmark, ILogger l } // important assumption! project's file name === output dll name - string projectName = benchmark.Descriptor.Type.GetTypeInfo().Assembly.GetName().Name; + string projectName = benchmarkTarget.GetTypeInfo().Assembly.GetName().Name; var possibleNames = new HashSet { $"{projectName}.csproj", $"{projectName}.fsproj", $"{projectName}.vbproj" }; var projectFiles = rootDirectory @@ -288,18 +266,12 @@ protected virtual FileInfo GetProjectFilePath(BenchmarkCase benchmark, ILogger l if (projectFiles.Length == 0) { - string message; - - if (notFound.Count > 0) - message = $"Unable to find {projectName} in any of the paths: {string.Join(", ", notFound)} or in {rootDirectory.FullName} and its subfolders"; - else - message = $"Unable to find {projectName} in {rootDirectory.FullName} and its subfolders. Most probably the name of output exe is different than the name of the .(c/f)sproj. You can add an IFileLocator to the config if this is on purpose."; - - throw new FileNotFoundException(message); + throw new NotSupportedException( + $"Unable to find {projectName} in {rootDirectory.FullName} and its subfolders. Most probably the name of output exe is different than the name of the .(c/f)sproj"); } else if (projectFiles.Length > 1) { - throw new InvalidOperationException( + throw new NotSupportedException( $"Found more than one matching project file for {projectName} in {rootDirectory.FullName} and its subfolders: {string.Join(",", projectFiles.Select(pf => $"'{pf.FullName}'"))}. Benchmark project names needs to be unique."); } diff --git a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs index 906735a425..de3cef53d3 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs @@ -28,7 +28,7 @@ public MonoAotLLVMGenerator(string targetFrameworkMoniker, string cliPath, strin protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { BenchmarkCase benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark, logger); + var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); string useLLVM = AotCompilerMode == MonoAotCompilerMode.llvm ? "true" : "false"; diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index e5e4262429..7c9aa8826f 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -41,7 +41,7 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, bool aot, ILogger logger) { BenchmarkCase benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark, logger); + var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); WasmRuntime runtime = (WasmRuntime) buildPartition.Runtime; diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs index 3c97bab265..061b64a9a8 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs @@ -151,17 +151,17 @@ private string GenerateProjectForNuGetBuild(BuildPartition buildPartition, Artif {GetILCompilerPackageReference()} - + - {string.Join(Environment.NewLine, GetRdXmlFiles(buildPartition.RepresentativeBenchmarkCase, logger).Select(file => $""))} + {string.Join(Environment.NewLine, GetRdXmlFiles(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger).Select(file => $""))} {GetCustomProperties(buildPartition, logger)} "; private string GetCustomProperties(BuildPartition buildPartition, ILogger logger) { - var projectFile = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase, logger); + var projectFile = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger); var xmlDoc = new XmlDocument(); xmlDoc.Load(projectFile.FullName); @@ -186,11 +186,11 @@ private string GetInstructionSetSettings(BuildPartition buildPartition) return !string.IsNullOrEmpty(instructionSet) ? $"{instructionSet}" : ""; } - public IEnumerable GetRdXmlFiles(BenchmarkCase benchmark, ILogger logger) + public IEnumerable GetRdXmlFiles(Type benchmarkTarget, ILogger logger) { yield return GeneratedRdXmlFileName; - var projectFile = GetProjectFilePath(benchmark, logger); + var projectFile = GetProjectFilePath(benchmarkTarget, logger); var projectFileFolder = projectFile.DirectoryName; var rdXml = Path.Combine(projectFileFolder, "rd.xml"); if (File.Exists(rdXml)) diff --git a/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs deleted file mode 100644 index 0dc62277d9..0000000000 --- a/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs +++ /dev/null @@ -1,13 +0,0 @@ -using BenchmarkDotNet.Attributes; - -namespace BenchmarkDotNet.IntegrationTests.FileLocators -{ - public class AssemblyNameIsSetBenchmarks - { - [Benchmark] - public string Benchmark() - { - return "This will only run when a FileLocator is set due to in the csproj"; - } - } -} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj deleted file mode 100644 index 3cfa0494e4..0000000000 --- a/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net462;net8.0 - - - MyCustomName - false - false - - - - - \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj index 5a08fe5a24..16f12c41b4 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj @@ -26,13 +26,11 @@ - - diff --git a/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs b/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs deleted file mode 100644 index d41fbf1341..0000000000 --- a/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.IntegrationTests.FileLocators; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Locators; -using BenchmarkDotNet.Toolchains.CsProj; -using Xunit; -using Xunit.Abstractions; - -namespace BenchmarkDotNet.IntegrationTests -{ - public class FileLocatorTests : BenchmarkTestExecutor - { - public FileLocatorTests(ITestOutputHelper output) : base(output) { } - - [Fact] - public void ExecutionWithoutFileLocatorShouldFail() - { - var config = ManualConfig.CreateMinimumViable() - .AddJob(Job.Dry - .WithToolchain(CsProjClassicNetToolchain.Net462) - .WithToolchain(CsProjCoreToolchain.NetCoreApp80)); - - var summary = CanExecute(config, false); - Assert.True(summary.Reports.All(r => !r.BuildResult.IsBuildSuccess)); - } - - [Fact] - public void ExecutionWithFileLocatorShouldSucceed() - { - var config = ManualConfig.CreateMinimumViable() - .AddJob(Job.Dry - .WithToolchain(CsProjClassicNetToolchain.Net462) - .WithToolchain(CsProjCoreToolchain.NetCoreApp80)) - .AddFileLocator(new CustomFileLocator()); - - CanExecute(config); - } - - private class CustomFileLocator : IFileLocator - { - public FileLocatorType LocatorType => FileLocatorType.Project; - - public bool TryLocate(FileLocatorArgs args, out FileInfo fileInfo) - { - // We manually locate the csproj file, since the default logic of using the AssemblyName does not work - fileInfo = new FileInfo(Path.Combine(Environment.CurrentDirectory, "../../../../BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj")); - return true; - } - } - } -} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs index d7e17701e6..2393cb737c 100644 --- a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs +++ b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Columns; @@ -9,7 +8,6 @@ using BenchmarkDotNet.Environments; using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -454,43 +452,5 @@ public void GenerateWarningWhenExporterDependencyAlreadyExistInConfig() } } - - [Fact] - public void LocatorsAreAddedCorrectly() - { - var mutable = ManualConfig.CreateEmpty(); - - var expected = new TestFileLocator(); - mutable.AddFileLocator(expected); - - var final = ImmutableConfigBuilder.Create(mutable); - - var actual = Assert.Single(final.GetFileLocators()); - Assert.Same(expected, actual); - } - - [Fact] - public void DuplicateLocatorsAreAllowed() - { - var mutable = ManualConfig.CreateEmpty(); - - var locator = new TestFileLocator(); - mutable.AddFileLocator(locator); - mutable.AddFileLocator(locator); - - var final = ImmutableConfigBuilder.Create(mutable); - - Assert.Equal(2, final.GetFileLocators().Count()); - } - - private class TestFileLocator : IFileLocator - { - public FileLocatorType LocatorType => FileLocatorType.Project; - public bool TryLocate(FileLocatorArgs fileLocatorArgs, out FileInfo fileInfo) - { - fileInfo = new FileInfo(""); - return true; - } - } } }