diff --git a/src/Perfolizer/Perfolizer.Tests/Common/PrecisionHelperTests.cs b/src/Perfolizer/Perfolizer.Tests/Common/PrecisionHelperTests.cs new file mode 100644 index 00000000..0431a500 --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Common/PrecisionHelperTests.cs @@ -0,0 +1,20 @@ +using Perfolizer.Mathematics.Common; + +namespace Perfolizer.Tests.Common; + +public class PrecisionHelperTests +{ + [Theory] + [InlineData(2, 1.0)] + [InlineData(1, 10.0)] + [InlineData(1, 100.0)] + [InlineData(3, 0.1)] + [InlineData(4, 0.01)] + [InlineData(5, 0.001)] + [InlineData(5, 0.001, 10.0)] + public void PrecisionHelperTest(int expected, params double[] values) + { + int actual = PrecisionHelper.GetOptimalPrecision(values); + Assert.Equal(expected, actual); + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Common/SampleTests.cs b/src/Perfolizer/Perfolizer.Tests/Common/SampleTests.cs deleted file mode 100644 index 942217ed..00000000 --- a/src/Perfolizer/Perfolizer.Tests/Common/SampleTests.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Perfolizer.Common; - -namespace Perfolizer.Tests.Common; - -public class SampleTests -{ - [Fact] - public void SampleCtorTest() - { - string expected = "1;2;3"; - string Format(Sample s) => string.Join(";", s.Values); - - Assert.Equal(expected, Format(new Sample(new[] { 1.0, 2.0, 3.0 }))); - Assert.Equal(expected, Format(new Sample(new[] { 1, 2, 3 }))); - Assert.Equal(expected, Format(new Sample(new[] { 1L, 2L, 3L }))); - } -} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Helpers/CpuBrandHelperTests.cs b/src/Perfolizer/Perfolizer.Tests/Helpers/CpuBrandHelperTests.cs new file mode 100644 index 00000000..08c7f625 --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Helpers/CpuBrandHelperTests.cs @@ -0,0 +1,35 @@ +using System.Text; +using Perfolizer.Helpers; +using Perfolizer.Phd.Dto; +using Perfolizer.Tests.Infra; + +namespace Perfolizer.Tests.Helpers +{ + [Collection("VerifyTests")] + public class CpuBrandHelperTests + { + [Fact] + public Task CpuBrandHelperTest01() + { + 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 cpu = new PhdCpu + { + ProcessorName = processorName, + PhysicalProcessorCount = physicalProcessorCount, + PhysicalCoreCount = physicalCoreCount, + LogicalCoreCount = logicalCoreCount, + }; + + captions.AppendLine(cpu.ToFullBrandName()); + } + + var settings = VerifyHelper.CreateSettings("Cpu"); + return Verifier.Verify(captions.ToString(), settings); + } + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Helpers/OsBrandHelperTests.cs b/src/Perfolizer/Perfolizer.Tests/Helpers/OsBrandHelperTests.cs new file mode 100644 index 00000000..94c6beba --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Helpers/OsBrandHelperTests.cs @@ -0,0 +1,63 @@ +using Perfolizer.Helpers; + +namespace Perfolizer.Tests.Helpers; + +public class OsBrandHelperTests(ITestOutputHelper 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(OsBrandHelper.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(OsBrandHelper.Prettify("Windows", originalVersion, ubr.ToString()), 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(OsBrandHelper.PrettifyMacOSX(systemVersion, kernelVersion), prettifiedName); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Helpers/ProcessorBrandStringTests.cs b/src/Perfolizer/Perfolizer.Tests/Helpers/ProcessorBrandStringTests.cs new file mode 100644 index 00000000..37e9058d --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Helpers/ProcessorBrandStringTests.cs @@ -0,0 +1,81 @@ +using Perfolizer.Helpers; +using Perfolizer.Horology; +using Perfolizer.Mathematics.Common; +using Perfolizer.Phd.Dto; + +namespace Perfolizer.Tests.Helpers; + +// ReSharper disable StringLiteralTypo +// ReSharper disable CommentTypo +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 processorName, string cpuBrandName) => + Assert.Equal(cpuBrandName, new PhdCpu { ProcessorName = processorName }.ToShortBrandName()); + + [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 processorName, string brandName, double nominalFrequency) + { + var cpu = new PhdCpu + { + ProcessorName = processorName, + NominalFrequencyHz = Frequency.FromGHz(nominalFrequency).Hertz.RoundToLong() + }; + Assert.Equal(brandName, cpu.ToShortBrandName(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 processorName, + string brandName, + double nominalFrequency, + int? physicalCoreCount, + int? logicalCoreCount) + { + var cpu = new PhdCpu + { + ProcessorName = processorName, + NominalFrequencyHz = Frequency.FromGHz(nominalFrequency).Hertz.RoundToLong(), + PhysicalCoreCount = physicalCoreCount, + LogicalCoreCount = logicalCoreCount + }; + Assert.Equal(brandName, cpu.ToShortBrandName(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, CpuBrandHelper.ParseIntelCoreMicroarchitecture(modelNumber)); + } + + [Theory] + [InlineData("", "Unknown processor")] + [InlineData(null, "Unknown processor")] + public void UnknownProcessorDoesNotThrow(string? processorName, string brandName) + { + var cpu = new PhdCpu { ProcessorName = processorName }; + Assert.Equal(brandName, cpu.ToShortBrandName(includeMaxFrequency: true)); + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Helpers/VerifiedFiles/Cpu.CpuBrandHelperTest01.verified.txt b/src/Perfolizer/Perfolizer.Tests/Helpers/VerifiedFiles/Cpu.CpuBrandHelperTest01.verified.txt new file mode 100644 index 00000000..63212b55 --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Helpers/VerifiedFiles/Cpu.CpuBrandHelperTest01.verified.txt @@ -0,0 +1,192 @@ +Unknown processor +Unknown processor +Unknown processor, 1 logical core +Unknown processor, 2 logical cores +Unknown processor +Unknown processor +Unknown processor, 1 logical core +Unknown processor, 2 logical cores +Unknown processor, 1 physical core +Unknown processor, 1 physical core +Unknown processor, 1 logical core and 1 physical core +Unknown processor, 2 logical cores and 1 physical core +Unknown processor, 2 physical cores +Unknown processor, 2 physical cores +Unknown processor, 1 logical core and 2 physical cores +Unknown processor, 2 logical and 2 physical cores +Unknown processor +Unknown processor +Unknown processor, 1 logical core +Unknown processor, 2 logical cores +Unknown processor +Unknown processor +Unknown processor, 1 logical core +Unknown processor, 2 logical cores +Unknown processor, 1 physical core +Unknown processor, 1 physical core +Unknown processor, 1 logical core and 1 physical core +Unknown processor, 2 logical cores and 1 physical core +Unknown processor, 2 physical cores +Unknown processor, 2 physical cores +Unknown processor, 1 logical core and 2 physical cores +Unknown processor, 2 logical and 2 physical cores +Unknown processor, 1 CPU +Unknown processor, 1 CPU +Unknown processor, 1 CPU, 1 logical core +Unknown processor, 1 CPU, 2 logical cores +Unknown processor, 1 CPU +Unknown processor, 1 CPU +Unknown processor, 1 CPU, 1 logical core +Unknown processor, 1 CPU, 2 logical cores +Unknown processor, 1 CPU, 1 physical core +Unknown processor, 1 CPU, 1 physical core +Unknown processor, 1 CPU, 1 logical core and 1 physical core +Unknown processor, 1 CPU, 2 logical cores and 1 physical core +Unknown processor, 1 CPU, 2 physical cores +Unknown processor, 1 CPU, 2 physical cores +Unknown processor, 1 CPU, 1 logical core and 2 physical cores +Unknown processor, 1 CPU, 2 logical and 2 physical cores +Unknown processor, 2 CPU +Unknown processor, 2 CPU +Unknown processor, 2 CPU, 1 logical core +Unknown processor, 2 CPU, 2 logical cores +Unknown processor, 2 CPU +Unknown processor, 2 CPU +Unknown processor, 2 CPU, 1 logical core +Unknown processor, 2 CPU, 2 logical cores +Unknown processor, 2 CPU, 1 physical core +Unknown processor, 2 CPU, 1 physical core +Unknown processor, 2 CPU, 1 logical core and 1 physical core +Unknown processor, 2 CPU, 2 logical cores and 1 physical core +Unknown processor, 2 CPU, 2 physical cores +Unknown processor, 2 CPU, 2 physical cores +Unknown processor, 2 CPU, 1 logical core and 2 physical cores +Unknown processor, 2 CPU, 2 logical and 2 physical cores +Unknown processor +Unknown processor +Unknown processor, 1 logical core +Unknown processor, 2 logical cores +Unknown processor +Unknown processor +Unknown processor, 1 logical core +Unknown processor, 2 logical cores +Unknown processor, 1 physical core +Unknown processor, 1 physical core +Unknown processor, 1 logical core and 1 physical core +Unknown processor, 2 logical cores and 1 physical core +Unknown processor, 2 physical cores +Unknown processor, 2 physical cores +Unknown processor, 1 logical core and 2 physical cores +Unknown processor, 2 logical and 2 physical cores +Unknown processor +Unknown processor +Unknown processor, 1 logical core +Unknown processor, 2 logical cores +Unknown processor +Unknown processor +Unknown processor, 1 logical core +Unknown processor, 2 logical cores +Unknown processor, 1 physical core +Unknown processor, 1 physical core +Unknown processor, 1 logical core and 1 physical core +Unknown processor, 2 logical cores and 1 physical core +Unknown processor, 2 physical cores +Unknown processor, 2 physical cores +Unknown processor, 1 logical core and 2 physical cores +Unknown processor, 2 logical and 2 physical cores +Unknown processor, 1 CPU +Unknown processor, 1 CPU +Unknown processor, 1 CPU, 1 logical core +Unknown processor, 1 CPU, 2 logical cores +Unknown processor, 1 CPU +Unknown processor, 1 CPU +Unknown processor, 1 CPU, 1 logical core +Unknown processor, 1 CPU, 2 logical cores +Unknown processor, 1 CPU, 1 physical core +Unknown processor, 1 CPU, 1 physical core +Unknown processor, 1 CPU, 1 logical core and 1 physical core +Unknown processor, 1 CPU, 2 logical cores and 1 physical core +Unknown processor, 1 CPU, 2 physical cores +Unknown processor, 1 CPU, 2 physical cores +Unknown processor, 1 CPU, 1 logical core and 2 physical cores +Unknown processor, 1 CPU, 2 logical and 2 physical cores +Unknown processor, 2 CPU +Unknown processor, 2 CPU +Unknown processor, 2 CPU, 1 logical core +Unknown processor, 2 CPU, 2 logical cores +Unknown processor, 2 CPU +Unknown processor, 2 CPU +Unknown processor, 2 CPU, 1 logical core +Unknown processor, 2 CPU, 2 logical cores +Unknown processor, 2 CPU, 1 physical core +Unknown processor, 2 CPU, 1 physical core +Unknown processor, 2 CPU, 1 logical core and 1 physical core +Unknown processor, 2 CPU, 2 logical cores and 1 physical core +Unknown processor, 2 CPU, 2 physical cores +Unknown processor, 2 CPU, 2 physical cores +Unknown processor, 2 CPU, 1 logical core and 2 physical cores +Unknown processor, 2 CPU, 2 logical and 2 physical cores +Intel +Intel +Intel, 1 logical core +Intel, 2 logical cores +Intel +Intel +Intel, 1 logical core +Intel, 2 logical cores +Intel, 1 physical core +Intel, 1 physical core +Intel, 1 logical core and 1 physical core +Intel, 2 logical cores and 1 physical core +Intel, 2 physical cores +Intel, 2 physical cores +Intel, 1 logical core and 2 physical cores +Intel, 2 logical and 2 physical cores +Intel +Intel +Intel, 1 logical core +Intel, 2 logical cores +Intel +Intel +Intel, 1 logical core +Intel, 2 logical cores +Intel, 1 physical core +Intel, 1 physical core +Intel, 1 logical core and 1 physical core +Intel, 2 logical cores and 1 physical core +Intel, 2 physical cores +Intel, 2 physical cores +Intel, 1 logical core and 2 physical cores +Intel, 2 logical and 2 physical cores +Intel, 1 CPU +Intel, 1 CPU +Intel, 1 CPU, 1 logical core +Intel, 1 CPU, 2 logical cores +Intel, 1 CPU +Intel, 1 CPU +Intel, 1 CPU, 1 logical core +Intel, 1 CPU, 2 logical cores +Intel, 1 CPU, 1 physical core +Intel, 1 CPU, 1 physical core +Intel, 1 CPU, 1 logical core and 1 physical core +Intel, 1 CPU, 2 logical cores and 1 physical core +Intel, 1 CPU, 2 physical cores +Intel, 1 CPU, 2 physical cores +Intel, 1 CPU, 1 logical core and 2 physical cores +Intel, 1 CPU, 2 logical and 2 physical cores +Intel, 2 CPU +Intel, 2 CPU +Intel, 2 CPU, 1 logical core +Intel, 2 CPU, 2 logical cores +Intel, 2 CPU +Intel, 2 CPU +Intel, 2 CPU, 1 logical core +Intel, 2 CPU, 2 logical cores +Intel, 2 CPU, 1 physical core +Intel, 2 CPU, 1 physical core +Intel, 2 CPU, 1 logical core and 1 physical core +Intel, 2 CPU, 2 logical cores and 1 physical core +Intel, 2 CPU, 2 physical cores +Intel, 2 CPU, 2 physical cores +Intel, 2 CPU, 1 logical core and 2 physical cores +Intel, 2 CPU, 2 logical and 2 physical cores diff --git a/src/Perfolizer/Perfolizer.Tests/Horology/TimeSpanExtensionsTests.cs b/src/Perfolizer/Perfolizer.Tests/Horology/TimeSpanExtensionsTests.cs index 853e2e15..6d218300 100644 --- a/src/Perfolizer/Perfolizer.Tests/Horology/TimeSpanExtensionsTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Horology/TimeSpanExtensionsTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Horology; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Horology; diff --git a/src/Perfolizer/Perfolizer.Tests/Common/AbsoluteEqualityComparer.cs b/src/Perfolizer/Perfolizer.Tests/Infra/AbsoluteEqualityComparer.cs similarity index 95% rename from src/Perfolizer/Perfolizer.Tests/Common/AbsoluteEqualityComparer.cs rename to src/Perfolizer/Perfolizer.Tests/Infra/AbsoluteEqualityComparer.cs index 8109bed7..fba70315 100644 --- a/src/Perfolizer/Perfolizer.Tests/Common/AbsoluteEqualityComparer.cs +++ b/src/Perfolizer/Perfolizer.Tests/Infra/AbsoluteEqualityComparer.cs @@ -1,4 +1,4 @@ -namespace Perfolizer.Tests.Common; +namespace Perfolizer.Tests.Infra; public class AbsoluteEqualityComparer(double eps) : IEqualityComparer { diff --git a/src/Perfolizer/Perfolizer.Tests/Common/Permutator.cs b/src/Perfolizer/Perfolizer.Tests/Infra/Permutator.cs similarity index 94% rename from src/Perfolizer/Perfolizer.Tests/Common/Permutator.cs rename to src/Perfolizer/Perfolizer.Tests/Infra/Permutator.cs index ab2e6362..b6865794 100644 --- a/src/Perfolizer/Perfolizer.Tests/Common/Permutator.cs +++ b/src/Perfolizer/Perfolizer.Tests/Infra/Permutator.cs @@ -1,4 +1,4 @@ -namespace Perfolizer.Tests.Common; +namespace Perfolizer.Tests.Infra; public static class Permutator { diff --git a/src/Perfolizer/Perfolizer.Tests/Common/TestCultureInfo.cs b/src/Perfolizer/Perfolizer.Tests/Infra/TestCultureInfo.cs similarity index 88% rename from src/Perfolizer/Perfolizer.Tests/Common/TestCultureInfo.cs rename to src/Perfolizer/Perfolizer.Tests/Infra/TestCultureInfo.cs index 4fd3a59a..500c21ee 100644 --- a/src/Perfolizer/Perfolizer.Tests/Common/TestCultureInfo.cs +++ b/src/Perfolizer/Perfolizer.Tests/Infra/TestCultureInfo.cs @@ -1,7 +1,7 @@ using System.Globalization; using Perfolizer.Common; -namespace Perfolizer.Tests.Common; +namespace Perfolizer.Tests.Infra; internal static class TestCultureInfo { diff --git a/src/Perfolizer/Perfolizer.Tests/Common/TestOutputHelperExtensions.cs b/src/Perfolizer/Perfolizer.Tests/Infra/TestOutputHelperExtensions.cs similarity index 96% rename from src/Perfolizer/Perfolizer.Tests/Common/TestOutputHelperExtensions.cs rename to src/Perfolizer/Perfolizer.Tests/Infra/TestOutputHelperExtensions.cs index acac50df..c8dc1c27 100644 --- a/src/Perfolizer/Perfolizer.Tests/Common/TestOutputHelperExtensions.cs +++ b/src/Perfolizer/Perfolizer.Tests/Infra/TestOutputHelperExtensions.cs @@ -1,6 +1,6 @@ using JetBrains.Annotations; -namespace Perfolizer.Tests.Common; +namespace Perfolizer.Tests.Infra; public static class TestOutputHelperExtensions { diff --git a/src/Perfolizer/Perfolizer.Tests/Common/TheoryDataHelper.cs b/src/Perfolizer/Perfolizer.Tests/Infra/TheoryDataHelper.cs similarity index 88% rename from src/Perfolizer/Perfolizer.Tests/Common/TheoryDataHelper.cs rename to src/Perfolizer/Perfolizer.Tests/Infra/TheoryDataHelper.cs index f2c03aa4..a6e9ab21 100644 --- a/src/Perfolizer/Perfolizer.Tests/Common/TheoryDataHelper.cs +++ b/src/Perfolizer/Perfolizer.Tests/Infra/TheoryDataHelper.cs @@ -1,4 +1,4 @@ -namespace Perfolizer.Tests.Common; +namespace Perfolizer.Tests.Infra; public static class TheoryDataHelper { diff --git a/src/Perfolizer/Perfolizer.Tests/Common/TraitConstants.cs b/src/Perfolizer/Perfolizer.Tests/Infra/TraitConstants.cs similarity index 78% rename from src/Perfolizer/Perfolizer.Tests/Common/TraitConstants.cs rename to src/Perfolizer/Perfolizer.Tests/Infra/TraitConstants.cs index 86d9814a..552a4da0 100644 --- a/src/Perfolizer/Perfolizer.Tests/Common/TraitConstants.cs +++ b/src/Perfolizer/Perfolizer.Tests/Infra/TraitConstants.cs @@ -1,4 +1,4 @@ -namespace Perfolizer.Tests.Common; +namespace Perfolizer.Tests.Infra; public static class TraitConstants { diff --git a/src/Perfolizer/Perfolizer.Tests/Infra/VerifyHelper.cs b/src/Perfolizer/Perfolizer.Tests/Infra/VerifyHelper.cs new file mode 100644 index 00000000..82774fda --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Infra/VerifyHelper.cs @@ -0,0 +1,15 @@ +namespace Perfolizer.Tests.Infra; + +public static class VerifyHelper +{ + public static VerifySettings CreateSettings(string? typeName = null) + { + var settings = new VerifySettings(); + settings.UseDirectory("VerifiedFiles"); + settings.DisableDiff(); + if (typeName != null) + settings.UseTypeName(typeName); + + return settings; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Common/RankerTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Common/RankerTests.cs index b7218580..038342e8 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Common/RankerTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Common/RankerTests.cs @@ -4,6 +4,7 @@ using Perfolizer.Mathematics.Common; using Perfolizer.Mathematics.Functions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Common; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Cpd/EdPeltTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Cpd/EdPeltTests.cs index fe17bf00..11ab8443 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Cpd/EdPeltTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Cpd/EdPeltTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Cpd; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Cpd; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Cpd/RqqPeltTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Cpd/RqqPeltTests.cs index ecf4495c..e7ceb9ba 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Cpd/RqqPeltTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Cpd/RqqPeltTests.cs @@ -2,6 +2,7 @@ using Perfolizer.Mathematics.Cpd; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; using Perfolizer.Tests.Mathematics.Cpd.TestDataSets; namespace Perfolizer.Tests.Mathematics.Cpd; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/BetaDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/BetaDistributionTests.cs index 497d86f2..d5907363 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/BetaDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/BetaDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/CauchyDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/CauchyDistributionTests.cs index 5d4c0e9c..2563203b 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/CauchyDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/CauchyDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/DistributionTestsBase.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/DistributionTestsBase.cs index 5abbde43..cf0e8732 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/DistributionTestsBase.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/DistributionTestsBase.cs @@ -2,6 +2,7 @@ using Perfolizer.Mathematics.Common; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/ExponentialDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/ExponentialDistributionTests.cs index e10957b4..3685432d 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/ExponentialDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/ExponentialDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/FrechetDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/FrechetDistributionTests.cs index b84ce38d..c224f274 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/FrechetDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/FrechetDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/GumbelDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/GumbelDistributionTests.cs index 9f5b26f0..74b359ed 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/GumbelDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/GumbelDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/LaplaceDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/LaplaceDistributionTests.cs index 4352c0a0..c690a097 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/LaplaceDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/LaplaceDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/LogNormalDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/LogNormalDistributionTests.cs index f87f7612..f7f488a0 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/LogNormalDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/LogNormalDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/MixtureDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/MixtureDistributionTests.cs index 07068eaa..6c9dafa7 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/MixtureDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/MixtureDistributionTests.cs @@ -2,6 +2,7 @@ using Perfolizer.Extensions; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/NormalDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/NormalDistributionTests.cs index c5864429..3cf5ba5e 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/NormalDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/NormalDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/ParetoDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/ParetoDistributionTests.cs index 61d9cbfc..a0107568 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/ParetoDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/ParetoDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/StudentDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/StudentDistributionTests.cs index a7a456f9..5c1ac754 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/StudentDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/StudentDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/TriangularDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/TriangularDistributionTests.cs index 653114b2..8ec4d79e 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/TriangularDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/TriangularDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; using static System.Math; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/TukeyGhDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/TukeyGhDistributionTests.cs index bd0276e8..195511cf 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/TukeyGhDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/TukeyGhDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/UniformDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/UniformDistributionTests.cs index 39b2f041..7a354465 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/UniformDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/UniformDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/WeibullDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/WeibullDistributionTests.cs index 0fa5b574..e37c83a7 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/WeibullDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/ContinuousDistributions/WeibullDistributionTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.ContinuousDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/DiscreteDistributions/BinomialDistributionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/DiscreteDistributions/BinomialDistributionTests.cs index b125c0e8..e80d4cce 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/DiscreteDistributions/BinomialDistributionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Distributions/DiscreteDistributions/BinomialDistributionTests.cs @@ -2,6 +2,7 @@ using Perfolizer.Mathematics.Common; using Perfolizer.Mathematics.Distributions.DiscreteDistributions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Distributions.DiscreteDistributions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/EffectSizes/GammaEffectSizeTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/EffectSizes/GammaEffectSizeTests.cs index dee3d34c..b2aa504c 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/EffectSizes/GammaEffectSizeTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/EffectSizes/GammaEffectSizeTests.cs @@ -6,6 +6,7 @@ using Perfolizer.Mathematics.EffectSizes; using Perfolizer.Mathematics.Functions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.EffectSizes; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/BetaFunctionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/BetaFunctionTests.cs index 9d077a39..50e2e2ff 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/BetaFunctionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/BetaFunctionTests.cs @@ -1,5 +1,6 @@ using Perfolizer.Mathematics.Functions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Functions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/ErrorFunctionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/ErrorFunctionTests.cs index 24fcb311..ff0f630d 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/ErrorFunctionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/ErrorFunctionTests.cs @@ -1,5 +1,6 @@ using Perfolizer.Mathematics.Functions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Functions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/GammaFunctionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/GammaFunctionTests.cs index 6533c1f0..be022ffe 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/GammaFunctionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/GammaFunctionTests.cs @@ -1,5 +1,6 @@ using Perfolizer.Mathematics.Functions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Functions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/InverseMonotonousFunctionTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/InverseMonotonousFunctionTests.cs index 2d5d8df2..9660b24d 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/InverseMonotonousFunctionTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Functions/InverseMonotonousFunctionTests.cs @@ -1,5 +1,6 @@ using Perfolizer.Mathematics.Functions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Functions; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/GenericEstimators/HodgesLehmannEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/GenericEstimators/HodgesLehmannEstimatorTests.cs index 85efccd7..3e995744 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/GenericEstimators/HodgesLehmannEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/GenericEstimators/HodgesLehmannEstimatorTests.cs @@ -4,6 +4,7 @@ using Perfolizer.Mathematics.GenericEstimators; using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.GenericEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Histograms/HistogramTestHelper.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Histograms/HistogramTestHelper.cs index 25d83257..d254af70 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Histograms/HistogramTestHelper.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Histograms/HistogramTestHelper.cs @@ -4,6 +4,7 @@ using Perfolizer.Mathematics.Histograms; using Perfolizer.Mathematics.Multimodality; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Histograms; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Histograms/MultimodalTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Histograms/MultimodalTests.cs index 47fdeaea..5f544ca9 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Histograms/MultimodalTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Histograms/MultimodalTests.cs @@ -4,6 +4,7 @@ using Perfolizer.Mathematics.Multimodality; using Perfolizer.Mathematics.OutlierDetection; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Histograms; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Modality/ModalityDataFormatterTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Modality/ModalityDataFormatterTests.cs index 7a2a86dd..412c2446 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Modality/ModalityDataFormatterTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Modality/ModalityDataFormatterTests.cs @@ -3,6 +3,7 @@ using Perfolizer.Mathematics.Common; using Perfolizer.Mathematics.Multimodality; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Modality; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Modality/ModalityDetectorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Modality/ModalityDetectorTests.cs index 0bd031bf..0b98b03f 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Modality/ModalityDetectorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Modality/ModalityDetectorTests.cs @@ -7,6 +7,7 @@ using Perfolizer.Mathematics.Multimodality; using Perfolizer.Mathematics.Sequences; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; using Perfolizer.Tests.Mathematics.Modality.TestDataSets; namespace Perfolizer.Tests.Mathematics.Modality; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/DoubleMadOutlierDetectorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/DoubleMadOutlierDetectorTests.cs index 9a5a927a..754c945d 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/DoubleMadOutlierDetectorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/DoubleMadOutlierDetectorTests.cs @@ -3,6 +3,7 @@ using Perfolizer.Mathematics.OutlierDetection; using Perfolizer.Mathematics.ScaleEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.OutlierDetection; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/MadOutlierDetectorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/MadOutlierDetectorTests.cs index c67a4ccb..ef2bfff3 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/MadOutlierDetectorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/MadOutlierDetectorTests.cs @@ -3,6 +3,7 @@ using Perfolizer.Mathematics.OutlierDetection; using Perfolizer.Mathematics.ScaleEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.OutlierDetection; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/OutlierDetectorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/OutlierDetectorTests.cs index 90f4c11e..a0a6db3c 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/OutlierDetectorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/OutlierDetectorTests.cs @@ -1,5 +1,6 @@ using Perfolizer.Mathematics.OutlierDetection; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.OutlierDetection; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/TukeyOutlierDetectorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/TukeyOutlierDetectorTests.cs index 6bf6c496..2c67305a 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/TukeyOutlierDetectorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/OutlierDetection/TukeyOutlierDetectorTests.cs @@ -2,6 +2,7 @@ using Perfolizer.Mathematics.OutlierDetection; using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.OutlierDetection; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/ExtendedP2QuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/ExtendedP2QuantileEstimatorTests.cs index 6940b007..1cb66905 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/ExtendedP2QuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/ExtendedP2QuantileEstimatorTests.cs @@ -5,6 +5,7 @@ using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Mathematics.ScaleEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/HarrellDavisQuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/HarrellDavisQuantileEstimatorTests.cs index d1d67e6a..592aeb64 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/HarrellDavisQuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/HarrellDavisQuantileEstimatorTests.cs @@ -7,6 +7,7 @@ using Perfolizer.Mathematics.Randomization; using Perfolizer.Mathematics.Sequences; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/HyndmanFanQuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/HyndmanFanQuantileEstimatorTests.cs index 49eef0ba..699423e4 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/HyndmanFanQuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/HyndmanFanQuantileEstimatorTests.cs @@ -4,6 +4,7 @@ using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/MovingP2QuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/MovingP2QuantileEstimatorTests.cs index 12550351..ad1d976e 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/MovingP2QuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/MovingP2QuantileEstimatorTests.cs @@ -3,6 +3,7 @@ using Perfolizer.Extensions; using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/MovingQuantileEstimatorTestsBase.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/MovingQuantileEstimatorTestsBase.cs index 3ed3bae6..7779977a 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/MovingQuantileEstimatorTestsBase.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/MovingQuantileEstimatorTestsBase.cs @@ -2,6 +2,7 @@ using Perfolizer.Mathematics.Common; using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/P2QuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/P2QuantileEstimatorTests.cs index 5cda3081..ad48d641 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/P2QuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/P2QuantileEstimatorTests.cs @@ -6,6 +6,7 @@ using Perfolizer.Mathematics.Randomization; using Perfolizer.Mathematics.ScaleEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/PartitioningHeapsMovingQuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/PartitioningHeapsMovingQuantileEstimatorTests.cs index d1bf59c9..c9ed8224 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/PartitioningHeapsMovingQuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/PartitioningHeapsMovingQuantileEstimatorTests.cs @@ -5,6 +5,7 @@ using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Mathematics.Randomization; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/QuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/QuantileEstimatorTests.cs index ed325f59..d30a985d 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/QuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/QuantileEstimatorTests.cs @@ -2,6 +2,7 @@ using Perfolizer.Mathematics.Common; using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/SampleQuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/SampleQuantileEstimatorTests.cs index c6c8f779..8d87d4af 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/SampleQuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/SampleQuantileEstimatorTests.cs @@ -2,6 +2,7 @@ using Perfolizer.Mathematics.Common; using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/SmokeQuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/SmokeQuantileEstimatorTests.cs index 6adef81a..5f26c25a 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/SmokeQuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/SmokeQuantileEstimatorTests.cs @@ -5,6 +5,7 @@ using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; using Range = Perfolizer.Mathematics.Common.Range; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; @@ -68,7 +69,7 @@ static SmokeQuantileEstimatorTests() ("HF8", new HyndmanFanQuantileEstimator(HyndmanFanType.Type8)), ("HF9", new HyndmanFanQuantileEstimator(HyndmanFanType.Type9)), ("HD", HarrellDavisQuantileEstimator.Instance), - ("THD", TrimmedHarrellDavisQuantileEstimator.SqrtInstance), + ("THD", TrimmedHarrellDavisQuantileEstimator.Sqrt), ("SV1", SfakianakisVerginis1QuantileEstimator.Instance), ("SV2", SfakianakisVerginis2QuantileEstimator.Instance), ("SV3", SfakianakisVerginis3QuantileEstimator.Instance), diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/TrimmedHarrellDavisQuantileEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/TrimmedHarrellDavisQuantileEstimatorTests.cs index ced8f9a3..fe924e13 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/TrimmedHarrellDavisQuantileEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/QuantileEstimators/TrimmedHarrellDavisQuantileEstimatorTests.cs @@ -3,6 +3,7 @@ using Perfolizer.Mathematics.QuantileEstimators; using Perfolizer.Mathematics.Sequences; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.QuantileEstimators; @@ -29,7 +30,7 @@ public void EstimationTest01() { var sample = new Sample(new double[] { -3, -2, -1, 0, 1, 2, 3 }); var probabilities = new Probability[] { 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 }; - double[] actualQuantiles = TrimmedHarrellDavisQuantileEstimator.SqrtInstance.Quantiles(sample, probabilities); + double[] actualQuantiles = TrimmedHarrellDavisQuantileEstimator.Sqrt.Quantiles(sample, probabilities); double[] expectedQuantiles = { -3, -2.72276083590394, -2.30045481668633, -1.66479731161074, -0.877210708467137, 2.22044604925031e-16, 0.877210708467138, @@ -43,7 +44,7 @@ public void EstimationTest02() { var sample = new Sample(new ArithmeticProgressionSequence(0, 1).GenerateArray(100)); var probabilities = new Probability[] { 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 }; - double[] actualQuantiles = TrimmedHarrellDavisQuantileEstimator.SqrtInstance.Quantiles(sample, probabilities); + double[] actualQuantiles = TrimmedHarrellDavisQuantileEstimator.Sqrt.Quantiles(sample, probabilities); double[] expectedQuantiles = { 0, 9.23049888501009, 19.1571295075902, 29.235253395819, 39.3599642036428, 49.5, 59.6400357963572, 69.764746604181, @@ -57,7 +58,7 @@ public void EstimationTest03() { var sample = new Sample(new ArithmeticProgressionSequence(0, 1).GenerateArray(1_000_000)); var probabilities = new Probability[] { 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 }; - double[] actualQuantiles = TrimmedHarrellDavisQuantileEstimator.SqrtInstance.Quantiles(sample, probabilities); + double[] actualQuantiles = TrimmedHarrellDavisQuantileEstimator.Sqrt.Quantiles(sample, probabilities); double[] expectedQuantiles = { 0, 99999.2066949439, 199999.152626934, 299999.235056257, 399999.360305779, 499999.5, 599999.639694222, 699999.764943743, diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/RangeEstimator/RangeEstimatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/RangeEstimator/RangeEstimatorTests.cs index c7b299a0..31e682e1 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/RangeEstimator/RangeEstimatorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/RangeEstimator/RangeEstimatorTests.cs @@ -4,6 +4,7 @@ using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Mathematics.Functions; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; using Range = Perfolizer.Mathematics.Common.Range; namespace Perfolizer.Tests.Mathematics.RangeEstimator; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/RangeEstimator/RangeTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/RangeEstimator/RangeTests.cs index 32d78bed..384da834 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/RangeEstimator/RangeTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/RangeEstimator/RangeTests.cs @@ -1,5 +1,6 @@ using System.Globalization; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; using Range = Perfolizer.Mathematics.Common.Range; namespace Perfolizer.Tests.Mathematics.RangeEstimator; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/ScaleEstimators/ScaleEstimatorTestsBase.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/ScaleEstimators/ScaleEstimatorTestsBase.cs index 4126ae39..504897fe 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/ScaleEstimators/ScaleEstimatorTestsBase.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/ScaleEstimators/ScaleEstimatorTestsBase.cs @@ -3,6 +3,7 @@ using Perfolizer.Mathematics.Distributions.ContinuousDistributions; using Perfolizer.Mathematics.ScaleEstimators; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.ScaleEstimators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Selectors/QuickSelectAdaptiveAlgorithmsTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Selectors/QuickSelectAdaptiveAlgorithmsTests.cs index 8aebb424..5549f6ce 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Selectors/QuickSelectAdaptiveAlgorithmsTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Selectors/QuickSelectAdaptiveAlgorithmsTests.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using Perfolizer.Mathematics.Selectors; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Selectors; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/Selectors/SelectorTestBase.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/Selectors/SelectorTestBase.cs index 768da3a6..de06c6ed 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/Selectors/SelectorTestBase.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/Selectors/SelectorTestBase.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.Selectors; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/SequenceGenerators/ExponentialDecaySequenceGeneratorTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/SequenceGenerators/ExponentialDecaySequenceGeneratorTests.cs index ed46d2ba..612e8c0b 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/SequenceGenerators/ExponentialDecaySequenceGeneratorTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/SequenceGenerators/ExponentialDecaySequenceGeneratorTests.cs @@ -1,5 +1,6 @@ using Perfolizer.Mathematics.Sequences; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.SequenceGenerators; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/BrunnerMunzelTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/BrunnerMunzelTests.cs index 03ef47b1..94dd19c6 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/BrunnerMunzelTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/BrunnerMunzelTests.cs @@ -4,6 +4,7 @@ using Perfolizer.Mathematics.SignificanceTesting.Base; using Perfolizer.Metrology; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.SignificanceTesting; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/MannWhitneyTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/MannWhitneyTests.cs index ec9971a7..77ac4559 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/MannWhitneyTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/MannWhitneyTests.cs @@ -8,6 +8,7 @@ using Perfolizer.Mathematics.SignificanceTesting.MannWhitney; using Perfolizer.Metrology; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.SignificanceTesting; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/StudentTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/StudentTests.cs index a4effebd..98a909dd 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/StudentTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/StudentTests.cs @@ -3,6 +3,7 @@ using Perfolizer.Mathematics.SignificanceTesting; using Perfolizer.Mathematics.SignificanceTesting.Base; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.SignificanceTesting; diff --git a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/WelchTests.cs b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/WelchTests.cs index d0edbf8a..9d5fa502 100644 --- a/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/WelchTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Mathematics/SignificanceTesting/WelchTests.cs @@ -4,6 +4,7 @@ using Perfolizer.Mathematics.SignificanceTesting.Base; using Perfolizer.Metrology; using Perfolizer.Tests.Common; +using Perfolizer.Tests.Infra; namespace Perfolizer.Tests.Mathematics.SignificanceTesting; diff --git a/src/Perfolizer/Perfolizer.Tests/Metrology/MetrologyTests.cs b/src/Perfolizer/Perfolizer.Tests/Metrology/MetrologyTests.cs index 4bd9edd2..010c5515 100644 --- a/src/Perfolizer/Perfolizer.Tests/Metrology/MetrologyTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/Metrology/MetrologyTests.cs @@ -27,7 +27,7 @@ public class MetrologyTests [InlineData("20GHz")] public void MeasurementUnitToStringParseTest(string s) { - if (!MeasurementValue.TryParse(s, out var value)) + if (!Measurement.TryParse(s, out var value)) throw new Exception($"Failed to parse '{s}'"); Assert.Equal(s, value.ToString()); } diff --git a/src/Perfolizer/Perfolizer.Tests/Perfolizer.Tests.csproj b/src/Perfolizer/Perfolizer.Tests/Perfolizer.Tests.csproj index 8467bee4..6b5253ad 100644 --- a/src/Perfolizer/Perfolizer.Tests/Perfolizer.Tests.csproj +++ b/src/Perfolizer/Perfolizer.Tests/Perfolizer.Tests.csproj @@ -7,16 +7,27 @@ - - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/JsonHelper.cs b/src/Perfolizer/Perfolizer.Tests/Phd/JsonHelper.cs new file mode 100644 index 00000000..c599b380 --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/JsonHelper.cs @@ -0,0 +1,34 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using Perfolizer.Phd; +using Perfolizer.Phd.Base; + +namespace Perfolizer.Tests.Phd; + +public class PolymorphicTypeResolver(PhdSchema schema) : DefaultJsonTypeInfoResolver +{ + public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) + { + var jsonTypeInfo = base.GetTypeInfo(type, options); + + foreach (var implementation in schema.Implementations) + { + if (jsonTypeInfo.Type == implementation.Base) + { + jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions + { + TypeDiscriminatorPropertyName = "$type", + IgnoreUnrecognizedTypeDiscriminators = true, + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization, + DerivedTypes = + { + new JsonDerivedType(implementation.Derived, schema.Name) + } + }; + } + } + + return jsonTypeInfo; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/ModuleInit.cs b/src/Perfolizer/Perfolizer.Tests/Phd/ModuleInit.cs new file mode 100644 index 00000000..7f995eb6 --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/ModuleInit.cs @@ -0,0 +1,21 @@ +using System.Runtime.CompilerServices; + +namespace Perfolizer.Tests.Phd; + +public static class ModuleInit +{ + [ModuleInitializer] + public static void Init() + { + VerifierSettings.UseStrictJson(); + VerifierSettings.AutoVerify(); + VerifierSettings.DontScrubGuids(); + VerifierSettings.DontScrubDateTimes(); + VerifierSettings.DontSortDictionaries(); + } + + [ModuleInitializer] + public static void InitDerivePathInfo() => + DerivePathInfo( + (_, _, type, method) => new(AttributeReader.GetProjectDirectory(), typeName: type.Name, method.Name)); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/PhdBdnTests.cs b/src/Perfolizer/Perfolizer.Tests/Phd/PhdBdnTests.cs new file mode 100644 index 00000000..36b6260d --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/PhdBdnTests.cs @@ -0,0 +1,80 @@ +using JetBrains.Annotations; +using Perfolizer.Horology; +using Perfolizer.Phd; + +namespace Perfolizer.Tests.Phd; + +// public class PhdBdnTests : PhdTestsBase +// { +// [Fact] +// public Task PhdBdn() +// { +// PerfMetric Metric(double value, int iterationIndex) => new() +// { Value = value, Unit = TimeUnit.Nanosecond, IterationIndex = iterationIndex, InvocationCount = 2 }; +// +// var root = new PhdAttributes +// { +// Info = new BdnInfo { Title = "BenchmarkDotNet.Samples.IntroExportJson-20240309-013216" }, +// Host = new BdnHost +// { +// BenchmarkDotNetCaption = "BenchmarkDotNet", +// BenchmarkDotNetVersion = "0.13.13-develop (2024-03-09)", +// OsVersion = "macOS Sonoma 14.2.1 (23C71) [Darwin 23.2.0]", +// ProcessorName = "Apple M1 Max", +// PhysicalProcessorCount = "1", +// PhysicalCoreCount = "10", +// LogicalCoreCount = "10", +// RuntimeVersion = ".NET 8.0.0 (8.0.23.53103)", +// Architecture = "Arm64", +// HasAttachedDebugger = false, +// HasRyuJit = true, +// Configuration = "RELEASE", +// DotNetSdkVersion = "8.0.100", +// ChronometerFrequency = 1000000000, +// HardwareTimerKind = "Unknown" +// } +// }.ToPerfEntry().Add( +// new PhdAttributes +// { +// Descriptor = new BdnDescriptor +// { +// DisplayInfo = "BenchmarkDotNet.Samples.IntroExportJson-20240309-013216", +// Namespace = "BenchmarkDotNet.Samples", +// Type = "IntroExportJson", +// Method = "ExportJson", +// MethodTitle = "ExportJson", +// Parameters = "Foo=1" +// } +// }.ToPerfEntry() +// .Add(new PhdAttributes +// { +// Lifecycle = new BdnLifecycle +// { +// IterationMode = "Pilot", +// IterationStage = "Overhead", +// LaunchIndex = 0 +// } +// }.ToPerfEntry() +// .Add(Metric(12, 1)) +// .Add(Metric(13, 2)) +// .Add(Metric(14, 3))) +// .Add(new PhdAttributes +// { +// Lifecycle = new BdnLifecycle +// { +// IterationMode = "Result", +// IterationStage = "Workload", +// LaunchIndex = 1 +// } +// }.ToPerfEntry() +// .Add(Metric(15, 1)) +// .Add(Metric(16, 2)) +// .Add(Metric(17, 3))) +// ); +// +// +// return VerifyPhd(root, schema); +// } +// +// +// } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/PhdEmptyTests.cs b/src/Perfolizer/Perfolizer.Tests/Phd/PhdEmptyTests.cs new file mode 100644 index 00000000..5e771129 --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/PhdEmptyTests.cs @@ -0,0 +1,11 @@ +using Perfolizer.Phd; +using Perfolizer.Phd.Base; +using Perfolizer.Phd.Dto; + +namespace Perfolizer.Tests.Phd; + +public class PhdEmptyTests : PhdTestsBase +{ + [Fact] + public Task PhdEmpty() => VerifyPhd(new PhdEntry(new PhdAttributes()), new PhdSchema("")); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/PhdPerforatorTests.cs b/src/Perfolizer/Perfolizer.Tests/Phd/PhdPerforatorTests.cs new file mode 100644 index 00000000..29a8b0f7 --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/PhdPerforatorTests.cs @@ -0,0 +1,93 @@ +using JetBrains.Annotations; +using Perfolizer.Horology; +using Perfolizer.Mathematics.Distributions.ContinuousDistributions; +using Perfolizer.Metrology; +using Perfolizer.Phd; +using Perfolizer.Phd.Base; +using Perfolizer.Phd.Dto; + +namespace Perfolizer.Tests.Phd; + +public class PhdPerforatorTests : PhdTestsBase +{ + [Fact] + public Task PhdPerforator() + { + var runId = Guid.Parse("11214D6B-4E25-44A4-8032-D4290C9F5617"); + var random = new NormalDistribution(10).Random(1729); + double NextValue() => Math.Round(random.Next(), 3); + int minute = 0; + + PhdEntry CreateEntry(string benchmarkId) => new PhdAttributes + { + Info = new PhdInfo + { + RunId = runId, + Timestamp = DateTimeOffset.Parse($"2021-01-01T00:0{minute++}:00Z").ToUnixTimeMilliseconds() + }, + Source = new PerforatorSource + { + BuildId = 123456, + Branch = "main", + ConfigurationId = "Configuration1", + }, + Host = new PerforatorHost + { + Os = "Linux-x64", + ImageId = "Environment1", + }, + Benchmark = new PerforatorBenchmark + { + BenchmarkId = benchmarkId, + BenchmarkVersion = 1, + }, + }.ToPerfEntry() + .Add(new PhdMetric { Id = "stage1", Value = NextValue(), Unit = TimeUnit.Millisecond }) + .Add(new PhdMetric { Id = "stage2", Version = 2, Value = NextValue(), Unit = TimeUnit.Millisecond }) + .Add(new PhdMetric { Id = "totalTime", Value = NextValue(), Unit = TimeUnit.Millisecond }) + .Add(new PhdMetric { Id = "Footprint", Value = 20, Unit = SizeUnit.MB }) + .Add(new PhdMetric { Id = "Gc.CollectCount", Value = 3 }); + + var root = new PhdAttributes + { + Info = new PhdInfo { Title = "Perforator Measurements" } + }.ToPerfEntry() + .Add(CreateEntry("benchmark1")) + .Add(CreateEntry("benchmark1")) + .Add(CreateEntry("benchmark2")) + .Add(CreateEntry("benchmark2")); + + var schema = new PhdSchema("perforator") + .Add() + .Add() + .Add() + .Add(); + + return VerifyPhd(root, schema); + } + + [PublicAPI] + private class PerforatorHost : PhdHost + { + public string Os { get; set; } = ""; + public string ImageId { get; set; } = ""; + } + + [PublicAPI] + private class PerforatorExecution : PhdExecution { } + + [PublicAPI] + private class PerforatorBenchmark : PhdBenchmark + { + public string BenchmarkId { get; set; } = ""; + public int BenchmarkVersion { get; set; } + } + + [PublicAPI] + private class PerforatorSource : PhdSource + { + public int BuildId { get; set; } + public string ConfigurationId { get; set; } = ""; + public string Branch { get; set; } = ""; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/PhdTestsBase.cs b/src/Perfolizer/Perfolizer.Tests/Phd/PhdTestsBase.cs new file mode 100644 index 00000000..03ccbd1f --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/PhdTestsBase.cs @@ -0,0 +1,23 @@ +using Perfolizer.Json; +using Perfolizer.Phd.Base; +using Perfolizer.Tests.Infra; + +namespace Perfolizer.Tests.Phd; + +public class PhdTestsBase +{ + protected static Task VerifyPhd(PhdEntry entry, PhdSchema schema) + { + // JsonSerializerOptions jsonOptions = new() + // { + // DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + // PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + // TypeInfoResolver = new PolymorphicTypeResolver(schema) + // }; + // string json = JsonSerializer.Serialize(entry.Serialize(), jsonOptions); + string json = LightJsonSerializer.Serialize(entry.ToRaw()); + return VerifyJson(json, CreateSettings()); + } + + private static VerifySettings CreateSettings() => VerifyHelper.CreateSettings("Phd"); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdBdn.verified.json b/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdBdn.verified.json new file mode 100644 index 00000000..4fddf364 --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdBdn.verified.json @@ -0,0 +1,98 @@ +{ + "attributes": { + "info": { + "title": "BenchmarkDotNet.Samples.IntroExportJson-20240309-013216" + }, + "environment": { + "benchmarkDotNetCaption": "BenchmarkDotNet", + "benchmarkDotNetVersion": "0.13.13-develop (2024-03-09)", + "osVersion": "macOS Sonoma 14.2.1 (23C71) [Darwin 23.2.0]", + "processorName": "Apple M1 Max", + "physicalProcessorCount": "1", + "physicalCoreCount": "10", + "logicalCoreCount": "10", + "runtimeVersion": ".NET 8.0.0 (8.0.23.53103)", + "architecture": "Arm64", + "hasAttachedDebugger": false, + "hasRyuJit": true, + "configuration": "RELEASE", + "dotNetSdkVersion": "8.0.100", + "chronometerFrequency": 1000000000, + "hardwareTimerKind": "Unknown" + } + }, + "children": [ + { + "attributes": { + "descriptor": { + "displayInfo": "BenchmarkDotNet.Samples.IntroExportJson-20240309-013216", + "namespace": "BenchmarkDotNet.Samples", + "type": "IntroExportJson", + "method": "ExportJson", + "methodTitle": "ExportJson", + "parameters": "Foo=1" + } + }, + "children": [ + { + "attributes": { + "lifecycle": { + "launchIndex": 0, + "iterationMode": "Pilot", + "iterationStage": "Overhead" + } + }, + "metrics": [ + { + "iterationIndex": 1, + "invocationCount": 2, + "value": 12, + "unit": "ns" + }, + { + "iterationIndex": 2, + "invocationCount": 2, + "value": 13, + "unit": "ns" + }, + { + "iterationIndex": 3, + "invocationCount": 2, + "value": 14, + "unit": "ns" + } + ] + }, + { + "attributes": { + "lifecycle": { + "launchIndex": 1, + "iterationMode": "Result", + "iterationStage": "Workload" + } + }, + "metrics": [ + { + "iterationIndex": 1, + "invocationCount": 2, + "value": 15, + "unit": "ns" + }, + { + "iterationIndex": 2, + "invocationCount": 2, + "value": 16, + "unit": "ns" + }, + { + "iterationIndex": 3, + "invocationCount": 2, + "value": 17, + "unit": "ns" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdEmpty.verified.json b/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdEmpty.verified.json new file mode 100644 index 00000000..22fdca1b --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdEmpty.verified.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdPerforator.verified.json b/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdPerforator.verified.json new file mode 100644 index 00000000..5489838e --- /dev/null +++ b/src/Perfolizer/Perfolizer.Tests/Phd/VerifiedFiles/Phd.PhdPerforator.verified.json @@ -0,0 +1,203 @@ +{ + "Attributes": { + "Info": { + "Title": "Perforator Measurements", + "RunId": "00000000-0000-0000-0000-000000000000", + "Timestamp": 0 + } + }, + "Children": [ + { + "Attributes": { + "Info": { + "RunId": "11214d6b-4e25-44a4-8032-d4290c9f5617", + "Timestamp": 1609459200000 + }, + "Source": { + "BuildId": 123456, + "ConfigurationId": "Configuration1", + "Branch": "main" + }, + "Host": { + "Os": "Linux-x64", + "ImageId": "Environment1" + }, + "Benchmark": { + "BenchmarkId": "benchmark1", + "BenchmarkVersion": 1 + } + }, + "Metrics": [ + { + "Id": "stage1", + "Value": 10.021, + "Unit": "ms" + }, + { + "Id": "stage2", + "Version": 2, + "Value": 9.103, + "Unit": "ms" + }, + { + "Id": "totalTime", + "Value": 9.891, + "Unit": "ms" + }, + { + "Id": "Footprint", + "Value": 20, + "Unit": "MB" + }, + { + "Id": "Gc.CollectCount", + "Value": 3 + } + ] + }, + { + "Attributes": { + "Info": { + "RunId": "11214d6b-4e25-44a4-8032-d4290c9f5617", + "Timestamp": 1609459260000 + }, + "Source": { + "BuildId": 123456, + "ConfigurationId": "Configuration1", + "Branch": "main" + }, + "Host": { + "Os": "Linux-x64", + "ImageId": "Environment1" + }, + "Benchmark": { + "BenchmarkId": "benchmark1", + "BenchmarkVersion": 1 + } + }, + "Metrics": [ + { + "Id": "stage1", + "Value": 9.194, + "Unit": "ms" + }, + { + "Id": "stage2", + "Version": 2, + "Value": 9.588, + "Unit": "ms" + }, + { + "Id": "totalTime", + "Value": 11.931, + "Unit": "ms" + }, + { + "Id": "Footprint", + "Value": 20, + "Unit": "MB" + }, + { + "Id": "Gc.CollectCount", + "Value": 3 + } + ] + }, + { + "Attributes": { + "Info": { + "RunId": "11214d6b-4e25-44a4-8032-d4290c9f5617", + "Timestamp": 1609459320000 + }, + "Source": { + "BuildId": 123456, + "ConfigurationId": "Configuration1", + "Branch": "main" + }, + "Host": { + "Os": "Linux-x64", + "ImageId": "Environment1" + }, + "Benchmark": { + "BenchmarkId": "benchmark2", + "BenchmarkVersion": 1 + } + }, + "Metrics": [ + { + "Id": "stage1", + "Value": 9.903, + "Unit": "ms" + }, + { + "Id": "stage2", + "Version": 2, + "Value": 8.49, + "Unit": "ms" + }, + { + "Id": "totalTime", + "Value": 9.854, + "Unit": "ms" + }, + { + "Id": "Footprint", + "Value": 20, + "Unit": "MB" + }, + { + "Id": "Gc.CollectCount", + "Value": 3 + } + ] + }, + { + "Attributes": { + "Info": { + "RunId": "11214d6b-4e25-44a4-8032-d4290c9f5617", + "Timestamp": 1609459380000 + }, + "Source": { + "BuildId": 123456, + "ConfigurationId": "Configuration1", + "Branch": "main" + }, + "Host": { + "Os": "Linux-x64", + "ImageId": "Environment1" + }, + "Benchmark": { + "BenchmarkId": "benchmark2", + "BenchmarkVersion": 1 + } + }, + "Metrics": [ + { + "Id": "stage1", + "Value": 8.833, + "Unit": "ms" + }, + { + "Id": "stage2", + "Version": 2, + "Value": 10.24, + "Unit": "ms" + }, + { + "Id": "totalTime", + "Value": 10.571, + "Unit": "ms" + }, + { + "Id": "Footprint", + "Value": 20, + "Unit": "MB" + }, + { + "Id": "Gc.CollectCount", + "Value": 3 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.Tests/SampleTests.cs b/src/Perfolizer/Perfolizer.Tests/SampleTests.cs index 5de53636..d3b6967d 100644 --- a/src/Perfolizer/Perfolizer.Tests/SampleTests.cs +++ b/src/Perfolizer/Perfolizer.Tests/SampleTests.cs @@ -11,4 +11,15 @@ public void SampleConcatTest(string a, string b, string c) string expected = Sample.Parse(c).ToString(); Assert.Equal(expected, actual); } + + [Fact] + public void SampleCtorTest() + { + string expected = "1;2;3"; + string Format(Sample s) => string.Join(";", s.Values); + + Assert.Equal(expected, Format(new Sample(new[] { 1.0, 2.0, 3.0 }))); + Assert.Equal(expected, Format(new Sample(new[] { 1, 2, 3 }))); + Assert.Equal(expected, Format(new Sample(new[] { 1L, 2L, 3L }))); + } } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer.sln.DotSettings b/src/Perfolizer/Perfolizer.sln.DotSettings index ee3f0fcd..882a2404 100644 --- a/src/Perfolizer/Perfolizer.sln.DotSettings +++ b/src/Perfolizer/Perfolizer.sln.DotSettings @@ -8,6 +8,7 @@ 0 4 True + True False 120 UseExplicitType diff --git a/src/Perfolizer/Perfolizer/Attributes/NullableAttributes.cs b/src/Perfolizer/Perfolizer/Attributes/NullableAttributes.cs index 9de89540..12db3118 100644 --- a/src/Perfolizer/Perfolizer/Attributes/NullableAttributes.cs +++ b/src/Perfolizer/Perfolizer/Attributes/NullableAttributes.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace System.Diagnostics.CodeAnalysis; +namespace Perfolizer.Attributes; #if !NETSTANDARD2_1 /// Specifies that null is allowed as an input even if the corresponding type disallows it. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] diff --git a/src/Perfolizer/Perfolizer/Collections/CollectionExtensions.cs b/src/Perfolizer/Perfolizer/Collections/CollectionExtensions.cs index aa682379..7640d065 100644 --- a/src/Perfolizer/Perfolizer/Collections/CollectionExtensions.cs +++ b/src/Perfolizer/Perfolizer/Collections/CollectionExtensions.cs @@ -1,5 +1,7 @@ +using System.Collections; using System.Diagnostics.CodeAnalysis; using Perfolizer.Common; +using Perfolizer.Metrology; namespace Perfolizer.Collections; @@ -89,16 +91,26 @@ internal static int WhichMax(this IReadOnlyList source, int start, int l /// internal static int WhichMax(this IReadOnlyList source) => WhichMax(source, 0, source.Count); - public static Sample ToSample(this IEnumerable values) + public static Sample ToSample(this IEnumerable values, MeasurementUnit? unit = null) { Assertion.NotNull(nameof(values), values); if (values is IReadOnlyList list) - return new Sample(list); - return new Sample(values.ToList()); + return new Sample(list, unit); + return new Sample(values.ToList(), unit); } + public static bool IsEmpty(this IReadOnlyCollection value) => value.Count == 0; + public static bool IsNotEmpty(this IReadOnlyCollection value) => value.Count > 0; public static bool IsEmpty(this IEnumerable values) => !values.Any(); + public static bool IsNotEmpty(this IEnumerable values) => values.Any(); public static IEnumerable WhereNotNull(this IEnumerable values) => values.Where(value => value != null).Cast(); + + public static IReadOnlyList ToReadOnlyList(this IEnumerable values) => values.ToList(); + +#if NETSTANDARD2_0 + public static TValue? GetValueOrDefault(this IDictionary dictionary, TKey key) + => dictionary.TryGetValue(key, out var value) ? value : default; +#endif } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Common/Assertion.cs b/src/Perfolizer/Perfolizer/Common/Assertion.cs index 7bc7cbbc..dd2378ff 100644 --- a/src/Perfolizer/Perfolizer/Common/Assertion.cs +++ b/src/Perfolizer/Perfolizer/Common/Assertion.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using Perfolizer.Exceptions; +using Perfolizer.Metrology; namespace Perfolizer.Common; @@ -166,6 +167,14 @@ public static void Equal(string name1, int value1, string name2, int value2) } } + [AssertionMethod] + public static void Equal(MeasurementUnit unit1, MeasurementUnit unit2) + { + if (unit1 != unit2) + throw new InvalidOperationException( + $"Invalid operations on incompatible units: {unit1.FullName} and {unit2.FullName}"); + } + [AssertionMethod] public static void Equal(string name, double value, double expectedValue, double eps = 1e-9) { diff --git a/src/Perfolizer/Perfolizer/Extensions/DoubleExtensions.cs b/src/Perfolizer/Perfolizer/Extensions/DoubleExtensions.cs index a966a0ac..e9ae507a 100644 --- a/src/Perfolizer/Perfolizer/Extensions/DoubleExtensions.cs +++ b/src/Perfolizer/Perfolizer/Extensions/DoubleExtensions.cs @@ -1,5 +1,6 @@ using Perfolizer.Common; using Perfolizer.Mathematics.Common; +using Perfolizer.Metrology; namespace Perfolizer.Extensions; @@ -10,4 +11,6 @@ internal static class DoubleExtensions public static string ToStringInvariant(this Probability p) => p.ToString(DefaultCultureInfo.Instance); public static string ToStringInvariant(this Probability p, string format) => p.ToString(format, DefaultCultureInfo.Instance); + + public static Measurement WithUnit(this double value, MeasurementUnit unit) => new (value, unit); } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Extensions/IntExtensions.cs b/src/Perfolizer/Perfolizer/Extensions/IntExtensions.cs new file mode 100644 index 00000000..514792b6 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Extensions/IntExtensions.cs @@ -0,0 +1,8 @@ +using Perfolizer.Metrology; + +namespace Perfolizer.Extensions; + +internal static class IntExtensions +{ + public static Measurement AsMeasurement(this int value) => new (value, NumberUnit.Instance); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Extensions/StringBuilderExtensions.cs b/src/Perfolizer/Perfolizer/Extensions/StringBuilderExtensions.cs index 7c2b8cc8..c6f860fe 100644 --- a/src/Perfolizer/Perfolizer/Extensions/StringBuilderExtensions.cs +++ b/src/Perfolizer/Perfolizer/Extensions/StringBuilderExtensions.cs @@ -5,7 +5,6 @@ namespace Perfolizer.Extensions; internal static class StringBuilderExtensions { - [return: NotNullIfNotNull("builder")] public static StringBuilder? TrimEnd(this StringBuilder? builder, params char[] trimChars) { if (builder == null) diff --git a/src/Perfolizer/Perfolizer/Extensions/StringExtensions.cs b/src/Perfolizer/Perfolizer/Extensions/StringExtensions.cs index 1fce28dc..b52047df 100644 --- a/src/Perfolizer/Perfolizer/Extensions/StringExtensions.cs +++ b/src/Perfolizer/Perfolizer/Extensions/StringExtensions.cs @@ -5,4 +5,6 @@ internal static class StringExtensions public static bool IsBlank(this string? value) => string.IsNullOrWhiteSpace(value); public static bool IsNotBlank(this string? value) => !value.IsBlank(); public static bool EquationsIgnoreCase(this string a, string b) => a.Equals(b, StringComparison.OrdinalIgnoreCase); + public static bool StartWithIgnoreCase(this string a, string b) => a.StartsWith(b, StringComparison.OrdinalIgnoreCase); + public static string JoinToString(this IEnumerable values, string separator) => string.Join(separator, values); } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Helpers/CpuBrandHelper.cs b/src/Perfolizer/Perfolizer/Helpers/CpuBrandHelper.cs new file mode 100644 index 00000000..ea6fed33 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Helpers/CpuBrandHelper.cs @@ -0,0 +1,188 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using Perfolizer.Horology; +using Perfolizer.Phd.Dto; + +namespace Perfolizer.Helpers +{ + public static class CpuBrandHelper + { + public static string ToFullBrandName(this PhdCpu? cpu) + { + if (cpu == null) + { + return "Unknown processor"; + } + + var parts = new List { cpu.ToShortBrandName(includeMaxFrequency: true) }; + + if (cpu.PhysicalProcessorCount > 0) + parts.Add($", {cpu.PhysicalProcessorCount} CPU"); + + if (cpu.LogicalCoreCount == 1) + parts.Add(", 1 logical core"); + + if (cpu.LogicalCoreCount > 1) + parts.Add($", {cpu.LogicalCoreCount} logical cores"); + + if (cpu.LogicalCoreCount > 0 && cpu.PhysicalCoreCount > 0) + parts.Add(" and "); + else if (cpu.PhysicalCoreCount > 0) + parts.Add(", "); + + if (cpu.PhysicalCoreCount == 1) + parts.Add("1 physical core"); + if (cpu.PhysicalCoreCount > 1) + parts.Add($"{cpu.PhysicalCoreCount} physical cores"); + + 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; + } + + + /// + /// 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 ToShortBrandName(this PhdCpu? cpu, bool includeMaxFrequency = false) + { + if (cpu == null || string.IsNullOrEmpty(cpu.ProcessorName)) + { + return "Unknown processor"; + } + + // Remove parts which don't provide any useful information for user + var processorName = cpu.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 (cpu.PhysicalCoreCount.HasValue && cpu.PhysicalCoreCount.Value > 0) + processorName = Regex.Replace(processorName, @"(\w+?-Core Processor)", "").Trim(); + + string? frequencyString = GetBrandStyledActualFrequency(cpu.GetNominalFrequency()); + 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? GetBrandStyledActualFrequency(Frequency? frequency) => frequency == null + ? null + : $"{frequency.Value.ToString(FrequencyUnit.GHz, "N2")}"; + + /// + /// 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 (() => + { + var data = ResourceHelper.LoadResource("Perfolizer.Resources.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/Perfolizer/Perfolizer/Helpers/OsBrandHelper.cs b/src/Perfolizer/Perfolizer/Helpers/OsBrandHelper.cs new file mode 100644 index 00000000..bbf4900d --- /dev/null +++ b/src/Perfolizer/Perfolizer/Helpers/OsBrandHelper.cs @@ -0,0 +1,298 @@ +using System.Diagnostics.CodeAnalysis; +using Perfolizer.Collections; +using Perfolizer.Extensions; +using Perfolizer.Phd.Dto; + +namespace Perfolizer.Helpers; + +[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] +public static class OsBrandHelper +{ + // 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 () + { + { "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(string? 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(string? 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, string? windowsUbr = null) + { + if (osName == "Windows") + return PrettifyWindows(osVersion, windowsUbr); + return $"{osName} {osVersion}"; + } + + private static string PrettifyWindows(string osVersion, string? 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 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") + ]; + + 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}]"; + } + + public static string ToBrandString(this PhdOs os) + { + string? display = os.Display; + if (display != null) return display; + + string? name = os.Name; + if (name == null) return ""; + + if (name.EquationsIgnoreCase("macos") && os is { Version: not null, KernelVersion: not null }) + return PrettifyMacOSX(os.Version, os.KernelVersion); + if (name.EquationsIgnoreCase("windows")) + { + string? fullVersion = os.Version; + if (fullVersion != null) + { + string baseVersion = fullVersion.Split('.').Take(3).JoinToString("."); + string? ubr = fullVersion.Split('.').Skip(3).FirstOrDefault(); + return PrettifyWindows(baseVersion, ubr); + } + } + return name + " " + os.Version; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Helpers/ResourceHelper.cs b/src/Perfolizer/Perfolizer/Helpers/ResourceHelper.cs new file mode 100644 index 00000000..bc0ff6a6 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Helpers/ResourceHelper.cs @@ -0,0 +1,18 @@ +using System.Reflection; + +namespace Perfolizer.Helpers; + +internal static class ResourceHelper +{ + internal static string LoadResource(string resourceName) + { + using var stream = GetResourceStream(resourceName); + if (stream == null) throw new Exception($"Resource {resourceName} not found"); + + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + private static Stream? GetResourceStream(string resourceName) => + typeof(ResourceHelper).GetTypeInfo().Assembly.GetManifestResourceStream(resourceName); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Helpers/StringHelper.cs b/src/Perfolizer/Perfolizer/Helpers/StringHelper.cs new file mode 100644 index 00000000..7650126a --- /dev/null +++ b/src/Perfolizer/Perfolizer/Helpers/StringHelper.cs @@ -0,0 +1,8 @@ +using Perfolizer.Extensions; + +namespace Perfolizer.Helpers; + +public static class StringHelper +{ + public static string FirstUpper(string s) => s.IsBlank() ? s : char.ToUpper(s[0]) + s.Substring(1); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Horology/Frequency.cs b/src/Perfolizer/Perfolizer/Horology/Frequency.cs index 479b32fb..f120ee48 100644 --- a/src/Perfolizer/Perfolizer/Horology/Frequency.cs +++ b/src/Perfolizer/Perfolizer/Horology/Frequency.cs @@ -53,13 +53,13 @@ [PublicAPI] public Frequency(double value, FrequencyUnit unit) : this(value * un [PublicAPI] public static bool operator !=(Frequency a, Frequency b) => !a.Hertz.Equals(b.Hertz); [PublicAPI] - public static bool TryParse([NotNullWhen(true)] string? s, FrequencyUnit unit, out Frequency freq) + public static bool TryParse(string? s, FrequencyUnit unit, out Frequency freq) { return TryParse(s, unit, NumberStyles.Any, DefaultCultureInfo.Instance, out freq); } [PublicAPI] - public static bool TryParse([NotNullWhen(true)] string? s, FrequencyUnit unit, NumberStyles numberStyle, + public static bool TryParse(string? s, FrequencyUnit unit, NumberStyles numberStyle, IFormatProvider formatProvider, out Frequency freq) { bool success = double.TryParse(s, numberStyle, formatProvider, out double result); @@ -67,35 +67,35 @@ public static bool TryParse([NotNullWhen(true)] string? s, FrequencyUnit unit, N return success; } - [PublicAPI] public static bool TryParseHz([NotNullWhen(true)] string? s, out Frequency freq) => + [PublicAPI] public static bool TryParseHz(string? s, out Frequency freq) => TryParse(s, FrequencyUnit.Hz, out freq); [PublicAPI] - public static bool TryParseHz([NotNullWhen(true)] string? s, NumberStyles numberStyle, + public static bool TryParseHz(string? s, NumberStyles numberStyle, IFormatProvider formatProvider, out Frequency freq) => TryParse(s, FrequencyUnit.Hz, numberStyle, formatProvider, out freq); - [PublicAPI] public static bool TryParseKHz([NotNullWhen(true)] string? s, out Frequency freq) => + [PublicAPI] public static bool TryParseKHz(string? s, out Frequency freq) => TryParse(s, FrequencyUnit.KHz, out freq); [PublicAPI] - public static bool TryParseKHz([NotNullWhen(true)] string? s, NumberStyles numberStyle, + public static bool TryParseKHz(string? s, NumberStyles numberStyle, IFormatProvider formatProvider, out Frequency freq) => TryParse(s, FrequencyUnit.KHz, numberStyle, formatProvider, out freq); - [PublicAPI] public static bool TryParseMHz([NotNullWhen(true)] string? s, out Frequency freq) => + [PublicAPI] public static bool TryParseMHz(string? s, out Frequency freq) => TryParse(s, FrequencyUnit.MHz, out freq); [PublicAPI] - public static bool TryParseMHz([NotNullWhen(true)] string? s, NumberStyles numberStyle, + public static bool TryParseMHz(string? s, NumberStyles numberStyle, IFormatProvider formatProvider, out Frequency freq) => TryParse(s, FrequencyUnit.MHz, numberStyle, formatProvider, out freq); - [PublicAPI] public static bool TryParseGHz([NotNullWhen(true)] string? s, out Frequency freq) => + [PublicAPI] public static bool TryParseGHz(string? s, out Frequency freq) => TryParse(s, FrequencyUnit.GHz, out freq); [PublicAPI] - public static bool TryParseGHz([NotNullWhen(true)] string? s, NumberStyles numberStyle, + public static bool TryParseGHz(string? s, NumberStyles numberStyle, IFormatProvider formatProvider, out Frequency freq) => TryParse(s, FrequencyUnit.GHz, numberStyle, formatProvider, out freq); @@ -129,13 +129,13 @@ public string ToString( frequencyUnit ??= FrequencyUnit.GetBestFrequencyUnit(Hertz); format ??= DefaultFormat; double nominalValue = FrequencyUnit.Convert(Hertz, FrequencyUnit.Hz, frequencyUnit); - var measurementValue = new MeasurementValue(nominalValue, frequencyUnit); + var measurementValue = new Measurement(nominalValue, frequencyUnit); return measurementValue.ToString(format, formatProvider, unitPresentation); } public bool Equals(Frequency other) => Hertz.Equals(other.Hertz); public bool Equals(Frequency other, double hertzEpsilon) => Abs(Hertz - other.Hertz) < hertzEpsilon; - public override bool Equals([NotNullWhen(true)] object? obj) => obj is Frequency other && Equals(other); + public override bool Equals(object? obj) => obj is Frequency other && Equals(other); public override int GetHashCode() => Hertz.GetHashCode(); public int CompareTo(Frequency other) => Hertz.CompareTo(other.Hertz); } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Horology/TimeInterval.cs b/src/Perfolizer/Perfolizer/Horology/TimeInterval.cs index d797aaa2..4e1e74c0 100644 --- a/src/Perfolizer/Perfolizer/Horology/TimeInterval.cs +++ b/src/Perfolizer/Perfolizer/Horology/TimeInterval.cs @@ -77,7 +77,7 @@ public string ToString( timeUnit ??= TimeUnit.GetBestTimeUnit(Nanoseconds); format ??= DefaultFormat; double nominalValue = TimeUnit.Convert(Nanoseconds, TimeUnit.Nanosecond, timeUnit); - var measurementValue = new MeasurementValue(nominalValue, timeUnit); + var measurementValue = new Measurement(nominalValue, timeUnit); return measurementValue.ToString(format, formatProvider, unitPresentation); } @@ -87,7 +87,7 @@ public string ToString( public bool Equals(TimeInterval other, double nanosecondEpsilon) => Math.Abs(other.Nanoseconds - Nanoseconds) < nanosecondEpsilon; - public override bool Equals([NotNullWhen(true)] object? obj) => obj is TimeInterval other && Equals(other); + public override bool Equals(object? obj) => obj is TimeInterval other && Equals(other); public override int GetHashCode() => Nanoseconds.GetHashCode(); public MeasurementUnit Unit => TimeUnit.Nanosecond; diff --git a/src/Perfolizer/Perfolizer/Horology/TimeUnit.cs b/src/Perfolizer/Perfolizer/Horology/TimeUnit.cs index e775c116..49262ebd 100644 --- a/src/Perfolizer/Perfolizer/Horology/TimeUnit.cs +++ b/src/Perfolizer/Perfolizer/Horology/TimeUnit.cs @@ -32,7 +32,7 @@ public static TimeUnit GetBestTimeUnit(params double[] values) return Nanosecond; // Use the largest unit to display the smallest recorded measurement without loss of precision. double minValue = values.Min(); - return All.LastOrDefault(unit => minValue >= unit.BaseUnits) ?? All.Last(); + return All.LastOrDefault(unit => minValue >= unit.BaseUnits) ?? All.First(); } public static double Convert(double value, TimeUnit from, TimeUnit? to) => diff --git a/src/Perfolizer/Perfolizer/Json/LightJsonSerializer.cs b/src/Perfolizer/Perfolizer/Json/LightJsonSerializer.cs new file mode 100644 index 00000000..e8a4ad7c --- /dev/null +++ b/src/Perfolizer/Perfolizer/Json/LightJsonSerializer.cs @@ -0,0 +1,133 @@ +using System.Collections; +using System.Text; +using Perfolizer.Collections; +using Perfolizer.Common; +using Perfolizer.Extensions; +using Perfolizer.Phd; +using Perfolizer.Phd.Base; + +namespace Perfolizer.Json; + +public class LightJsonSerializer +{ + public static string Serialize(object obj, LightJsonSettings? settings = null) => + new LightJsonSerializer(settings).Append(obj).ToString(); + + private readonly LightJsonSettings settings; + private readonly StringBuilder builder = new (); + + private LightJsonSerializer(LightJsonSettings? settings = null) + { + this.settings = settings ?? new LightJsonSettings(); + } + + public override string ToString() => builder.ToString(); + + private LightJsonSerializer Append(object obj) + { + switch (obj) + { + case PhdEntry phdEntry: + Append(phdEntry.ToRaw()); + break; + case string stringValue: + AppendString(stringValue); + break; + case int intValue: + AppendInt(intValue); + break; + case long longValue: + AppendLong(longValue); + break; + case double doubleValue: + AppendDouble(doubleValue); + break; + case bool boolValue: + AppendBool(boolValue); + break; + case Guid guidValue: + AppendGuidValue(guidValue); + break; + case DateTimeOffset dateTimeOffsetValue: + AppendDateTimeOffset(dateTimeOffsetValue); + break; + case ICollection collection: + AppendCollection(collection); + break; + case Enum enumValue: + AppendEnum(enumValue); + break; + default: + AppendObject(obj); + break; + } + + return this; + } + + private void AppendObject(object obj) + { + builder.Append('{'); + bool isFirst = true; + foreach (var property in obj.GetType().GetProperties()) + { + object? value; + try + { + value = property.GetValue(obj); + } + catch (Exception) + { + continue; + } + if (value == null) + continue; + if (value is string stringValue && stringValue.IsBlank()) + continue; + if (value is ICollection { Count: 0 }) + continue; + + if (!isFirst) + builder.Append(','); + builder.Append('\"'); + builder.Append(property.Name); + builder.Append('\"'); + builder.Append(':'); + Append(value); + isFirst = false; + } + builder.Append("}"); + } + + private void AppendCollection(ICollection collection) + { + builder.Append('['); + bool isFirst = true; + foreach (object value in collection) + { + if (value == null) + continue; + if (!isFirst) + builder.Append(','); + Append(value); + isFirst = false; + } + builder.Append(']'); + } + + private void AppendString(string value) + { + builder.Append('\"'); + builder.Append(value); + builder.Append('\"'); + } + + private void AppendBool(bool value) => builder.Append(value ? "true" : "false"); + private void AppendEnum(Enum enumValue) => AppendString(enumValue.ToString()); + private void AppendGuidValue(Guid value) => AppendString(value.ToString()); + private void AppendDateTimeOffset(DateTimeOffset value) => AppendLong(value.ToUnixTimeMilliseconds()); + + private void AppendInt(int value) => builder.Append(value.ToString(DefaultCultureInfo.Instance)); + private void AppendLong(long value) => builder.Append(value.ToString(DefaultCultureInfo.Instance)); + private void AppendDouble(double value) => builder.Append(value.ToString(DefaultCultureInfo.Instance)); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Json/LightJsonSettings.cs b/src/Perfolizer/Perfolizer/Json/LightJsonSettings.cs new file mode 100644 index 00000000..69d9ef65 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Json/LightJsonSettings.cs @@ -0,0 +1,3 @@ +namespace Perfolizer.Json; + +public class LightJsonSettings { } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/Common/ConfidenceInterval.cs b/src/Perfolizer/Perfolizer/Mathematics/Common/ConfidenceInterval.cs index 25fb74b3..716d95c1 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/Common/ConfidenceInterval.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/Common/ConfidenceInterval.cs @@ -72,7 +72,7 @@ public bool Equals(ConfidenceInterval other, IEqualityComparer comparer) comparer.Equals(ConfidenceLevel, other.ConfidenceLevel); } - public override bool Equals([NotNullWhen(true)] object? obj) + public override bool Equals(object? obj) { return obj is ConfidenceInterval other && Equals(other); } diff --git a/src/Perfolizer/Perfolizer/Mathematics/Common/PrecisionHelper.cs b/src/Perfolizer/Perfolizer/Mathematics/Common/PrecisionHelper.cs new file mode 100644 index 00000000..7c3e2fa5 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Mathematics/Common/PrecisionHelper.cs @@ -0,0 +1,14 @@ +namespace Perfolizer.Mathematics.Common; + +public static class PrecisionHelper +{ + public static int GetOptimalPrecision(IReadOnlyList values) + { + double minLog = values + .Where(value => value > 0) + .Select(Log10) + .DefaultIfEmpty(0) + .Min(); + return Max(1, -(minLog - 2).RoundToInt()); + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Mathematics/Common/Probability.cs b/src/Perfolizer/Perfolizer/Mathematics/Common/Probability.cs index 1809aaaa..aaf43143 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/Common/Probability.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/Common/Probability.cs @@ -49,7 +49,7 @@ public bool Equals(Probability other) return Value.Equals(other.Value); } - public override bool Equals([NotNullWhen(true)] object? obj) + public override bool Equals(object? obj) { return obj is Probability other && Equals(other); } @@ -64,7 +64,6 @@ public int CompareTo(Probability other) return Value.CompareTo(other.Value); } - [return: NotNullIfNotNull("values")] public static Probability[]? ToProbabilities(double[]? values) { if (values == null) diff --git a/src/Perfolizer/Perfolizer/Mathematics/Multimodality/LowlandModalityDetector.cs b/src/Perfolizer/Perfolizer/Mathematics/Multimodality/LowlandModalityDetector.cs index f86c411e..896b15e2 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/Multimodality/LowlandModalityDetector.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/Multimodality/LowlandModalityDetector.cs @@ -65,7 +65,7 @@ RangedMode LocalMode(double location, double left, double right) modeWeights?.Add(sample.SortedWeights[i]); } } - if (modeValues.IsEmpty()) + if (modeValues.Count == 0) throw new InvalidOperationException( $"Can't find any values in [{left.ToStringInvariant()}, {right.ToStringInvariant()}]"); diff --git a/src/Perfolizer/Perfolizer/Mathematics/QuantileEstimators/TrimmedHarrellDavisQuantileEstimator.cs b/src/Perfolizer/Perfolizer/Mathematics/QuantileEstimators/TrimmedHarrellDavisQuantileEstimator.cs index f3611137..f5f6e6e6 100644 --- a/src/Perfolizer/Perfolizer/Mathematics/QuantileEstimators/TrimmedHarrellDavisQuantileEstimator.cs +++ b/src/Perfolizer/Perfolizer/Mathematics/QuantileEstimators/TrimmedHarrellDavisQuantileEstimator.cs @@ -12,7 +12,7 @@ public class TrimmedHarrellDavisQuantileEstimator : IQuantileEstimator { private readonly Func getIntervalWidth; - public static readonly IQuantileEstimator SqrtInstance = new TrimmedHarrellDavisQuantileEstimator(n => 1.0 / Sqrt(n), "SQRT"); + public static readonly IQuantileEstimator Sqrt = new TrimmedHarrellDavisQuantileEstimator(n => 1.0 / Sqrt(n), "SQRT"); public TrimmedHarrellDavisQuantileEstimator(Func getIntervalWidth, string? alias = null) { diff --git a/src/Perfolizer/Perfolizer/Metrology/EffectSizeValue.cs b/src/Perfolizer/Perfolizer/Metrology/EffectSizeValue.cs index 7fe35c93..2caed0d8 100644 --- a/src/Perfolizer/Perfolizer/Metrology/EffectSizeValue.cs +++ b/src/Perfolizer/Perfolizer/Metrology/EffectSizeValue.cs @@ -16,7 +16,7 @@ public string ToString( UnitPresentation? unitPresentation = null) { format ??= DefaultFormat; - var measurementValue = new MeasurementValue(Value, EffectSizeUnit.Instance); + var measurementValue = new Measurement(Value, EffectSizeUnit.Instance); return measurementValue.ToString(format, formatProvider, unitPresentation); } diff --git a/src/Perfolizer/Perfolizer/Metrology/MeasurementValue.cs b/src/Perfolizer/Perfolizer/Metrology/Measurement.cs similarity index 81% rename from src/Perfolizer/Perfolizer/Metrology/MeasurementValue.cs rename to src/Perfolizer/Perfolizer/Metrology/Measurement.cs index 05469e53..eb1abd6d 100644 --- a/src/Perfolizer/Perfolizer/Metrology/MeasurementValue.cs +++ b/src/Perfolizer/Perfolizer/Metrology/Measurement.cs @@ -7,9 +7,9 @@ namespace Perfolizer.Metrology; -public class MeasurementValue(double nominalValue, MeasurementUnit unit) : IWithUnits +public class Measurement(double nominalValue, MeasurementUnit unit) : IWithUnits { - public static readonly MeasurementValue Zero = new(0, NumberUnit.Instance); + public static readonly Measurement Zero = new (0, NumberUnit.Instance); protected virtual string DefaultFormat => "G"; @@ -66,7 +66,7 @@ public string ToString( return $"{nominalValue}{Unit.ToString(unitPresentation)}"; } - public static bool TryParse(string s, out MeasurementValue value) + public static bool TryParse(string s, out Measurement value) { if (s.IsNotBlank()) { @@ -77,17 +77,17 @@ public static bool TryParse(string s, out MeasurementValue value) } } - value = new MeasurementValue(0, NumberUnit.Instance); + value = new Measurement(0, NumberUnit.Instance); return false; } - public static bool TryParse(string s, MeasurementUnit unit, out MeasurementValue value) + public static bool TryParse(string s, MeasurementUnit unit, out Measurement value) { if (TryParseBySuffix(unit.Abbreviation, out double nominalValue) || TryParseBySuffix(unit.AbbreviationAscii, out nominalValue) || TryParseBySuffix(unit.FullName, out nominalValue)) { - value = new MeasurementValue(nominalValue, unit); + value = new Measurement(nominalValue, unit); return true; } @@ -107,4 +107,16 @@ bool TryParseBySuffix(string suffix, out double value) return false; } } + + public static Measurement operator +(Measurement a, Measurement b) + { + Assertion.Equal(a.Unit, b.Unit); + return new Measurement(a.NominalValue + b.NominalValue, a.Unit); + } + + public static Measurement operator -(Measurement a, Measurement b) + { + Assertion.Equal(a.Unit, b.Unit); + return new Measurement(a.NominalValue - b.NominalValue, a.Unit); + } } \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Metrology/MeasurementUnit.cs b/src/Perfolizer/Perfolizer/Metrology/MeasurementUnit.cs index 3c84ea2d..cc8e9f58 100644 --- a/src/Perfolizer/Perfolizer/Metrology/MeasurementUnit.cs +++ b/src/Perfolizer/Perfolizer/Metrology/MeasurementUnit.cs @@ -44,6 +44,10 @@ public string ToString(UnitPresentation? unitPresentation) public static bool TryParse(string? s, out MeasurementUnit unit) { + unit = NumberUnit.Instance; + if (s is { Length: 0 }) + return true; + if (s != null && s.IsNotBlank()) foreach (var measurementUnit in GetAll()) { @@ -55,7 +59,6 @@ public static bool TryParse(string? s, out MeasurementUnit unit) return true; } } - unit = NumberUnit.Instance; return false; } diff --git a/src/Perfolizer/Perfolizer/Metrology/NumberValue.cs b/src/Perfolizer/Perfolizer/Metrology/NumberValue.cs index 841690cf..6a7481ca 100644 --- a/src/Perfolizer/Perfolizer/Metrology/NumberValue.cs +++ b/src/Perfolizer/Perfolizer/Metrology/NumberValue.cs @@ -17,7 +17,7 @@ public string ToString( UnitPresentation? unitPresentation = null) { format ??= DefaultFormat; - var measurementValue = new MeasurementValue(Value, NumberUnit.Instance); + var measurementValue = new Measurement(Value, NumberUnit.Instance); return measurementValue.ToString(format, formatProvider, unitPresentation); } diff --git a/src/Perfolizer/Perfolizer/Metrology/PercentValue.cs b/src/Perfolizer/Perfolizer/Metrology/PercentValue.cs index 50aff342..6d2583d2 100644 --- a/src/Perfolizer/Perfolizer/Metrology/PercentValue.cs +++ b/src/Perfolizer/Perfolizer/Metrology/PercentValue.cs @@ -17,7 +17,7 @@ public string ToString( UnitPresentation? unitPresentation = null) { format ??= DefaultFormat; - var measurementValue = new MeasurementValue(Percentage, PercentUnit.Instance); + var measurementValue = new Measurement(Percentage, PercentUnit.Instance); return measurementValue.ToString(format, formatProvider, unitPresentation); } diff --git a/src/Perfolizer/Perfolizer/Metrology/RatioValue.cs b/src/Perfolizer/Perfolizer/Metrology/RatioValue.cs index c13c1deb..14bfab4b 100644 --- a/src/Perfolizer/Perfolizer/Metrology/RatioValue.cs +++ b/src/Perfolizer/Perfolizer/Metrology/RatioValue.cs @@ -15,7 +15,7 @@ public string ToString( UnitPresentation? unitPresentation = null) { format ??= DefaultFormat; - var measurementValue = new MeasurementValue(Value, RatioUnit.Instance); + var measurementValue = new Measurement(Value, RatioUnit.Instance); return measurementValue.ToString(format, formatProvider, unitPresentation); } diff --git a/src/Perfolizer/Perfolizer/Metrology/SizeValue.cs b/src/Perfolizer/Perfolizer/Metrology/SizeValue.cs index f82b48d2..b93f311e 100644 --- a/src/Perfolizer/Perfolizer/Metrology/SizeValue.cs +++ b/src/Perfolizer/Perfolizer/Metrology/SizeValue.cs @@ -52,7 +52,7 @@ public string ToString( sizeUnit ??= SizeUnit.GetBestSizeUnit(Bytes); format ??= DefaultFormat; double nominalValue = SizeUnit.Convert(Bytes, SizeUnit.B, sizeUnit); - var measurementValue = new MeasurementValue(nominalValue, sizeUnit); + var measurementValue = new Measurement(nominalValue, sizeUnit); return measurementValue.ToString(format, formatProvider, unitPresentation); } diff --git a/src/Perfolizer/Perfolizer/Metrology/Threshold.cs b/src/Perfolizer/Perfolizer/Metrology/Threshold.cs index 7941c046..0e457e5e 100644 --- a/src/Perfolizer/Perfolizer/Metrology/Threshold.cs +++ b/src/Perfolizer/Perfolizer/Metrology/Threshold.cs @@ -29,7 +29,7 @@ public Sample ApplyMax(Sample sample) values[i] = double.MinValue; foreach (var appliedSample in appliedSamples) values[i] = Max(values[i], appliedSample.Values[i]); - if (appliedSamples.IsEmpty()) + if (appliedSamples.Length == 0) values[i] = sample.Values[i]; } return sample.IsWeighted @@ -83,7 +83,7 @@ public static bool TryParse(string s, out Threshold threshold) var thresholdValues = new ISpecificMeasurementValue[parts.Length]; for (int i = 0; i < parts.Length; i++) { - if (!MeasurementValue.TryParse(parts[i], out var measurementValue)) + if (!Measurement.TryParse(parts[i], out var measurementValue)) { threshold = Zero; return false; diff --git a/src/Perfolizer/Perfolizer/Perfolizer.csproj b/src/Perfolizer/Perfolizer/Perfolizer.csproj index 9189df74..9e088b38 100644 --- a/src/Perfolizer/Perfolizer/Perfolizer.csproj +++ b/src/Perfolizer/Perfolizer/Perfolizer.csproj @@ -1,15 +1,17 @@ - - netstandard2.0;netstandard2.1 - Performance analysis toolkit - - - - - - + + netstandard2.0;net6.0 + Performance analysis toolkit + + + + + + + + diff --git a/src/Perfolizer/Perfolizer/Phd/Base/IPhdDisplay.cs b/src/Perfolizer/Perfolizer/Phd/Base/IPhdDisplay.cs new file mode 100644 index 00000000..45de03ab --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/IPhdDisplay.cs @@ -0,0 +1,6 @@ +namespace Perfolizer.Phd.Base; + +public interface IPhdDisplay +{ + string Display { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdEntry.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdEntry.cs new file mode 100644 index 00000000..73263c41 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdEntry.cs @@ -0,0 +1,73 @@ +using Perfolizer.Phd.Dto; + +namespace Perfolizer.Phd.Base; + +public class PhdEntry(PhdAttributes attributes) +{ + public PhdEntry? Parent { get; private set; } + + public PhdAttributes Attributes { get; } = attributes; + + private readonly List metrics = []; + public IReadOnlyList Metrics => metrics; + + private readonly List children = []; + public IReadOnlyList Children => children; + + // Metrics + public bool Remove(PhdMetric metric) => metrics.Remove(metric); + + public PhdEntry Add(PhdMetric metric) + { + metrics.Add(metric); + return this; + } + + // Tree + public PhdEntry Add(PhdEntry entry) + { + children.Add(entry); + entry.Parent = this; + return this; + } + + public bool Remove(PhdEntry entry) + { + if (entry.Parent == this && children.Remove(entry)) + { + entry.Parent = null; + return true; + } + return false; + } + + public IEnumerable Traverse() + { + yield return this; + foreach (var entry in children.SelectMany(entry => entry.Traverse())) + yield return entry; + } + + // Serialization + + public PhdRawEntry ToRaw() + { + var root = new PhdRawEntry { Attributes = Attributes }; + foreach (var metric in metrics) + root.Metrics.Add(metric.Serialize()); + foreach (var child in children) + root.Children.Add(child.ToRaw()); + return root; + } + + public static T Deserialize(PhdRawEntry phdRawEntry, PhdEntry? parent = null) where T : PhdEntry + { + var perfEntry = (T)Activator.CreateInstance(typeof(T), phdRawEntry.Attributes); + perfEntry.Parent = parent; + foreach (var metric in phdRawEntry.Metrics) + perfEntry.metrics.Add(PhdMetric.Deserialize(metric)); + foreach (var child in phdRawEntry.Children) + perfEntry.children.Add(Deserialize(child, perfEntry)); + return perfEntry; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdHelper.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdHelper.cs new file mode 100644 index 00000000..8bc8c1c8 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdHelper.cs @@ -0,0 +1,11 @@ +namespace Perfolizer.Phd.Base; + +internal static class PhdHelper +{ + public static bool IsPhdPrimitive(this Type type) => + type.IsPrimitive || + type.IsEnum || + type == typeof(Guid) || + type == typeof(DateTimeOffset) || + type == typeof(string); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdIndex.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdIndex.cs new file mode 100644 index 00000000..f3cebb88 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdIndex.cs @@ -0,0 +1,91 @@ +using System.Text; +using Perfolizer.Extensions; + +namespace Perfolizer.Phd.Base; + +public class PhdIndex +{ + private readonly PhdEntry rootEntry; + private readonly Dictionary entries = new (); + public IReadOnlyList Keys { get; } + public IReadOnlyCollection Entries => entries.Keys; + public PhdIndexedEntry this[PhdEntry entry] => entries[entry]; + + public PhdIndex(PhdEntry rootEntry) + { + this.rootEntry = rootEntry; + IndexEntry(PhdKey.Empty, rootEntry, null); + Keys = entries.Values + .SelectMany(indexedEntry => indexedEntry + .AllProperties + .Select(property => property.Key) + .Concat([indexedEntry.Key])) + .Distinct() + .Where(key => key.Path.IsNotBlank()) + .ToList(); + } + + private void IndexEntry(PhdKey key, PhdEntry entry, PhdEntry? parent) + { + IndexSelf(key, entry, parent); + foreach (var child in entry.Children) + IndexEntry(key, child, entry); + } + + private void IndexSelf(PhdKey key, PhdEntry entry, PhdEntry? parent) + { + var indexedEntry = entries[entry] = new PhdIndexedEntry(key, entry, new Dictionary()); + + if (parent != null) + { + var indexedParent = entries[parent]; + foreach (var property in indexedParent.AllProperties) + indexedEntry[property.Key] = property; + } + + IndexAttributes(key, entry, entry.Attributes); + } + + private void IndexAttributes(PhdKey key, PhdEntry entry, object obj) + { + var indexedEntry = entries[entry]; + foreach (var property in obj.GetType().GetProperties()) + { + object? value; + try + { + value = property.GetValue(obj); + } + catch (Exception) + { + continue; + } + if (value == null || value.ToString().IsBlank()) + continue; + var propertyKey = key.Append(property.Name, value.GetType()); + indexedEntry[propertyKey] = new PhdProperty(propertyKey, value); + if (!value.GetType().IsPrimitive && value is not string) + IndexAttributes(propertyKey, entry, value); + } + } + + public string Dump() + { + var builder = new StringBuilder(); + builder.AppendLine("[Keys]"); + foreach (var key in Keys.OrderBy(key => key.Path, StringComparer.OrdinalIgnoreCase)) + builder.AppendLine($" {key.Path}"); + builder.AppendLine(); + + int index = 0; + foreach (var entry in rootEntry.Traverse()) + { + builder.AppendLine($"[Entry{index++}]"); + var indexedEntry = entries[entry]; + foreach (var property in indexedEntry.AllProperties.OrderBy(property => property.Key.Path)) + builder.AppendLine($" {property.Key.Path} = {property.Value}"); + } + + return builder.ToString().Trim(); + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdIndexedEntry.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdIndexedEntry.cs new file mode 100644 index 00000000..aa557461 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdIndexedEntry.cs @@ -0,0 +1,23 @@ +namespace Perfolizer.Phd.Base; + +public class PhdIndexedEntry(PhdKey key, PhdEntry entry, Dictionary properties) +{ + public PhdKey Key { get; } = key; + public PhdEntry Entry { get; } = entry; + public IReadOnlyCollection AllProperties => properties.Values; + + private PhdProperty? this[string key] + { + get => properties.TryGetValue(key, out var value) ? value : null; + set + { + if (value != null) properties[key] = value; + } + } + + public PhdProperty? this[PhdKey key] + { + get => this[key.Path]; + set => this[key.Path] = value; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdKey.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdKey.cs new file mode 100644 index 00000000..c93f0b21 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdKey.cs @@ -0,0 +1,46 @@ +using Perfolizer.Extensions; + +namespace Perfolizer.Phd.Base; + +public class PhdKey(string path, Type type) : IEquatable +{ + public static readonly PhdKey Empty = new ("", typeof(object)); + + public string Path { get; } = path; + public Type Type { get; } = type; + + public string Name { get; } = path.Split('.').DefaultIfEmpty("").Last(); + public override string ToString() => Path; + + public bool IsComposite() => !Type.IsPhdPrimitive(); + + public bool IsMatched(string selector) => + Path.EquationsIgnoreCase(selector) || + Path.StartWithIgnoreCase(selector) || + Name.EquationsIgnoreCase(selector); + + public bool Equals(PhdKey? other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + return Path == other.Path; + } + + 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((PhdKey)obj); + } + + public override int GetHashCode() => Path.GetHashCode(); + public static bool operator ==(PhdKey? left, PhdKey? right) => Equals(left, right); + public static bool operator !=(PhdKey? left, PhdKey? right) => !Equals(left, right); + public PhdKey Append(string subName, Type subType) => new ($"{Path}.{subName}", subType); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdMeta.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdMeta.cs new file mode 100644 index 00000000..05db44be --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdMeta.cs @@ -0,0 +1,10 @@ +using JetBrains.Annotations; +using Perfolizer.Phd.Reports; + +namespace Perfolizer.Phd.Base; + +[PublicAPI] +public class PhdMeta +{ + public PhdSummary? Summary { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdMetric.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdMetric.cs new file mode 100644 index 00000000..e9d03005 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdMetric.cs @@ -0,0 +1,58 @@ +using Perfolizer.Extensions; +using Perfolizer.Metrology; + +namespace Perfolizer.Phd.Base; + +public class PhdMetric +{ + public string? Id { get; set; } + public int? Version { get; set; } + public int? IterationIndex { get; set; } + public long? InvocationCount { get; set; } + public DateTimeOffset? StartTimestamp { get; set; } + public DateTimeOffset? EndTimestamp { get; set; } + public string? Marker { get; set; } + + /// + /// Examples: mean, median, min, max, p0, p25, p75, p100 + /// + public string? Aggregator { get; set; } + + public double Value { get; set; } + public MeasurementUnit Unit { get; set; } = NumberUnit.Instance; + + public Measurement GetMeasurementValue() => new(Value, Unit); + public string GetIdentity() => $"{Id}§{Version}§{Aggregator}§{Marker}§{Unit}"; + + public PhdRawMetric Serialize() => new() + { + Id = Id, + Version = Version, + IterationIndex = IterationIndex, + InvocationCount = InvocationCount, + StartTimestamp = StartTimestamp?.ToUnixTimeMilliseconds(), + EndTimestamp = EndTimestamp?.ToUnixTimeMilliseconds(), + Marker = Marker, + Aggregator = Aggregator, + Value = Value, + Unit = Unit.AbbreviationAscii.IsBlank() ? null : Unit.AbbreviationAscii + }; + + public static PhdMetric Deserialize(PhdRawMetric phdRawMetric) => new() + { + Id = phdRawMetric.Id, + Version = phdRawMetric.Version, + IterationIndex = phdRawMetric.IterationIndex, + InvocationCount = phdRawMetric.InvocationCount, + StartTimestamp = phdRawMetric.StartTimestamp.HasValue + ? DateTimeOffset.FromUnixTimeMilliseconds(phdRawMetric.StartTimestamp.Value) + : null, + EndTimestamp = phdRawMetric.EndTimestamp.HasValue + ? DateTimeOffset.FromUnixTimeMilliseconds(phdRawMetric.EndTimestamp.Value) + : null, + Marker = phdRawMetric.Marker, + Aggregator = phdRawMetric.Aggregator, + Value = phdRawMetric.Value, + Unit = MeasurementUnit.TryParse(phdRawMetric.Unit, out var unit) ? unit : NumberUnit.Instance + }; +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdObject.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdObject.cs new file mode 100644 index 00000000..3dd1fd5d --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdObject.cs @@ -0,0 +1,8 @@ +using System.Collections; + +namespace Perfolizer.Phd.Base; + +public class PhdObject +{ + public string? Display { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdProperty.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdProperty.cs new file mode 100644 index 00000000..b1b430cb --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdProperty.cs @@ -0,0 +1,10 @@ +using System.Diagnostics; + +namespace Perfolizer.Phd.Base; + +[DebuggerDisplay("{Key}={Value}")] +public class PhdProperty(PhdKey key, object value) +{ + public PhdKey Key { get; } = key; + public object Value { get; } = value; +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdRawEntry.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdRawEntry.cs new file mode 100644 index 00000000..7c58e95c --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdRawEntry.cs @@ -0,0 +1,10 @@ +using Perfolizer.Phd.Dto; + +namespace Perfolizer.Phd.Base; + +public class PhdRawEntry +{ + public PhdAttributes Attributes { get; set; } = new(); + public List Metrics { get; set; } = new(); + public List Children { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdRawMetric.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdRawMetric.cs new file mode 100644 index 00000000..b87437b5 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdRawMetric.cs @@ -0,0 +1,20 @@ +namespace Perfolizer.Phd.Base; + +public class PhdRawMetric +{ + public string? Id { get; set; } + public int? Version { get; set; } + public int? IterationIndex { get; set; } + public long? InvocationCount { get; set; } + public long? StartTimestamp { get; set; } + public long? EndTimestamp { get; set; } + public string? Marker { get; set; } + + /// + /// Examples: mean, median, min, max, p0, p25, p75, p100 + /// + public string? Aggregator { get; set; } + + public double Value { get; set; } + public string? Unit { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Base/PhdSchema.cs b/src/Perfolizer/Perfolizer/Phd/Base/PhdSchema.cs new file mode 100644 index 00000000..019bc862 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Base/PhdSchema.cs @@ -0,0 +1,31 @@ +namespace Perfolizer.Phd.Base; + +public class PhdSchema(string name) +{ + public string Name { get; } = name; + private readonly List implementations = []; + public IReadOnlyList Implementations => implementations; + + public PhdSchema Add() where T : PhdObject + { + var baseType = GetBaseType(); + if (baseType == null) + throw new InvalidCastException($"Failed to case {typeof(T).Name} to {nameof(PhdObject)}"); + implementations.Add(new PhdImplementation(baseType, typeof(T))); + return this; + } + + private static Type? GetBaseType() + { + var t = typeof(T); + while (t != null && t.Assembly != typeof(PhdObject).Assembly) + t = t.BaseType; + return t; + } +} + +public class PhdImplementation(Type @base, Type derived) +{ + public Type Base { get; } = @base; + public Type Derived { get; } = derived; +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdAttributes.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdAttributes.cs new file mode 100644 index 00000000..1ea0896a --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdAttributes.cs @@ -0,0 +1,53 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public class PhdAttributes +{ + /// + /// Service information (like the structure of the reports) + /// + public PhdMeta? Meta { get; set; } + + /// + /// Primary information about the current report + /// + public PhdInfo? Info { get; set; } + + /// + /// Information about the measurement framework + /// + public PhdEngine? Engine { get; set; } + + /// + /// BuildId, Commit, etc. + /// + public PhdSource? Source { get; set; } + + /// + /// Os, Cpu, etc. + /// + public PhdHost? Host { get; set; } + + /// + /// Benchmark execution + /// + public PhdJob? Job { get; set; } + + /// + /// Benchmark parameters + /// + public PhdParameters? Parameters { get; set; } + + /// + /// Benchmark descriptor + /// + public PhdBenchmark? Benchmark { get; set; } + + /// + /// Stage of the benchmark lifecycle (e.g. Pilot/Warmup/Actual/etc., Overhead/Workload, etc.) + /// + public PhdLifecycle? Lifecycle { get; set; } + + public PhdEntry ToPerfEntry() => new (this); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdBenchmark.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdBenchmark.cs new file mode 100644 index 00000000..330751fd --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdBenchmark.cs @@ -0,0 +1,5 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public abstract class PhdBenchmark : PhdObject; \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdCpu.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdCpu.cs new file mode 100644 index 00000000..4977654c --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdCpu.cs @@ -0,0 +1,31 @@ +using JetBrains.Annotations; +using Perfolizer.Helpers; +using Perfolizer.Horology; +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +[PublicAPI] +public class PhdCpu : PhdObject +{ + public string? ProcessorName { get; set; } + public int? PhysicalProcessorCount { get; set; } + public int? PhysicalCoreCount { get; set; } + public int? LogicalCoreCount { get; set; } + public string? Architecture { get; set; } + public long? NominalFrequencyHz { get; set; } + public long? MinFrequencyHz { get; set; } + public long? MaxFrequencyHz { get; set; } + + public Frequency? GetNominalFrequency() => NominalFrequencyHz.HasValue ? Frequency.FromHz(NominalFrequencyHz.Value) : null; + public Frequency? GetMinFrequency() => MinFrequencyHz.HasValue ? Frequency.FromHz(MinFrequencyHz.Value) : null; + public Frequency? GetMaxFrequency() => MaxFrequencyHz.HasValue ? Frequency.FromHz(MaxFrequencyHz.Value) : null; + + public PhdCpu SetDisplay() + { + Display = this.ToFullBrandName(); + return this; + } + + public override string ToString() => this.ToFullBrandName(); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdEngine.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdEngine.cs new file mode 100644 index 00000000..586b5db9 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdEngine.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +[PublicAPI] +public class PhdEngine : PhdObject +{ + public string Name { get; set; } = ""; + public string? Version { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdEnvironment.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdEnvironment.cs new file mode 100644 index 00000000..a5c026d6 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdEnvironment.cs @@ -0,0 +1,5 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public abstract class PhdEnvironment : PhdObject; \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdExecution.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdExecution.cs new file mode 100644 index 00000000..4f5bf8ee --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdExecution.cs @@ -0,0 +1,5 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public abstract class PhdExecution : PhdObject; \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdHost.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdHost.cs new file mode 100644 index 00000000..bc0f2166 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdHost.cs @@ -0,0 +1,9 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public class PhdHost : PhdObject +{ + public PhdOs? Os { get; set; } + public PhdCpu? Cpu { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdInfo.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdInfo.cs new file mode 100644 index 00000000..0d457ee4 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdInfo.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +[PublicAPI] +public class PhdInfo : PhdObject +{ + public string Title { get; set; } = ""; + public Guid RunId { get; set; } + public long Timestamp { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdJob.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdJob.cs new file mode 100644 index 00000000..812ec81c --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdJob.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +[PublicAPI] +public class PhdJob : PhdObject +{ + public PhdEnvironment? Environment { get; set; } + public PhdExecution? Execution { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdLifecycle.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdLifecycle.cs new file mode 100644 index 00000000..f79b49c0 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdLifecycle.cs @@ -0,0 +1,5 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public abstract class PhdLifecycle : PhdObject; \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdOs.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdOs.cs new file mode 100644 index 00000000..c52de07a --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdOs.cs @@ -0,0 +1,34 @@ +using Perfolizer.Helpers; +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public class PhdOs : PhdObject +{ + /// + /// E.g., Windows, Linux, macOS + /// + public string? Name { get; set; } + + /// + /// E.g., Ubuntu, Fedora, CentOS, Debian, RHEL + /// + public string? Distro { get; set; } + + public string? Version { get; set; } + + public string? KernelVersion { get; set; } + + /// + /// E.g., Docker + /// + public string? Container { get; set; } + + public PhdOs SetDisplay() + { + Display = this.ToBrandString(); + return this; + } + + public override string ToString() => this.ToBrandString(); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdParameters.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdParameters.cs new file mode 100644 index 00000000..ceeb7ecc --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdParameters.cs @@ -0,0 +1,5 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public abstract class PhdParameters : PhdObject; \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdRatioStyle.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdRatioStyle.cs new file mode 100644 index 00000000..69343e88 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdRatioStyle.cs @@ -0,0 +1,8 @@ +namespace Perfolizer.Phd.Dto; + +public enum PhdRatioStyle +{ + Value, + Percentage, + Trend +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdSource.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdSource.cs new file mode 100644 index 00000000..eb30ccf4 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdSource.cs @@ -0,0 +1,5 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Dto; + +public abstract class PhdSource : PhdObject; \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Dto/PhdTextJustification.cs b/src/Perfolizer/Perfolizer/Phd/Dto/PhdTextJustification.cs new file mode 100644 index 00000000..6f98e742 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Dto/PhdTextJustification.cs @@ -0,0 +1,7 @@ +namespace Perfolizer.Phd.Dto; + +public enum PhdTextJustification +{ + Left, + Right +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Functions/PhdFunction.cs b/src/Perfolizer/Perfolizer/Phd/Functions/PhdFunction.cs new file mode 100644 index 00000000..7a013336 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Functions/PhdFunction.cs @@ -0,0 +1,24 @@ +using System.Diagnostics; +using Perfolizer.Helpers; + +namespace Perfolizer.Phd.Functions; + +[DebuggerDisplay("{Id}")] +public class PhdFunction(string id, string title, Func apply) +{ + public PhdFunction(string id, Func apply) : this(id, StringHelper.FirstUpper(id), apply) { } + public string Id { get; } = id; + public string Title { get; } = title; + public Func Apply { get; } = apply; + public string? Legend { get; set; } + + public virtual Type ReturnType { get; } = typeof(object); +} + +[DebuggerDisplay("{Id}")] +public class PhdFunction(string id, string title, Func apply) : PhdFunction(id, title, s => apply(s)) +{ + public PhdFunction(string id, Func apply) : this(id, StringHelper.FirstUpper(id), apply) { } + public new Func Apply { get; } = apply; + public override Type ReturnType { get; } = typeof(T); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Functions/PhdFunctionResolver.cs b/src/Perfolizer/Perfolizer/Phd/Functions/PhdFunctionResolver.cs new file mode 100644 index 00000000..edce335c --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Functions/PhdFunctionResolver.cs @@ -0,0 +1,41 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using Perfolizer.Extensions; +using Perfolizer.Mathematics.GenericEstimators; +using Perfolizer.Mathematics.QuantileEstimators; +using Perfolizer.Mathematics.ScaleEstimators; +using Perfolizer.Metrology; + +namespace Perfolizer.Phd.Functions; + +public class PhdFunctionResolver +{ + private readonly Dictionary registeredFunctions = new (StringComparer.OrdinalIgnoreCase); + + [PublicAPI] + public PhdFunctionResolver Register(params PhdFunction[] functions) + { + foreach (var function in functions) + registeredFunctions[function.Id] = function; + return this; + } + + public PhdFunctionResolver RegisterDefaults() => Register( + new PhdFunction("n", sample => sample.Size.AsMeasurement()) + { Legend = "Sample Size" }, + new PhdFunction("mean", sample => sample.Mean().WithUnit(sample.Unit)) + { Legend = "Arithmetic Average" }, + new PhdFunction("min", sample => sample.Min().WithUnit(sample.Unit)), + new PhdFunction("max", sample => sample.Max().WithUnit(sample.Unit)), + new PhdFunction("median", + sample => TrimmedHarrellDavisQuantileEstimator.Sqrt.Median(sample).WithUnit(sample.Unit)), + new PhdFunction("average", + sample => HodgesLehmannEstimator.Instance.Median(sample).WithUnit(sample.Unit)), + new PhdFunction("spread", + sample => ShamosEstimator.Instance.Scale(sample).WithUnit(sample.Unit)) + ); + + [SuppressMessage("ReSharper", "CanSimplifyDictionaryTryGetValueWithGetValueOrDefault")] + public PhdFunction? Resolve(string id) => + registeredFunctions.TryGetValue(id.TrimStart(':'), out var function) ? function : null; +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Presenting/IPhdTablePresenter.cs b/src/Perfolizer/Perfolizer/Phd/Presenting/IPhdTablePresenter.cs new file mode 100644 index 00000000..6683a8f3 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Presenting/IPhdTablePresenter.cs @@ -0,0 +1,9 @@ +using Perfolizer.Phd.Tables; +using Perfolizer.Presenting; + +namespace Perfolizer.Phd.Presenting; + +public interface IPhdTablePresenter +{ + void Present(IPresenter presenter, PhdTable table, PhdTableStyle style); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Presenting/PhdMarkdownTablePresenter.cs b/src/Perfolizer/Perfolizer/Phd/Presenting/PhdMarkdownTablePresenter.cs new file mode 100644 index 00000000..44ce54c5 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Presenting/PhdMarkdownTablePresenter.cs @@ -0,0 +1,158 @@ +using Perfolizer.Collections; +using Perfolizer.Common; +using Perfolizer.Extensions; +using Perfolizer.Horology; +using Perfolizer.Mathematics.Common; +using Perfolizer.Metrology; +using Perfolizer.Phd.Tables; +using Perfolizer.Presenting; + +namespace Perfolizer.Phd.Presenting; + +public class PhdTableStyle +{ + public IFormatProvider FormatProvider { get; set; } = DefaultCultureInfo.Instance; + public UnitPresentation UnitPresentation { get; set; } = UnitPresentation.WithGap; + public int PreferredWidth { get; set; } = 100; +} + +public class PhdMarkdownTablePresenter : IPhdTablePresenter +{ + public static readonly PhdMarkdownTablePresenter Instance = new (); + + public void Present(IPresenter presenter, PhdTable table, PhdTableStyle style) + { + // ************************************************************************************************************* + // Filling cell objects + object?[,] cellObjects = new object?[table.RowCount, table.ColumnCount]; + for (int row = 0; row < table.RowCount; row++) + for (int col = 0; col < table.ColumnCount; col++) + cellObjects[row, col] = table[row, col]; + + // ************************************************************************************************************* + // Determining cell formats + var formatProvider = style.FormatProvider; + string?[] columnFormats = new string?[table.ColumnCount]; + for (int col = 0; col < table.ColumnCount; col++) + { + var column = table.Columns[col]; + if (column is PhdFunctionColumn functionColumn) + { + var measurements = table.Rows + .Select(row => row[functionColumn].Value as Measurement) + .WhereNotNull() + .ToArray(); + var unit = measurements.First().Unit; + + if (unit is TimeUnit timeUnit) + { + double[] timeIntervals = measurements + .Select(m => m.AsTimeInterval()!.Value.Nanoseconds) + .WhereNotNull() + .ToArray(); + var bestUnit = TimeUnit.GetBestTimeUnit(timeIntervals); + for (int i = 0; i < measurements.Length; i++) + { + double nominalValue = TimeUnit.Convert(measurements[i].NominalValue, timeUnit, bestUnit); + measurements[i] = nominalValue.WithUnit(bestUnit); + timeIntervals[i] = nominalValue; + } + int precision = PrecisionHelper.GetOptimalPrecision(timeIntervals); + columnFormats[col] = $"F{precision}"; + } + } + } + + // ************************************************************************************************************* + // Filling cells + string[,] cells = new string[table.RowCount, table.ColumnCount]; + for (int row = 0; row < table.RowCount; row++) + for (int col = 0; col < table.ColumnCount; col++) + { + object? cellObject = cellObjects[row, col]; + cells[row, col] = cellObject switch + { + IWithUnits withUnit => withUnit.ToString(columnFormats[col], formatProvider, style.UnitPresentation), + _ => cellObject?.ToString() ?? "" + }; + } + + // ************************************************************************************************************* + // Determining column widths + int[] columnWidths = new int[table.ColumnCount]; + for (int col = 0; col < table.ColumnCount; col++) + { + columnWidths[col] = table.Columns[col].Title.Length; + for (int row = 0; row < table.RowCount; row++) + columnWidths[col] = Max(columnWidths[col], cells[row, col].Length); + } + + // ************************************************************************************************************* + // Presenting shared cells + if (table.SharedCells.Any()) + { + var groups = table.SharedCells.Select(cell => cell.Column.Definition.Group).Distinct().ToList(); + foreach (string group in groups) + { + var cellPresentations = new List(); + var groupCells = table.SharedCells + .Where(cell => cell.Column.Definition.Group.EquationsIgnoreCase(group)); + foreach (var sharedCell in groupCells) + { + string cellContent = sharedCell.Value?.ToString() ?? ""; // TODO: Present + string cellPresentation = sharedCell.Column.Definition.IsSelfExplanatory + ? cellContent + : $"{sharedCell.Column.Title} = {cellContent}"; + cellPresentations.Add(cellPresentation); + } + + const string separator = ", "; + int currentLineLength = 0; + foreach (string cell in cellPresentations) + { + if (currentLineLength == 0 || + currentLineLength > 0 && currentLineLength + separator.Length + cell.Length > style.PreferredWidth) + { + if (currentLineLength > 0) + presenter.WriteLine(); + presenter.Write(cell); + currentLineLength = cell.Length; + } + else + { + currentLineLength += separator.Length + cell.Length; + presenter.Write(separator); + presenter.Write(cell); + } + } + + if (currentLineLength > 0) + presenter.WriteLine(); + presenter.WriteLine(); + } + } + + // ************************************************************************************************************* + // Presenting the table + void PresentRow(Func presentCell) + { + for (int col = 0; col < table.ColumnCount; col++) + { + if (col == 0) + presenter.Write("|"); + presenter.Write(presentCell(col)); + presenter.Write('|'); + } + presenter.WriteLine(); + } + + PresentRow(col => ' ' + table.Columns[col].Title.PadLeft(columnWidths[col]) + ' '); + PresentRow(col => new string('-', columnWidths[col] + 2)); // TODO: Justification + for (int row = 0; row < table.RowCount; row++) + { + int currentRow = row; + PresentRow(col => " " + cells[currentRow, col].PadLeft(columnWidths[col]) + " "); // TODO: Justification + } + presenter.Flush(); + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Reports/PhdBlob.cs b/src/Perfolizer/Perfolizer/Phd/Reports/PhdBlob.cs new file mode 100644 index 00000000..447498a2 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Reports/PhdBlob.cs @@ -0,0 +1,21 @@ +using System.Text; +using Perfolizer.Phd.Base; +using Perfolizer.Presenting; + +namespace Perfolizer.Phd.Reports; + +public class PhdBlob : PhdSection +{ + public int WrapWidth { get; set; } = 100; + public string? Join { get; set; } + public List Items { get; set; } = []; + + public override void Present(IPresenter presenter, PhdEntry entry) + { + var builder = new StringBuilder(); + foreach (string item in Items) + { + + } + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Reports/PhdReport.cs b/src/Perfolizer/Perfolizer/Phd/Reports/PhdReport.cs new file mode 100644 index 00000000..8449d500 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Reports/PhdReport.cs @@ -0,0 +1,16 @@ +using Perfolizer.Phd.Base; +using Perfolizer.Presenting; + +namespace Perfolizer.Phd.Reports; + +public class PhdReport +{ + public string? Name { get; set; } + public List Sections { get; set; } = []; + + public void Present(IPresenter presenter, PhdEntry entry) + { + foreach (var phdSection in Sections) + phdSection.Present(presenter, entry); + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Reports/PhdReportHelper.cs b/src/Perfolizer/Perfolizer/Phd/Reports/PhdReportHelper.cs new file mode 100644 index 00000000..2fc5d1b0 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Reports/PhdReportHelper.cs @@ -0,0 +1,44 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Reports; + +public class PhdReportHelper +{ + public static readonly PhdReportHelper Instance = new (); + private PhdReportHelper() { } + + public string? Format(PhdEntry entry, string id) + { + var obj = Resolve(entry, id); + if (obj == null) return null; + + if (obj.Display != null) + return obj.Display; + return null; + } + + public PhdObject? Resolve(PhdEntry? entry, string id) + { + while (entry != null) + { + var own = ResolveOwn(entry, id); + if (own != null) + return own; + entry = entry.Parent; + } + return null; + } + + // TODO: provide generic Reflection-based implementation + private static PhdObject? ResolveOwn(PhdEntry entry, string id) + { + var attr = entry.Attributes; + return id switch + { + "engine" => attr.Engine, + "os" => attr.Host?.Os, + "cpu" => attr.Host?.Cpu, + _ => null + }; + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Reports/PhdSection.cs b/src/Perfolizer/Perfolizer/Phd/Reports/PhdSection.cs new file mode 100644 index 00000000..5059a1b0 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Reports/PhdSection.cs @@ -0,0 +1,9 @@ +using Perfolizer.Phd.Base; +using Perfolizer.Presenting; + +namespace Perfolizer.Phd.Reports; + +public abstract class PhdSection : PhdObject +{ + public abstract void Present(IPresenter presenter, PhdEntry entry); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Reports/PhdSummary.cs b/src/Perfolizer/Perfolizer/Phd/Reports/PhdSummary.cs new file mode 100644 index 00000000..1e45dc5e --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Reports/PhdSummary.cs @@ -0,0 +1,18 @@ +using Perfolizer.Horology; +using Perfolizer.Metrology; +using Perfolizer.Phd.Dto; + +namespace Perfolizer.Phd.Reports; + +public class PhdSummary +{ + public bool PrintUnitsInHeader { get; set; } + public bool PrintUnitsInContent { get; set; } + public bool PrintZeroValuesInContent { get; set; } + public int MaxParameterColumnWidth { get; set; } + public SizeUnit? SizeUnit { get; set; } + public TimeUnit? TimeUnit { get; set; } + public PhdRatioStyle PhdRatioStyle { get; set; } + public PhdTextJustification PhdTextColumnJustification { get; set; } + public PhdTextJustification NumericColumnJustification { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/TODO.md b/src/Perfolizer/Perfolizer/Phd/TODO.md new file mode 100644 index 00000000..f7370230 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/TODO.md @@ -0,0 +1,8 @@ +# TODO + +* Sorting (columns and rows) +* Logical and physical groups +* Column justification +* Shared cell groups +* Nice Job presenting in summary +* Parameters \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdAttributeColumn.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdAttributeColumn.cs new file mode 100644 index 00000000..2e239eed --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdAttributeColumn.cs @@ -0,0 +1,9 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Tables; + +public class PhdAttributeColumn(string title, PhdColumnDefinition definition, PhdKey key) : PhdColumn(title, definition) +{ + public PhdKey Key { get; } = key; + public override string Selector => Definition.Selector + "_" + Key.Path; +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdCell.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdCell.cs new file mode 100644 index 00000000..28f92b74 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdCell.cs @@ -0,0 +1,10 @@ +using System.Diagnostics; + +namespace Perfolizer.Phd.Tables; + +[DebuggerDisplay("{Column.Title}={Value}")] +public class PhdCell(PhdColumn column, object? value) +{ + public PhdColumn Column { get; } = column; + public object? Value { get; } = value; +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdColumn.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdColumn.cs new file mode 100644 index 00000000..9179a78b --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdColumn.cs @@ -0,0 +1,11 @@ +using System.Diagnostics; + +namespace Perfolizer.Phd.Tables; + +[DebuggerDisplay("{Title}({Selector})")] +public class PhdColumn(string title, PhdColumnDefinition definition) +{ + public string Title { get; } = title; + public PhdColumnDefinition Definition { get; } = definition; + public virtual string Selector => Definition.Selector; +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdColumnDefinition.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdColumnDefinition.cs new file mode 100644 index 00000000..663d2ff8 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdColumnDefinition.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; + +namespace Perfolizer.Phd.Tables; + +[DebuggerDisplay("{Title}({Selector})")] +public class PhdColumnDefinition +{ + public string Title { get; set; } = ""; + public string Selector { get; set; } = ""; + + public string Group { get; set; } = ""; + + /// + /// If true, when presented in the shared section, the title is omitted. + /// + public bool IsSelfExplanatory { get; set; } + + /// + /// Controls whether the column can be presented in the shared section + /// + public bool? CanBeShared { get; set; } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdFilter.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdFilter.cs new file mode 100644 index 00000000..238ab173 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdFilter.cs @@ -0,0 +1,11 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Tables; + +public class PhdFilter +{ + public bool IsMatched(PhdEntry entry) + { + return true; // TODO + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdFunctionColumn.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdFunctionColumn.cs new file mode 100644 index 00000000..670c0e05 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdFunctionColumn.cs @@ -0,0 +1,18 @@ +using System.Diagnostics; +using Perfolizer.Phd.Functions; + +namespace Perfolizer.Phd.Tables; + +[DebuggerDisplay("{Function.Id}")] +public class PhdFunctionColumn(PhdColumnDefinition definition, PhdFunction function) + : PhdColumn(function.Title, definition) +{ + public PhdFunction Function { get; } = function; +} + +[DebuggerDisplay("{Function.Id}")] +public class PhdFunctionColumn(PhdColumnDefinition definition, PhdFunction function) + : PhdFunctionColumn(definition, function) +{ + public new PhdFunction Function { get; } = function; +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdRow.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdRow.cs new file mode 100644 index 00000000..6415e34a --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdRow.cs @@ -0,0 +1,34 @@ +using System.Diagnostics; +using System.Text; +using Perfolizer.Collections; +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Tables; + +[DebuggerDisplay("{Metrics.Count} metrics, {cells.Count} cells")] +public class PhdRow +{ + private readonly Dictionary cells = new (); + public List Metrics { get; } = []; + + public PhdCell this[PhdColumn column] + { + get => cells[column.Selector]; + set => cells[column.Selector] = value; + } + + public string BuildAttributeId() + { + var builder = new StringBuilder(); + foreach (var cell in cells.Values) + if (cell.Column is PhdAttributeColumn) + { + if (builder.Length > 0) + builder.Append("_"); + builder.Append(cell.Value); + } + return builder.ToString(); + } + + public Sample ToSample() => Metrics.Select(metric => metric.Value).ToSample(Metrics.First().Unit); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdTable.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdTable.cs new file mode 100644 index 00000000..c1113f2a --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdTable.cs @@ -0,0 +1,124 @@ +using Perfolizer.Collections; +using Perfolizer.Extensions; +using Perfolizer.Metrology; +using Perfolizer.Phd.Base; +using Perfolizer.Phd.Functions; + +namespace Perfolizer.Phd.Tables; + +public class PhdTable +{ + public PhdTableConfig Config { get; } + public PhdColumn[] Columns { get; } + public PhdRow[] Rows { get; } + public PhdCell[] SharedCells { get; } + public int RowCount => Rows.Length; + public int ColumnCount => Columns.Length; + public object? this[int row, int col] => Rows[row][Columns[col]].Value; + + // TODO: extract common + public PhdTable(PhdEntry rootEntry, PhdTableConfig config) + { + Config = config; + var index = new PhdIndex(rootEntry); + var keys = index.Keys; + + // Columns + var columns = new List(); + var functionResolver = new PhdFunctionResolver().RegisterDefaults(); + foreach (var definition in config.ColumnDefinitions) + { + // PhdFunctionColumn + if (definition.Selector.StartsWith(":")) + { + var function = functionResolver.Resolve(definition.Selector); + if (function == null) + continue; // TODO + + var column = function switch + { + PhdFunction measurementFunction => + new PhdFunctionColumn(definition, measurementFunction), + _ => new PhdFunctionColumn(definition, function) + }; + columns.Add(column); + } + + // PhdAttributeColumn + var columnKeys = keys.Where(key => key.IsMatched(definition.Selector)).ToReadOnlyList(); + if (columnKeys.IsEmpty()) continue; + + var displayKey = columnKeys.FirstOrDefault(key => key.IsMatched($"{definition.Selector}.Display")); + if (displayKey != null) + { + string title = definition.Title; + if (title.IsBlank()) + title = displayKey.Name; + columns.Add(new PhdAttributeColumn(title, definition, displayKey)); + } + else + { + // TODO: Support display columns + var primitiveColumnKeys = columnKeys.Where(key => !key.IsComposite()).ToList(); // TODO: support composite keys + foreach (var columnKey in primitiveColumnKeys) + { + string title = definition.Title; + if (title.IsBlank()) + title = columnKey.Name; + columns.Add(new PhdAttributeColumn(title, definition, columnKey)); + } + } + } + + // Rows + var rowMap = new Dictionary(); + foreach (var entry in rootEntry.Traverse().Where(config.IsMatched)) + { + if (entry.Metrics.IsEmpty()) + continue; + var row = new PhdRow(); + foreach (var column in columns.OfType()) + { + object? value = index[entry][column.Key]?.Value; + row[column] = new PhdCell(column, value); + } + string definitionId = row.BuildAttributeId(); + if (!rowMap.TryGetValue(definitionId, out var existingRow)) + rowMap[definitionId] = row; + else + row = existingRow; + row.Metrics.AddRange(entry.Metrics); + } + var rows = rowMap.Values.ToReadOnlyList(); + + // Function cells + foreach (var row in rows) + foreach (var column in columns.OfType()) + { + object? value = column.Function.Apply(row.ToSample()); + row[column] = new PhdCell(column, value); + } + + // Shared cells + var sharedCells = new List(); + var sharedColumns = new HashSet(); + foreach (var column in columns) + { + if (column.Definition.CanBeShared == false) continue; + if (column.Definition.CanBeShared == null && column is PhdFunctionColumn) continue; + + var cells = rows.Select(row => row[column]).ToReadOnlyList(); + if (cells.Select(cell => cell.Value).Distinct().Count() == 1) + { + sharedColumns.Add(column); + sharedCells.Add(cells.First()); + } + } + columns.RemoveAll(column => sharedColumns.Contains(column)); + + // Finalization + Columns = columns.ToArray(); // TODO: Sort + Rows = rows.ToArray(); // TODO: Sort + SharedCells = sharedCells.ToArray(); // TODO: Sort + } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Phd/Tables/PhdTableConfig.cs b/src/Perfolizer/Perfolizer/Phd/Tables/PhdTableConfig.cs new file mode 100644 index 00000000..e4adeb60 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Phd/Tables/PhdTableConfig.cs @@ -0,0 +1,11 @@ +using Perfolizer.Phd.Base; + +namespace Perfolizer.Phd.Tables; + +public class PhdTableConfig +{ + public List Filters { get; } = []; + public List ColumnDefinitions { get; } = []; + + public bool IsMatched(PhdEntry entry) => Filters.All(filter => filter.IsMatched(entry)); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Presenting/BufferedPresenter.cs b/src/Perfolizer/Perfolizer/Presenting/BufferedPresenter.cs new file mode 100644 index 00000000..b1bc4c4d --- /dev/null +++ b/src/Perfolizer/Perfolizer/Presenting/BufferedPresenter.cs @@ -0,0 +1,20 @@ +using System.Text; + +namespace Perfolizer.Presenting; + +public abstract class BufferedPresenter : IPresenter +{ + protected readonly StringBuilder Builder = new (); + + public void Write(char c) => Builder.Append(c); + public void Write(string message) => Builder.Append(message); + public void WriteLine() => Builder.AppendLine(); + + public virtual void Flush() + { + Flush(Builder.ToString()); + Builder.Clear(); + } + + protected abstract void Flush(string text); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Presenting/ConsolePresenter.cs b/src/Perfolizer/Perfolizer/Presenting/ConsolePresenter.cs new file mode 100644 index 00000000..f250ca2f --- /dev/null +++ b/src/Perfolizer/Perfolizer/Presenting/ConsolePresenter.cs @@ -0,0 +1,9 @@ +namespace Perfolizer.Presenting; + +public class ConsolePresenter : IPresenter +{ + public void Write(char c) => Console.Write(c); + public void Write(string message) => Console.Write(message); + public void WriteLine() => Console.WriteLine(); + public void Flush() { } +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Presenting/IPresenter.cs b/src/Perfolizer/Perfolizer/Presenting/IPresenter.cs new file mode 100644 index 00000000..8984b41c --- /dev/null +++ b/src/Perfolizer/Perfolizer/Presenting/IPresenter.cs @@ -0,0 +1,9 @@ +namespace Perfolizer.Presenting; + +public interface IPresenter +{ + void Write(char c); + void Write(string message); + void WriteLine(); + void Flush(); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Presenting/StringPresenter.cs b/src/Perfolizer/Perfolizer/Presenting/StringPresenter.cs new file mode 100644 index 00000000..e0c91a19 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Presenting/StringPresenter.cs @@ -0,0 +1,8 @@ +namespace Perfolizer.Presenting; + +public class StringPresenter : BufferedPresenter +{ + public override void Flush() { } + protected override void Flush(string text) { } + public string Dump() => Builder.ToString(); +} \ No newline at end of file diff --git a/src/Perfolizer/Perfolizer/Resources/microarchitectures.txt b/src/Perfolizer/Perfolizer/Resources/microarchitectures.txt new file mode 100644 index 00000000..f06d2953 --- /dev/null +++ b/src/Perfolizer/Perfolizer/Resources/microarchitectures.txt @@ -0,0 +1,183 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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/Perfolizer/Perfolizer/Sample.cs b/src/Perfolizer/Perfolizer/Sample.cs index 15a6ab51..42d80be9 100644 --- a/src/Perfolizer/Perfolizer/Sample.cs +++ b/src/Perfolizer/Perfolizer/Sample.cs @@ -36,20 +36,16 @@ public class Sample : IWithUnits /// public double WeightedSize { get; } - public Sample(params double[] values) : this(values, null) - { - } + public Sample(params double[] values) : this(values, null) { } - public Sample(params int[] values) : this(values, null) - { - } + public Sample(params int[] values) : this(values, null) { } - public Sample(IReadOnlyList values, MeasurementUnit? measurementUnit = null) + public Sample(IReadOnlyList values, MeasurementUnit? unit = null) { Assertion.NotNullOrEmpty(nameof(values), values); Values = values; - Unit = measurementUnit ?? NumberUnit.Instance; + Unit = unit ?? NumberUnit.Instance; double weight = 1.0 / values.Count; Weights = new IdenticalReadOnlyList(values.Count, weight); TotalWeight = 1.0; @@ -79,8 +75,8 @@ public Sample(IReadOnlyList values, IReadOnlyList weights, Measu { totalWeight += weight; totalWeightSquared += weight.Sqr(); - maxWeight = Max(maxWeight, weight); - minWeight = Min(minWeight, weight); + maxWeight = Math.Max(maxWeight, weight); + minWeight = Math.Min(minWeight, weight); } if (minWeight < 0) @@ -111,15 +107,11 @@ public Sample(IReadOnlyList values, IReadOnlyList weights, Measu } [PublicAPI] - public Sample(IEnumerable values, MeasurementUnit? measurementUnit = null) - : this(values.Select(x => (double)x).ToList(), measurementUnit) - { - } + public Sample(IEnumerable values, MeasurementUnit? unit = null) + : this(values.Select(x => (double)x).ToList(), unit) { } - public Sample(IEnumerable values, MeasurementUnit? measurementUnit = null) - : this(values.Select(x => (double)x).ToList(), measurementUnit) - { - } + public Sample(IEnumerable values, MeasurementUnit? unit = null) + : this(values.Select(x => (double)x).ToList(), unit) { } public Sample Concat(Sample sample) { @@ -192,6 +184,7 @@ public static bool TryParse(string s, out Sample sample) string unitString = s.Substring(closeBracketIndex + 1); if (!MeasurementUnit.TryParse(unitString, out var unit)) + return false; sample = new Sample(values, unit); @@ -250,4 +243,8 @@ private static bool IsSorted(IReadOnlyList list) return false; return true; } + + public double Mean() => Values.Average(); + public double Min() => SortedValues.First(); + public double Max() => SortedValues.Last(); } \ No newline at end of file