Adding benchmarks comparing different methods of parsing a date/time in the ISO 8601 format:
- ParseExact with a custom format
- ParseExact with a standard format
- Pre-compiled regex with date/time creation
- Pre-compiled regex just validating the input string's format
Bottom line:
- Regex is much faster to check the format without creating a date/time and does not allocate
- Regex is slower to parse and build a date/time, allocates significantly more (and requires more code...)
- Standard formats seem to allocate less
// * Summary *
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19044.2965/21H2/November2021Update)
12th Gen Intel Core i9-12900H, 1 CPU, 20 logical and 14 physical cores
.NET SDK=7.0.302
[Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
.NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
Job=.NET 7.0 Runtime=.NET 7.0
Method |
Loops |
Mean |
Error |
StdDev |
Ratio |
RatioSD |
Gen0 |
Allocated |
ParseExactCustomFormat |
10 |
277.65 ns |
3.148 ns |
2.791 ns |
1.00 |
0.00 |
0.0114 |
144 B |
ParseExactStandardFormat |
10 |
254.07 ns |
0.490 ns |
0.435 ns |
0.92 |
0.01 |
0.0095 |
120 B |
Regex |
10 |
423.21 ns |
4.026 ns |
3.766 ns |
1.52 |
0.02 |
0.0858 |
1080 B |
RegexIsMatch |
10 |
62.19 ns |
0.322 ns |
0.302 ns |
0.22 |
0.00 |
- |
- |
|
|
|
|
|
|
|
|
|
ParseExactCustomFormat |
100 |
255.08 ns |
1.176 ns |
1.042 ns |
1.00 |
0.00 |
0.0114 |
144 B |
ParseExactStandardFormat |
100 |
255.18 ns |
1.358 ns |
1.271 ns |
1.00 |
0.01 |
0.0095 |
120 B |
Regex |
100 |
427.15 ns |
8.149 ns |
7.224 ns |
1.67 |
0.03 |
0.0858 |
1080 B |
RegexIsMatch |
100 |
88.23 ns |
1.920 ns |
5.508 ns |
0.32 |
0.05 |
- |
- |
|
|
|
|
|
|
|
|
|
ParseExactCustomFormat |
1000 |
255.65 ns |
1.409 ns |
1.249 ns |
1.00 |
0.00 |
0.0114 |
144 B |
ParseExactStandardFormat |
1000 |
255.34 ns |
0.689 ns |
0.611 ns |
1.00 |
0.00 |
0.0095 |
120 B |
Regex |
1000 |
437.88 ns |
4.192 ns |
3.273 ns |
1.71 |
0.02 |
0.0858 |
1080 B |
RegexIsMatch |
1000 |
62.69 ns |
0.744 ns |
0.696 ns |
0.25 |
0.00 |
- |
- |
/// <summary>
/// Compares different methods of parsing a date/time in the ISO 8601 format.
/// </summary>
[SimpleJob(RuntimeMoniker.Net70)]
[MemoryDiagnoser]
public partial class DateTimeParseExactVsRegex
{
[Params(10, 100, 1_000)]
public int Loops { get; set; }
/// <summary>
/// Parses the input string by using <see cref="DateTime.ParseExact"/>
/// with a custom format.
/// </summary>
[Benchmark(Baseline = true)]
public void ParseExactCustomFormat()
{
DateTime.ParseExact(_serializedDateTime, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
}
private const string _serializedDateTime = "2002-08-11T10:11:12.0000000Z";
/// <summary>
/// Parses the input string by using <see cref="DateTime.ParseExact"/>
/// with a standard format.
/// </summary>
[Benchmark]
public void ParseExactStandardFormat()
{
DateTime.ParseExact(_serializedDateTime, "O",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
}
/// <summary>
/// Parses the input string by using a <see cref="Regex"/>.
/// </summary>
[Benchmark]
public void Regex()
{
Match match = GetRegex().Match(_serializedDateTime);
if (!match.Success)
{
throw new NotImplementedException();
}
int GetInt(string groupName)
{
ReadOnlySpan<char> yearAsString = match.Groups[groupName].ValueSpan;
return int.Parse(yearAsString);
}
int year = GetInt("year"), month = GetInt("month"), day = GetInt("day");
int hour = GetInt("hour"), minute = GetInt("minute"), second = GetInt("second");
int subSecond = GetInt("subsecond");
DateTime _ = new DateTime(year, month, day, hour, minute, second).AddTicks(subSecond);
}
[GeneratedRegex(@"^(?<year>\d{4})\-(?<month>\d{2})\-(?<day>\d{2})T(?<hour>\d{2})\:(?<minute>\d{2})\:(?<second>\d{2})\.(?<subsecond>\d{7})Z$")]
private static partial System.Text.RegularExpressions.Regex GetRegex();
/// <summary>
/// Detects whether the input string matches the date/time format by using a <see cref="Regex"/>.
/// </summary>
[Benchmark]
public void RegexIsMatch() => GetRegex().IsMatch(_serializedDateTime);
}