Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
make DateTime.UtcNow 5% faster to minimize the leap second performanc…
Browse files Browse the repository at this point in the history
…e impact (#26046)

* make few methods used by DateTime.UtcNow inlinable to minimize the leap second performance regression impact, related to #25728

* apply suggested micro-optimizations
  • Loading branch information
adamsitnik authored Aug 8, 2019
1 parent ef1b0f7 commit 4450e5c
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 15 deletions.
35 changes: 21 additions & 14 deletions src/System.Private.CoreLib/shared/System/DateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -641,32 +641,38 @@ public int CompareTo(DateTime value)

// Returns the tick count corresponding to the given year, month, and day.
// Will check the if the parameters are valid.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static long DateToTicks(int year, int month, int day)
{
if (year >= 1 && year <= 9999 && month >= 1 && month <= 12)
if (year < 1 || year > 9999 || month < 1 || month > 12 || day < 1)
{
int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365;
if (day >= 1 && day <= days[month] - days[month - 1])
{
int y = year - 1;
int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
return n * TicksPerDay;
}
ThrowHelper.ThrowArgumentOutOfRange_BadYearMonthDay();
}

int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365;
if (day > days[month] - days[month - 1])
{
ThrowHelper.ThrowArgumentOutOfRange_BadYearMonthDay();
}
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);

int y = year - 1;
int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
return n * TicksPerDay;
}

// Return the tick count corresponding to the given hour, minute, second.
// Will check the if the parameters are valid.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static long TimeToTicks(int hour, int minute, int second)
{
//TimeSpan.TimeToTicks is a family access function which does no error checking, so
//we need to put some error checking out here.
if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60)
if ((uint)hour >= 24 || (uint)minute >= 60 || (uint)second >= 60)
{
return (TimeSpan.TimeToTicks(hour, minute, second));
ThrowHelper.ThrowArgumentOutOfRange_BadHourMinuteSecond();
}
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond);

return TimeSpan.TimeToTicks(hour, minute, second);
}

// Returns the number of days in the month given by the year and
Expand Down Expand Up @@ -1182,13 +1188,14 @@ public int Year
// Checks whether a given year is a leap year. This method returns true if
// year is a leap year, or false if not.
//
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsLeapYear(int year)
{
if (year < 1 || year > 9999)
{
throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_Year);
ThrowHelper.ThrowArgumentOutOfRange_Year();
}
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
return (year & 3) == 0 && ((year & 15) == 0 || (year % 25) != 0);
}

// Constructs a DateTime from a string. The string must specify a
Expand Down
31 changes: 31 additions & 0 deletions src/System.Private.CoreLib/shared/System/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,31 @@ internal static void ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count()
ExceptionResource.ArgumentOutOfRange_Count);
}

[DoesNotReturn]
internal static void ThrowArgumentOutOfRange_Year()
{
throw GetArgumentOutOfRangeException(ExceptionArgument.year,
ExceptionResource.ArgumentOutOfRange_Year);
}

[DoesNotReturn]
internal static void ThrowArgumentOutOfRange_BadYearMonthDay()
{
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
}

[DoesNotReturn]
internal static void ThrowArgumentOutOfRange_BadHourMinuteSecond()
{
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond);
}

[DoesNotReturn]
internal static void ThrowArgumentOutOfRange_TimeSpanTooLong()
{
throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
}

[DoesNotReturn]
internal static void ThrowWrongKeyTypeArgumentException<T>(T key, Type targetType)
{
Expand Down Expand Up @@ -657,6 +682,8 @@ private static string GetArgumentName(ExceptionArgument argument)
return "elementType";
case ExceptionArgument.arrayIndex:
return "arrayIndex";
case ExceptionArgument.year:
return "year";
default:
Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum.");
return "";
Expand Down Expand Up @@ -687,6 +714,8 @@ private static string GetResourceString(ExceptionResource resource)
return SR.ArgumentOutOfRange_IndexCountBuffer;
case ExceptionResource.ArgumentOutOfRange_Count:
return SR.ArgumentOutOfRange_Count;
case ExceptionResource.ArgumentOutOfRange_Year:
return SR.ArgumentOutOfRange_Year;
case ExceptionResource.Arg_ArrayPlusOffTooSmall:
return SR.Arg_ArrayPlusOffTooSmall;
case ExceptionResource.NotSupported_ReadOnlyCollection:
Expand Down Expand Up @@ -901,6 +930,7 @@ internal enum ExceptionArgument
endIndex,
elementType,
arrayIndex,
year,
}

//
Expand All @@ -912,6 +942,7 @@ internal enum ExceptionResource
ArgumentOutOfRange_IndexCount,
ArgumentOutOfRange_IndexCountBuffer,
ArgumentOutOfRange_Count,
ArgumentOutOfRange_Year,
Arg_ArrayPlusOffTooSmall,
NotSupported_ReadOnlyCollection,
Arg_RankMultiDimNotSupported,
Expand Down
4 changes: 3 additions & 1 deletion src/System.Private.CoreLib/shared/System/TimeSpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Globalization;
using System.Runtime.CompilerServices;

namespace System
{
Expand Down Expand Up @@ -288,13 +289,14 @@ public static TimeSpan FromTicks(long value)
return new TimeSpan(value);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long TimeToTicks(int hour, int minute, int second)
{
// totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
// which is less than 2^44, meaning we won't overflow totalSeconds.
long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second;
if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds)
throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
return totalSeconds * TicksPerSecond;
}

Expand Down

0 comments on commit 4450e5c

Please sign in to comment.