Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize DateOnly, TimeOnly, ISOWeek #111244

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

pentp
Copy link
Contributor

@pentp pentp commented Jan 9, 2025

Small optimizations and cleanup.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jan 9, 2025
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-datetime
See info in area-owners.md if you want to be subscribed.

@@ -1086,7 +1086,7 @@ private static ulong DateToTicks(int year, int month, int day)
ThrowHelper.ThrowArgumentOutOfRange_BadYearMonthDay();
}

ReadOnlySpan<uint> days = IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
ReadOnlySpan<uint> days = month > 1 && IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
Copy link
Member

@tarekgh tarekgh Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

month > 1

what is the benefit of this check?

Copy link
Member

@tarekgh tarekgh Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand it is to avoid calling IsLeapYear. But wouldn't this check add cost when having other values of month?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main benefit is actually for the quite common case where a DateTime is constructed with const month=1,day=1 - this change allows skipping not only the leap year check but also the DaysToMonth lookup and check. The overhead for non-const cases is just one branch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the quite common case where a DateTime is constructed with const month=1,day=1

I am not sure this is true. I rarely see DateTime constructed with that.


/// <summary>
/// Gets the minute component of the time represented by this instance.
/// </summary>
public int Minute => new TimeSpan(_ticks).Minutes;
public int Minute => (int)((uint)(_ticks / TimeSpan.TicksPerMinute) % 60);
Copy link
Member

@tarekgh tarekgh Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

60

Will be good to use the constants instead (like TimeSpan.MinutesPerHour). This comment applies to other places using numbers here.


/// <summary>
/// Gets the second component of the time represented by this instance.
/// </summary>
public int Second => new TimeSpan(_ticks).Seconds;
public int Second => (int)((uint)(_ticks / TimeSpan.TicksPerSecond) % 60);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public int Second => (int)((uint)(_ticks / TimeSpan.TicksPerSecond) % 60);

I am wondering in general if these are worth optimizing? Having the calculation in one place in TimeSpan is better IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TimeSpan supports large ranges, including negative values. TimeOnly values are more constrained.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I understand that. I was wondering if it is worth the minor perf gain to repeat the calculation here.

public static TimeSpan operator -(TimeOnly t1, TimeOnly t2)
{
long diff = (long)(t1._ticks - t2._ticks);
return new TimeSpan(diff + ((diff >> 63) & TimeSpan.TicksPerDay));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return new TimeSpan(diff + ((diff >> 63) & TimeSpan.TicksPerDay));

will be good to add a comment telling what this operation does. It is not obvious what it does anymore.

@tarekgh
Copy link
Member

tarekgh commented Jan 9, 2025

Any benchmark numbers for the optimized parts?

@xtqqczze
Copy link
Contributor

xtqqczze commented Jan 9, 2025

Maybe rename the method UnsafeCreate to CreateUnchecked as part of these changes, since the method does not involve the use of unsafe context or types like Unsafe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.DateTime community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants