Skip to content

Commit 108eb66

Browse files
authored
make DateTime.UtcNow 5% faster to minimize the leap second performance impact (dotnet/coreclr#26046)
* make few methods used by DateTime.UtcNow inlinable to minimize the leap second performance regression impact, related to dotnet/coreclr#25728 * apply suggested micro-optimizations Commit migrated from dotnet/coreclr@4450e5c
1 parent a26f5cb commit 108eb66

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

src/libraries/System.Private.CoreLib/src/System/DateTime.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -641,32 +641,38 @@ public int CompareTo(DateTime value)
641641

642642
// Returns the tick count corresponding to the given year, month, and day.
643643
// Will check the if the parameters are valid.
644+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
644645
private static long DateToTicks(int year, int month, int day)
645646
{
646-
if (year >= 1 && year <= 9999 && month >= 1 && month <= 12)
647+
if (year < 1 || year > 9999 || month < 1 || month > 12 || day < 1)
647648
{
648-
int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365;
649-
if (day >= 1 && day <= days[month] - days[month - 1])
650-
{
651-
int y = year - 1;
652-
int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
653-
return n * TicksPerDay;
654-
}
649+
ThrowHelper.ThrowArgumentOutOfRange_BadYearMonthDay();
650+
}
651+
652+
int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365;
653+
if (day > days[month] - days[month - 1])
654+
{
655+
ThrowHelper.ThrowArgumentOutOfRange_BadYearMonthDay();
655656
}
656-
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
657+
658+
int y = year - 1;
659+
int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
660+
return n * TicksPerDay;
657661
}
658662

659663
// Return the tick count corresponding to the given hour, minute, second.
660664
// Will check the if the parameters are valid.
665+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
661666
private static long TimeToTicks(int hour, int minute, int second)
662667
{
663668
//TimeSpan.TimeToTicks is a family access function which does no error checking, so
664669
//we need to put some error checking out here.
665-
if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60)
670+
if ((uint)hour >= 24 || (uint)minute >= 60 || (uint)second >= 60)
666671
{
667-
return (TimeSpan.TimeToTicks(hour, minute, second));
672+
ThrowHelper.ThrowArgumentOutOfRange_BadHourMinuteSecond();
668673
}
669-
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond);
674+
675+
return TimeSpan.TimeToTicks(hour, minute, second);
670676
}
671677

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

11941201
// Constructs a DateTime from a string. The string must specify a

src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,31 @@ internal static void ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count()
131131
ExceptionResource.ArgumentOutOfRange_Count);
132132
}
133133

134+
[DoesNotReturn]
135+
internal static void ThrowArgumentOutOfRange_Year()
136+
{
137+
throw GetArgumentOutOfRangeException(ExceptionArgument.year,
138+
ExceptionResource.ArgumentOutOfRange_Year);
139+
}
140+
141+
[DoesNotReturn]
142+
internal static void ThrowArgumentOutOfRange_BadYearMonthDay()
143+
{
144+
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
145+
}
146+
147+
[DoesNotReturn]
148+
internal static void ThrowArgumentOutOfRange_BadHourMinuteSecond()
149+
{
150+
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond);
151+
}
152+
153+
[DoesNotReturn]
154+
internal static void ThrowArgumentOutOfRange_TimeSpanTooLong()
155+
{
156+
throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
157+
}
158+
134159
[DoesNotReturn]
135160
internal static void ThrowWrongKeyTypeArgumentException<T>(T key, Type targetType)
136161
{
@@ -657,6 +682,8 @@ private static string GetArgumentName(ExceptionArgument argument)
657682
return "elementType";
658683
case ExceptionArgument.arrayIndex:
659684
return "arrayIndex";
685+
case ExceptionArgument.year:
686+
return "year";
660687
default:
661688
Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum.");
662689
return "";
@@ -687,6 +714,8 @@ private static string GetResourceString(ExceptionResource resource)
687714
return SR.ArgumentOutOfRange_IndexCountBuffer;
688715
case ExceptionResource.ArgumentOutOfRange_Count:
689716
return SR.ArgumentOutOfRange_Count;
717+
case ExceptionResource.ArgumentOutOfRange_Year:
718+
return SR.ArgumentOutOfRange_Year;
690719
case ExceptionResource.Arg_ArrayPlusOffTooSmall:
691720
return SR.Arg_ArrayPlusOffTooSmall;
692721
case ExceptionResource.NotSupported_ReadOnlyCollection:
@@ -901,6 +930,7 @@ internal enum ExceptionArgument
901930
endIndex,
902931
elementType,
903932
arrayIndex,
933+
year,
904934
}
905935

906936
//
@@ -912,6 +942,7 @@ internal enum ExceptionResource
912942
ArgumentOutOfRange_IndexCount,
913943
ArgumentOutOfRange_IndexCountBuffer,
914944
ArgumentOutOfRange_Count,
945+
ArgumentOutOfRange_Year,
915946
Arg_ArrayPlusOffTooSmall,
916947
NotSupported_ReadOnlyCollection,
917948
Arg_RankMultiDimNotSupported,

src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Globalization;
6+
using System.Runtime.CompilerServices;
67

78
namespace System
89
{
@@ -288,13 +289,14 @@ public static TimeSpan FromTicks(long value)
288289
return new TimeSpan(value);
289290
}
290291

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

0 commit comments

Comments
 (0)