Skip to content

Commit

Permalink
Merge pull request #341 from rianjs/Performance
Browse files Browse the repository at this point in the history
Performance
  • Loading branch information
rianjs authored Nov 22, 2017
2 parents 1e4b238 + 625075a commit 9294ae2
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 25 deletions.
27 changes: 25 additions & 2 deletions net-core/Ical.Net.CoreUnitTests/ComponentTest.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using Ical.Net.CalendarComponents;
using Ical.Net.DataTypes;
using NUnit.Framework;

namespace Ical.Net.CoreUnitTests
{
[TestFixture]
public class ComponentTest
{
[Test, Category("Component")]
[Test, Category("Components")]
public void UniqueComponent1()
{
var iCal = new Calendar();
Expand All @@ -16,5 +17,27 @@ public void UniqueComponent1()
Assert.IsNull(evt.Created); // We don't want this to be set automatically
Assert.IsNotNull(evt.DtStamp);
}

[Test, Category("Components")]
public void ChangeCalDateTimeValue()
{
var e = new CalendarEvent
{
Start = new CalDateTime(2017, 11, 22, 11, 00, 01),
End = new CalDateTime(2017, 11, 22, 11, 30, 01),
};

var firstStartAsUtc = e.Start.AsUtc;
var firstEndAsUtc = e.End.AsUtc;

e.Start.Value = new DateTime(2017, 11, 22, 11, 30, 01);
e.End.Value = new DateTime(2017, 11, 22, 12, 00, 01);

var secondStartAsUtc = e.Start.AsUtc;
var secondEndAsUtc = e.End.AsUtc;

Assert.AreNotEqual(firstStartAsUtc, secondStartAsUtc);
Assert.AreNotEqual(firstEndAsUtc, secondEndAsUtc);
}
}
}
27 changes: 25 additions & 2 deletions net-core/Ical.Net.FrameworkUnitTests/ComponentTest.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using Ical.Net.CalendarComponents;
using Ical.Net.DataTypes;
using NUnit.Framework;

