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

Combine ExecutionContext/CapturedContext fields in ManualResetValueTaskSourceCore #82181

Merged
merged 3 commits into from
Mar 2, 2023

Conversation

stephentoub
Copy link
Member

There's currently an _executionContext field and a _capturedContext field. Both of these are for non-fast-path. This not only means the struct is larger than is necessary for the common case, it also means when we reset the instance between operations we need to clear an extra field, and it means we have an extra branch on some paths to check both fields.

We can instead combine them into a single field. We allocate an extra tuple object if there's both an ExecutionContext and a scheduler, but this is exceedingly rare: when used as part of awaits, there will never be an ExecutionContext, so this will only happen in situations where someone is directly using the awaiter's OnCompleted (not UnsafeOnCompleted) method and there's a scheduler and ConfigureAwait(false) wasn't used. Such a situation not only is rare, it also already has additional overheads.

This also cleans up a bit about how exceptions are handled and moves more logic out of the generic type to avoid code bloat with generic instantiations.

…skSourceCore

There's currently an _executionContext field and a _capturedContext field.  Both of these are for non-fast-path.  This not only means the struct is larger than is necessary for the common case, it also means when we reset the instance between operations we need to clear an extra field, and it means we have an extra branch on some paths to check both fields.

We can instead combine them into a single field.  We allocate an extra tuple object if there's both an ExecutionContext and a scheduler, but this is exceedingly rare: when used as part of awaits, there will never be an ExecutionContext, so this will only happen in situations where someone is directly using the awaiter's OnCompleted (not UnsafeOnCompleted) method and there's a scheduler and ConfigureAwait(false) wasn't used.  Such a situation not only is rare, it also already has additional overheads.

This also cleans up a bit about how exceptions are handled and moves more logic out of the generic type to avoid code bloat with generic instantiations.
@stephentoub stephentoub added this to the 8.0.0 milestone Feb 15, 2023
@ghost ghost assigned stephentoub Feb 15, 2023
@ghost
Copy link

ghost commented Feb 15, 2023

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

Issue Details

There's currently an _executionContext field and a _capturedContext field. Both of these are for non-fast-path. This not only means the struct is larger than is necessary for the common case, it also means when we reset the instance between operations we need to clear an extra field, and it means we have an extra branch on some paths to check both fields.

We can instead combine them into a single field. We allocate an extra tuple object if there's both an ExecutionContext and a scheduler, but this is exceedingly rare: when used as part of awaits, there will never be an ExecutionContext, so this will only happen in situations where someone is directly using the awaiter's OnCompleted (not UnsafeOnCompleted) method and there's a scheduler and ConfigureAwait(false) wasn't used. Such a situation not only is rare, it also already has additional overheads.

This also cleans up a bit about how exceptions are handled and moves more logic out of the generic type to avoid code bloat with generic instantiations.

Author: stephentoub
Assignees: -
Labels:

area-System.Threading.Tasks, tenet-performance

Milestone: 8.0.0

@davidfowl
Copy link
Member

Very similar to the change I made here. Nice!


if (continuation is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuation);
Copy link
Member

Choose a reason for hiding this comment

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

This is because of the generic context, right?

Should we add a comment to such helpers so future refactorings don't accidentally regress things?

Copy link
Member Author

Choose a reason for hiding this comment

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

We use these ThrowHelpers throughout corelib in literally thousands of places. I wouldn't want to comment each usage as to why it's being used. And the ThrowHelper class itself has a large comment about how it's there to reduce code size.

Copy link
Member

@tannergooding tannergooding Mar 2, 2023

Choose a reason for hiding this comment

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

I meant specifically this vs ArgumentNullException.ThrowIfNull(continuation)

That is, using throw helpers is fine/expected. But using this manual check + throw helper is "unexpected" vs using the new public API we expose that specifically does it. Not everyone may be aware its done due to the generic context and a general refactoring or analyzer might flag it as something to fixup.

Copy link
Member Author

@stephentoub stephentoub Mar 2, 2023

Choose a reason for hiding this comment

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

Sure, but that goes for 456 occurrences of ThrowHelper.ThrowArgumentNullException(ExceptionArgument.whatever) elsewhere in src\libraries\System.Private.CoreLib, too.

@stephentoub
Copy link
Member Author

Thanks for reviewing.

@stephentoub stephentoub merged commit 4da32cd into dotnet:main Mar 2, 2023
@stephentoub stephentoub deleted the mrvtscoptimization branch March 2, 2023 16:41
@ghost ghost locked as resolved and limited conversation to collaborators Apr 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants