From 7f26fb43388b559b3c036372666b07d3ef72b982 Mon Sep 17 00:00:00 2001 From: axunonb Date: Tue, 4 Mar 2025 17:02:59 +0100 Subject: [PATCH] Fix: Benchmarks Updated `ApplicationWorkflows` and `CalDateTimePerfTests` to use `CalDateTime` Enhanced `OccurencePerfTests` with new benchmarks and setup methods Project file to target only `.NET 8.0`. Refactored `Runner` class for improved benchmark execution flow and removed outdated code. --- Ical.Net.Benchmarks/ApplicationWorkflows.cs | 8 +- Ical.Net.Benchmarks/CalDateTimePerfTests.cs | 4 +- .../Ical.Net.Benchmarks.csproj | 2 +- Ical.Net.Benchmarks/OccurencePerfTests.cs | 63 ++++-- Ical.Net.Benchmarks/Runner.cs | 179 +++--------------- 5 files changed, 84 insertions(+), 172 deletions(-) diff --git a/Ical.Net.Benchmarks/ApplicationWorkflows.cs b/Ical.Net.Benchmarks/ApplicationWorkflows.cs index 82863cac..669a211c 100644 --- a/Ical.Net.Benchmarks/ApplicationWorkflows.cs +++ b/Ical.Net.Benchmarks/ApplicationWorkflows.cs @@ -14,9 +14,8 @@ namespace Ical.Net.Benchmarks; public class ApplicationWorkflows { - private static readonly TimeSpan _oneYear = TimeSpan.FromDays(365); - private static readonly DateTime _searchStart = DateTime.Now.Subtract(_oneYear); - private static readonly DateTime _searchEnd = DateTime.Now.Add(_oneYear); + private static readonly CalDateTime _searchStart = CalDateTime.Now.AddDays(-365); + private static readonly CalDateTime _searchEnd = CalDateTime.Now; private static readonly List _manyCalendars = GetIcalStrings(); private static List GetIcalStrings() @@ -24,6 +23,7 @@ private static List GetIcalStrings() var testProjectDirectory = Runner.FindParentFolder("Ical.Net.Tests", Directory.GetCurrentDirectory()); var topLevelIcsPath = Path.GetFullPath(Path.Combine(testProjectDirectory, "Calendars")); return Directory.EnumerateFiles(topLevelIcsPath, "*.ics", SearchOption.AllDirectories) + .Where(p => !p.EndsWith("DateTime1.ics")) // contains a deliberate error .Select(File.ReadAllText) .Distinct(StringComparer.OrdinalIgnoreCase) .Where(s => !s.Contains("InternetExplorer") && !s.Contains("SECONDLY")) @@ -75,4 +75,4 @@ public List ParallelDeserializeSequentialGatherEventsParallelGetOccu .SelectMany(e => e.GetOccurrences(_searchStart, _searchEnd)) .ToList(); } -} \ No newline at end of file +} diff --git a/Ical.Net.Benchmarks/CalDateTimePerfTests.cs b/Ical.Net.Benchmarks/CalDateTimePerfTests.cs index c09c8deb..9fcd2172 100644 --- a/Ical.Net.Benchmarks/CalDateTimePerfTests.cs +++ b/Ical.Net.Benchmarks/CalDateTimePerfTests.cs @@ -15,7 +15,7 @@ public class CalDateTimePerfTests private const string _bTzid = "America/New_York"; [Benchmark] - public CalDateTime EmptyTzid() => new CalDateTime(DateTime.Now); + public CalDateTime EmptyTzid() => CalDateTime.Now; [Benchmark] public CalDateTime SpecifiedTzid() => new CalDateTime(DateTime.Now, _aTzid); @@ -31,4 +31,4 @@ public class CalDateTimePerfTests [Benchmark] public CalDateTime UtcToDifferentTzid() => UtcDateTime().ToTimeZone(_bTzid); -} \ No newline at end of file +} diff --git a/Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj b/Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj index 1659d678..b2835058 100644 --- a/Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj +++ b/Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj @@ -1,6 +1,6 @@  - net8.0;net6.0;netcoreapp3.1;net48 + net8.0 Exe latest diff --git a/Ical.Net.Benchmarks/OccurencePerfTests.cs b/Ical.Net.Benchmarks/OccurencePerfTests.cs index 5f0493a0..be9f043e 100644 --- a/Ical.Net.Benchmarks/OccurencePerfTests.cs +++ b/Ical.Net.Benchmarks/OccurencePerfTests.cs @@ -14,22 +14,36 @@ namespace Ical.Net.Benchmarks; public class OccurencePerfTests { + private Calendar _calendarFourEvents = null!; + private Calendar _calendarWithRecurrences = null!; + + [GlobalSetup] + public void Setup() + { + _calendarFourEvents = GetFourCalendarEventsWithUntilRule(); + _calendarWithRecurrences = GenerateCalendarWithRecurrences(); + } + + [Benchmark] + public void GetOccurrences() + { + _ = _calendarWithRecurrences.GetOccurrences().ToList(); + } + [Benchmark] public void MultipleEventsWithUntilOccurrencesSearchingByWholeCalendar() { - var calendar = GetFourCalendarEventsWithUntilRule(); - var searchStart = calendar.Events.First().DtStart.AddYears(-1); - var searchEnd = calendar.Events.Last().DtStart.AddYears(1); - _ = calendar.GetOccurrences(searchStart, searchEnd); + var searchStart = _calendarFourEvents.Events.First().DtStart.AddYears(-1); + var searchEnd = _calendarFourEvents.Events.Last().DtStart.AddYears(1); + _ = _calendarFourEvents.GetOccurrences(searchStart, searchEnd); } [Benchmark] public void MultipleEventsWithUntilOccurrences() { - var calendar = GetFourCalendarEventsWithUntilRule(); - var searchStart = calendar.Events.First().DtStart.AddYears(-1); - var searchEnd = calendar.Events.Last().DtStart.AddYears(1); - _ = calendar.Events + var searchStart = _calendarFourEvents.Events.First().DtStart.AddYears(-1); + var searchEnd = _calendarFourEvents.Events.Last().DtStart.AddYears(1); + _ = _calendarFourEvents.Events .SelectMany(e => e.GetOccurrences(searchStart, searchEnd)) .ToList(); } @@ -37,21 +51,36 @@ public void MultipleEventsWithUntilOccurrences() [Benchmark] public void MultipleEventsWithUntilOccurrencesEventsAsParallel() { - var calendar = GetFourCalendarEventsWithUntilRule(); - var searchStart = calendar.Events.First().DtStart.AddYears(-1); - var searchEnd = calendar.Events.Last().DtStart.AddYears(1).AddDays(10); - _ = calendar.Events + var searchStart = _calendarFourEvents.Events.First().DtStart.AddYears(-1); + var searchEnd = _calendarFourEvents.Events.Last().DtStart.AddYears(1).AddDays(10); + _ = _calendarFourEvents.Events .AsParallel() .SelectMany(e => e.GetOccurrences(searchStart, searchEnd)) .ToList(); } + private static Calendar GenerateCalendarWithRecurrences() + { + var calendar = new Calendar(); + var dailyEvent = new CalendarEvent + { + Start = new CalDateTime(2025, 3, 1), + End = null, + RecurrenceRules = new List + { + new RecurrencePattern(FrequencyType.Daily, 1) { Count = 1000 } + } + }; + calendar.Events.Add(dailyEvent); + return calendar; + } + private static Calendar GetFourCalendarEventsWithUntilRule() { const string tzid = "America/New_York"; const int limit = 4; - var startTime = DateTime.Now.AddDays(-1); + var startTime = CalDateTime.Now.AddDays(-1); var interval = TimeSpan.FromDays(1); var events = Enumerable @@ -65,11 +94,11 @@ private static Calendar GetFourCalendarEventsWithUntilRule() var e = new CalendarEvent { - Start = new CalDateTime(startTime.AddMinutes(5), tzid), - End = new CalDateTime(startTime.AddMinutes(10), tzid), + Start = startTime.AddMinutes(5).ToTimeZone(tzid), + End = startTime.AddMinutes(10).ToTimeZone(tzid), RecurrenceRules = new List { rrule }, }; - startTime += interval; + startTime = startTime.Add(Duration.FromTimeSpanExact(interval)); return e; }); @@ -133,7 +162,7 @@ private static Calendar GetFourCalendarEventsWithCountRule() End = new CalDateTime(startTime.AddMinutes(10), tzid), RecurrenceRules = new List { rrule }, }; - startTime += interval; + startTime = startTime.Add(interval); return e; }); diff --git a/Ical.Net.Benchmarks/Runner.cs b/Ical.Net.Benchmarks/Runner.cs index fd4dbc1c..03e52a68 100644 --- a/Ical.Net.Benchmarks/Runner.cs +++ b/Ical.Net.Benchmarks/Runner.cs @@ -4,7 +4,11 @@ // using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains.InProcess.NoEmit; +using System; +using System.Diagnostics; using System.IO; namespace Ical.Net.Benchmarks; @@ -13,157 +17,36 @@ public class Runner { private static void Main(string[] args) { -#if DEBUG - BenchmarkSwitcher.FromAssembly(typeof(ApplicationWorkflows).Assembly).Run(args, new DebugInProcessConfig()); -#else - #region * ApplicationWorkflows results * - /* - // * Summary * - - BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) - 13th Gen Intel Core i7-13700K, 1 CPU, 24 logical and 16 physical cores - .NET SDK 8.0.403 - [Host] : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - DefaultJob : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - - | Method | Mean | Error | StdDev | - |---------------------------------------------------------------- |---------:|---------:|---------:| - | SingleThreaded | 18.20 ms | 0.184 ms | 0.163 ms | - | ParallelUponDeserialize | 17.56 ms | 0.350 ms | 0.739 ms | - | ParallelUponGetOccurrences | 26.39 ms | 0.294 ms | 0.275 ms | - | ParallelDeserializeSequentialGatherEventsParallelGetOccurrences | 19.39 ms | 0.373 ms | 0.399 ms | - - // * Hints * - Outliers - ApplicationWorkflows.SingleThreaded: Default -> 1 outlier was removed (18.95 ms) - ApplicationWorkflows.ParallelUponGetOccurrences: Default -> 1 outlier was detected (25.79 ms) - - // * Legends * - Mean : Arithmetic mean of all measurements - Error : Half of 99.9% confidence interval - StdDev : Standard deviation of all measurements - 1 ms : 1 Millisecond (0.001 sec) - - // ***** BenchmarkRunner: End ***** - */ - #endregion - BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(ApplicationWorkflows))); + if (Debugger.IsAttached) + { + var applicationWorkflows = new ApplicationWorkflows(); + applicationWorkflows.SingleThreaded(); + + var benchmark = new OccurencePerfTests(); + benchmark.Setup(); + benchmark.GetOccurrences(); + } + else + { + var config = ManualConfig.Create(DefaultConfig.Instance) + .WithOptions(ConfigOptions.DisableOptimizationsValidator) + .AddJob(Job.Default.WithToolchain(InProcessNoEmitToolchain.Instance)); + + Console.WriteLine("Running ApplicationWorkflows benchmarks..."); + BenchmarkRunner.Run(config); - #region * OccurencePerfTests results * - /* - // * Summary * - - BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) - 13th Gen Intel Core i7-13700K, 1 CPU, 24 logical and 16 physical cores - .NET SDK 8.0.403 - [Host] : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - DefaultJob : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - - | Method | Mean | Error | StdDev | - |----------------------------------------------------------- |-----------:|---------:|---------:| - | MultipleEventsWithUntilOccurrencesSearchingByWholeCalendar | 175.2 us | 1.87 us | 1.66 us | - | MultipleEventsWithUntilOccurrences | 126.6 us | 1.43 us | 1.34 us | - | MultipleEventsWithUntilOccurrencesEventsAsParallel | NA | NA | NA | - | MultipleEventsWithCountOccurrencesSearchingByWholeCalendar | 1,672.7 us | 33.29 us | 31.14 us | - | MultipleEventsWithCountOccurrences | 1,066.6 us | 20.83 us | 30.53 us | - | MultipleEventsWithCountOccurrencesEventsAsParallel | NA | NA | NA | - - Benchmarks with issues (System.AggregateException: One or more errors occurred. (Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct. - OccurencePerfTests.MultipleEventsWithUntilOccurrencesEventsAsParallel: DefaultJob - OccurencePerfTests.MultipleEventsWithCountOccurrencesEventsAsParallel: DefaultJob - - */ - #endregion - BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(OccurencePerfTests))); + Console.WriteLine("Running OccurencePerfTests benchmarks..."); + BenchmarkRunner.Run(config); - #region * CalDateTimePerfTests results * - /* - // * Summary * - - BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) - 13th Gen Intel Core i7-13700K, 1 CPU, 24 logical and 16 physical cores - .NET SDK 8.0.403 - [Host] : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - DefaultJob : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - - | Method | Mean | Error | StdDev | - |----------------------------- |----------:|---------:|---------:| - | EmptyTzid | 97.35 ns | 0.700 ns | 0.620 ns | - | SpecifiedTzid | 219.31 ns | 4.406 ns | 5.729 ns | - | UtcDateTime | 193.75 ns | 3.563 ns | 3.333 ns | - | EmptyTzidToTzid | 412.57 ns | 6.857 ns | 6.414 ns | - | SpecifiedTzidToDifferentTzid | 494.44 ns | 8.299 ns | 7.763 ns | - | UtcToDifferentTzid | 437.86 ns | 3.880 ns | 3.630 ns | - - // * Hints * - Outliers - CalDateTimePerfTests.EmptyTzid: Default -> 1 outlier was removed (101.43 ns) - */ - #endregion - BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(CalDateTimePerfTests))); + Console.WriteLine("Running CalDateTimePerfTests benchmarks..."); + BenchmarkRunner.Run(config); - #region * SerializationPerfTests results * - /* - // * Summary * - - BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) - 13th Gen Intel Core i7-13700K, 1 CPU, 24 logical and 16 physical cores - .NET SDK 8.0.403 - [Host] : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - DefaultJob : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - - - | Method | Mean | Error | StdDev | - |--------------------------- |---------:|---------:|---------:| - | Deserialize | 53.40 us | 0.456 us | 0.404 us | - | BenchmarkSerializeCalendar | 13.76 us | 0.266 us | 0.285 us | - - // * Hints * - Outliers - SerializationPerfTests.Deserialize: Default -> 1 outlier was removed (54.96 us) - - // * Legends * - Mean : Arithmetic mean of all measurements - Error : Half of 99.9% confidence interval - StdDev : Standard deviation of all measurements - 1 us : 1 Microsecond (0.000001 sec) - - // ***** BenchmarkRunner: End ***** - */ - #endregion - BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(SerializationPerfTests))); + Console.WriteLine("Running SerializationPerfTests benchmarks..."); + BenchmarkRunner.Run(config); - #region * ThroughputTests results * - /* - // * Summary * - - BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3) - 13th Gen Intel Core i7-13700K, 1 CPU, 24 logical and 16 physical cores - .NET SDK 8.0.403 - [Host] : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - DefaultJob : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2 - - - | Method | Mean | Error | StdDev | - |-------------------------------------- |---------:|----------:|----------:| - | DeserializeAndComputeUntilOccurrences | 2.178 ms | 0.0410 ms | 0.0403 ms | - | DeserializeAndComputeCountOccurrences | 2.116 ms | 0.0277 ms | 0.0245 ms | - - // * Hints * - Outliers - ThroughputTests.DeserializeAndComputeCountOccurrences: Default -> 1 outlier was removed (2.20 ms) - - // * Legends * - Mean : Arithmetic mean of all measurements - Error : Half of 99.9% confidence interval - StdDev : Standard deviation of all measurements - 1 ms : 1 Millisecond (0.001 sec) - - // ***** BenchmarkRunner: End ***** - */ - #endregion - BenchmarkRunner.Run(BenchmarkConverter.TypeToBenchmarks(typeof(ThroughputTests))); -#endif + Console.WriteLine("Running ThroughputTests benchmarks..."); + BenchmarkRunner.Run(config); + } } /// @@ -200,4 +83,4 @@ public static string FindParentFolder(string directoryName, string startPath) if (!Directory.Exists(resultPath)) throw new DirectoryNotFoundException($"Folder '{directoryName}' not found in parent directories."); return resultPath; } -} \ No newline at end of file +}