namespace Ical.Net.FrameworkUnitTests
{
[TestFixture]
public class ComponentTest
{
[Test, Category("Component")]
[Test, Category("Components")]
public void UniqueComponent1()
{
var iCal = new Calendar();
Expand All @@ -16,5 +17,27 @@ public void UniqueComponent1()
Assert.IsNull(evt.Created); // We don't want this to be set automatically
Assert.IsNotNull(evt.DtStamp);
}

[Test, Category("Components")]
public void ChangeCalDateTimeValue()
{
var e = new CalendarEvent
{
Start = new CalDateTime(2017, 11, 22, 11, 00, 01),
End = new CalDateTime(2017, 11, 22, 11, 30, 01),
};

var firstStartAsUtc = e.Start.AsUtc;
var firstEndAsUtc = e.End.AsUtc;

e.Start.Value = new DateTime(2017, 11, 22, 11, 30, 01);
e.End.Value = new DateTime(2017, 11, 22, 12, 00, 01);

var secondStartAsUtc = e.Start.AsUtc;
var secondEndAsUtc = e.End.AsUtc;

Assert.AreNotEqual(firstStartAsUtc, secondStartAsUtc);
Assert.AreNotEqual(firstEndAsUtc, secondEndAsUtc);
}
}
}
2 changes: 1 addition & 1 deletion net-core/Ical.Net.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Ical.Net</id>
<version>4.0.3</version>
<version>4.0.4</version>
<title>Ical.Net</title>
<authors>Rian Stockbower, Douglas Day</authors>
<owners>Rian Stockbower</owners>
Expand Down
14 changes: 14 additions & 0 deletions net-core/Ical.Net.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ical.Net.CoreUnitTests", "I
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ical.Net.FrameworkUnitTests", "Ical.Net.FrameworkUnitTests\Ical.Net.FrameworkUnitTests.csproj", "{90400333-D639-4303-B8F4-DBE0DA5D379D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PerfTests", "PerfTests\PerfTests.csproj", "{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -55,6 +57,18 @@ Global
{90400333-D639-4303-B8F4-DBE0DA5D379D}.Release|x64.Build.0 = Release|Any CPU
{90400333-D639-4303-B8F4-DBE0DA5D379D}.Release|x86.ActiveCfg = Release|Any CPU
{90400333-D639-4303-B8F4-DBE0DA5D379D}.Release|x86.Build.0 = Release|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Debug|x64.ActiveCfg = Debug|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Debug|x64.Build.0 = Debug|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Debug|x86.ActiveCfg = Debug|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Debug|x86.Build.0 = Debug|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Release|Any CPU.Build.0 = Release|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Release|x64.ActiveCfg = Release|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Release|x64.Build.0 = Release|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Release|x86.ActiveCfg = Release|Any CPU
{A65F31A8-28ED-4B4C-B314-7E8D06F7FD2D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
55 changes: 35 additions & 20 deletions net-core/Ical.Net/DataTypes/CalDateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public sealed class CalDateTime : EncodableDataType, IDateTime

public static CalDateTime Today => new CalDateTime(DateTime.Today);

private DateTime _value;
private bool _hasDate;
private bool _hasTime;

Expand Down Expand Up @@ -228,43 +227,57 @@ public DateTime AsSystemLocal
}
}

private DateTime _asUtc = DateTime.MinValue;
/// <summary>
/// Returns a representation of the DateTime in Coordinated Universal Time (UTC)
/// </summary>
public DateTime AsUtc
{
get
{
// In order of weighting:
// 1) Specified TzId
// 2) Value having a DateTimeKind.Utc

if (!string.IsNullOrWhiteSpace(TzId))
if (_asUtc == DateTime.MinValue)
{
var asLocal = DateUtil.ToZonedDateTimeLeniently(Value, TzId);
return asLocal.ToDateTimeUtc();
// In order of weighting:
// 1) Specified TzId
// 2) Value having a DateTimeKind.Utc
// 3) Use the OS's time zone

if (!string.IsNullOrWhiteSpace(TzId))
{
var asLocal = DateUtil.ToZonedDateTimeLeniently(Value, TzId);
_asUtc = asLocal.ToDateTimeUtc();
}
else if(IsUtc || Value.Kind == DateTimeKind.Utc)
{
_asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Utc);
}
else
{
_asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Local).ToUniversalTime();
}
}
return _asUtc;
}
}

if (IsUtc || Value.Kind == DateTimeKind.Utc)
private DateTime _value;
public DateTime Value
{
get => _value;
set
{
if (_value == value && _value.Kind == value.Kind)
{
return DateTime.SpecifyKind(_value, DateTimeKind.Utc);
return;
}

// Fall back to the OS conversion
return DateTime.SpecifyKind(Value, DateTimeKind.Local).ToUniversalTime();
_asUtc = DateTime.MinValue;
_value = value;
}
}

public bool IsUtc => _value.Kind == DateTimeKind.Utc;

public string TimeZoneName => TzId;

public DateTime Value
{
get => _value;
set => _value = value;
}

public bool HasDate
{
get => _hasDate;
Expand Down Expand Up @@ -320,6 +333,8 @@ public string TzId
}
}

public string TimeZoneName => TzId;

public int Year => Value.Year;

public int Month => Value.Month;
Expand Down
8 changes: 8 additions & 0 deletions net-core/PerfTests/CalDateTimePerf.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace PerfTests
{
class CalDateTimePerf
{


}
}
107 changes: 107 additions & 0 deletions net-core/PerfTests/OccurencePerfTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using Ical.Net;
using Ical.Net.CalendarComponents;
using Ical.Net.DataTypes;

