-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Reduce RendererSynchronizationContext overheads #48720
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
Conversation
stephentoub
commented
Jun 12, 2023
- Use AsyncTaskMethodBuilder instead of TaskCompletionSource for one allocation instead of two
- For InvokeAsync with synchronous callbacks, reuse the Task returned from InvokeAsync as the placeholder in the task queue
- Use awaits instead of ContinueWith
Method | Mode | Mean | Error | StdDev | Median | Allocated |
---|---|---|---|---|---|---|
InvokeAsyncReady | 0 | 89.89 us | 0.694 us | 0.580 us | 89.85 us | 187.5 KB |
InvokeAsyncReady | 1 | 57.25 us | 0.124 us | 0.104 us | 57.27 us | 70.3 KB |
InvokeAsyncBusy | 0 | 1,003.33 us | 13.244 us | 11.059 us | 1,000.87 us | 650.73 KB |
InvokeAsyncBusy | 1 | 207.76 us | 1.004 us | 0.784 us | 207.50 us | 351.75 KB |
PostSerial | 0 | 518.09 us | 29.269 us | 86.300 us | 478.09 us | 156.92 KB |
PostSerial | 1 | 454.82 us | 2.635 us | 2.058 us | 454.55 us | 141.15 KB |
PostConcurrent | 0 | 4,303.06 us | 95.365 us | 281.185 us | 4,320.83 us | 1562.86 KB |
PostConcurrent | 1 | 4,037.39 us | 80.344 us | 210.245 us | 4,077.91 us | 1406.61 KB |
- Use AsyncTaskMethodBuilder instead of TaskCompletionSource for one allocation instead of two - For InvokeAsync with synchronous callbacks, reuse the Task returned from InvokeAsync as the placeholder in the task queue - Use awaits instead of ContinueWith
Thanks @stephentoub! From the numbers this looks very beneficial so hopefully we can get it included in preview 6. I will admit that, even though I have read the diff and it looks completely plausible, sync contexts are hard to reason about (you have to anticipate all the threading issues) and this is a pretty intricate diff, so unless I spend a couple of days reinventing this completely I can't be 100% certain I've spotted any risks or potential compat issues. Based on your expertise I'm as confident as is likely possible though.
That comment does raise the question of if there are some app code patterns people might be using already that might work in .NET 7 (even if perilous) but deadlock after this PR. My understanding of sync-over-async in a sync context is that it would deadlock if the thing that triggers task completion itself depends on acquiring the sync context, but would not deadlock if task completion can be triggered without use of the sync context. Is that the sort of thing you mean? Does this PR change the set of sync-over-async cases that would work without deadlock? Pinging @javiercn in case there's anything he wants us to go back and check in more detail, but besides that I'd be happy to proceed. So, thanks for improving this! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I concur with @SteveSandersonMS here, everything looks good from my point of view, and I trust you more than I trust myself in this area to be correct.
Given the amount of test coverage that we have, not only on unit tests but also on E2E tests, I think we are good to go here. We have time to get feedback from previews and adjust if necessary.
Looks like this PR hasn't been active for some time and the codebase could have been changed in the meantime. |
I've not thought of any, but I can't 100% rule it out; as you say, these things are complicated. Certainly the obvious cases would deadlock with both old and proposed. |