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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Ical.Net.Benchmarks/ApplicationWorkflows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ 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<string> _manyCalendars = GetIcalStrings();

private static List<string> 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"))
Expand Down Expand Up @@ -75,4 +75,4 @@ public List<Occurrence> ParallelDeserializeSequentialGatherEventsParallelGetOccu
.SelectMany(e => e.GetOccurrences(_searchStart, _searchEnd))
.ToList();
}
}
}
4 changes: 2 additions & 2 deletions Ical.Net.Benchmarks/CalDateTimePerfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -31,4 +31,4 @@ public class CalDateTimePerfTests

[Benchmark]
public CalDateTime UtcToDifferentTzid() => UtcDateTime().ToTimeZone(_bTzid);
}
}
2 changes: 1 addition & 1 deletion Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net6.0;netcoreapp3.1;net48</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<LangVersion>latest</LangVersion>
</PropertyGroup>
Expand Down
63 changes: 46 additions & 17 deletions Ical.Net.Benchmarks/OccurencePerfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,73 @@ 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();
}

[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<RecurrencePattern>
{
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
Expand All @@ -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<RecurrencePattern> { rrule },
};
startTime += interval;
startTime = startTime.Add(Duration.FromTimeSpanExact(interval));
return e;
});

Expand Down Expand Up @@ -133,7 +162,7 @@ private static Calendar GetFourCalendarEventsWithCountRule()
End = new CalDateTime(startTime.AddMinutes(10), tzid),
RecurrenceRules = new List<RecurrencePattern> { rrule },
};
startTime += interval;
startTime = startTime.Add(interval);
return e;
});

Expand Down
179 changes: 31 additions & 148 deletions Ical.Net.Benchmarks/Runner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<ApplicationWorkflows>(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<OccurencePerfTests>(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<CalDateTimePerfTests>(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<SerializationPerfTests>(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<ThroughputTests>(config);
}
}

/// <summary>
Expand Down Expand Up @@ -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;
}
}
}