namespace PerfTests
{
public class OccurencePerfTests
{
[Benchmark]
public void TenYears()
{
const string tzid = "Eastern Standard Time";
var startDt = DateTime.Now;
var endDt = startDt.AddHours(1);

var start = new CalDateTime(startDt, tzid);
var end = new CalDateTime(endDt, tzid);

var rrule = new RecurrencePattern(FrequencyType.Daily, 1)
{
Until = startDt.AddYears(10),
};

var e = new CalendarEvent
{
Start = start,
End = end,
RecurrenceRules = new List<RecurrencePattern> { rrule },
};

var searchStart = startDt.AddYears(-1);
var searchEnd = startDt.AddYears(11);
var occurrences = e.GetOccurrences(searchStart, searchEnd);
}

[Benchmark]
public void GetOccurrencesWithCalendarWithMultipleEvents()
{
var calendar = GetManyCalendarEvents();
var searchStart = calendar.Events.First().DtStart.AddYears(-1);
var searchEnd = calendar.Events.Last().DtStart.AddYears(1);
var occurences = calendar.GetOccurrences(searchStart, searchEnd);
}

[Benchmark]
public void MultipleEventOccurrences()
{
var calendar = GetManyCalendarEvents();
var searchStart = calendar.Events.First().DtStart.AddYears(-1);
var searchEnd = calendar.Events.Last().DtStart.AddYears(1);
var eventOccurrences = calendar.Events
.SelectMany(e => e.GetOccurrences(searchStart, searchEnd))
.ToList();
}

[Benchmark]
public void MultipleEventOccurrencesAsParallel()
{
var calendar = GetManyCalendarEvents();
var searchStart = calendar.Events.First().DtStart.AddYears(-1);
var searchEnd = calendar.Events.Last().DtStart.AddYears(1).AddDays(10);
var eventOccurrences = calendar.Events
.AsParallel()
.SelectMany(e => e.GetOccurrences(searchStart, searchEnd))
.ToList();
}

private Calendar GetManyCalendarEvents()
{
const string tzid = "America/New_York";
const int limit = 10;
var list = new List<CalendarEvent>(limit);

var startTime = DateTime.Now.AddDays(-1);
var interval = TimeSpan.FromDays(1);

for (var i = 0; i < limit; i++)
{
var rrule = new RecurrencePattern(FrequencyType.Hourly, 1)
{
Until = startTime.AddYears(1),
};

var e = new CalendarEvent
{
Start = new CalDateTime(startTime.AddMinutes(5), tzid),
End = new CalDateTime(startTime.AddMinutes(10), tzid),
RecurrenceRules = new List<RecurrencePattern> {rrule},
};
list.Add(e);

startTime += interval;
}

var c = new Calendar();
foreach (var e in list)
{
c.Events.Add(e);
}
return c;
}
}
}
16 changes: 16 additions & 0 deletions net-core/PerfTests/PerfTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet.Core" Version="0.10.10" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Ical.Net\Ical.Net.csproj" />
</ItemGroup>

</Project>
13 changes: 13 additions & 0 deletions net-core/PerfTests/Runner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.InProcess;

namespace PerfTests
{
public class Runner
{
static void Main(string[] args)
{
BenchmarkRunnerCore.Run(BenchmarkConverter.TypeToBenchmarks(typeof(OccurencePerfTests)), t => InProcessToolchain.Instance);
}
}
}
1 change: 1 addition & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
A listing of what each [Nuget package](https://www.nuget.org/packages/Ical.Net) version represents.

### v4
* 4.0.4 [PR 341](https://github.com/rianjs/ical.net/pull/341). Cache the UTC representation for `CalDateTime`s. This results in a 12-16% reduction in unit test runtime.
* 4.0.3 [#337](https://github.com/rianjs/ical.net/issues/337). Fixed a bug in `SimpleDeserializer` where tab characters (`\t`) were excluded from regex match.
* 4.0.1 [#335](https://github.com/rianjs/ical.net/issues/335). Technically this should be 5.0, but given 4.0.0 only has 24 downloads, I've just done a minor version bump.
* Moved everything from Ical.Net.Collections into the Ical.Net assembly.
Expand Down

0 comments on commit 9294ae2

Please sign in to comment.