-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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 an async method that ends "return await e" to be non-async "return e" #1981
Comments
This is basically the tail-call optimization for async methods. |
Won't this change the semantics when Though I guess something like this would work: try
{
return e;
}
catch (Exception ex)
{
return Task.FromException(ex);
} |
@svick yes it would change the call stack for exceptions of this nature. |
@svick Given the proposed constraints, the callers will just bubble the exception up, so the end result is the same other than the fact that the call stack is different. |
@gafter I don't understand. Consider the following code: Task<int> E()
{
throw new Exception();
}
async Task<int> ReturnAwaitE()
{
return await E();
}
Task<int> ReturnE()
{
return E();
}
…
var t = ReturnAwaitE();
await t.ContinueWith(_ => {}); // await t, but don't throw if it's faulted
Console.WriteLine(t.Exception); This code writes out the exception, but if I replace Doesn't |
@svick this feature would not change exception semantics in that way. To do so would turn an optimization into a breaking change. The implementation of this would have to take exceptions into account when generating the code. Even in this case though the compiler can generate much more efficient code than the state machine that is generated today: Task<int> ReturnE()
{
try {
return E();
} catch (Exception e) {
return Task<int>.FromException(e);
}
} |
👍 on the idea. |
@jaredpar Yeah, that breaking change was what I was afraid of. It wasn't clear to me from the initial description that this was what the optimization would do. |
@pharring if there is another |
BTW, |
@pharring, it was in 4.5, it isn't anymore. |
ps Note, though, that the try/catch will also need to specialize catching an OperationCanceledException and returning a canceled task for that case, as that's what the async machinery does today. |
pps And for it to actually remain semantically equivalent, it'd have to appropriately handle ExecutionContext. In particular, inside of an async method, changes made to ExecutionContext (e.g. putting something into the logical call context) will not be visible to the caller of the async method upon the return of the synchronous invocation. |
This was proposed a couple of times already |
👍 on this optimization! |
Is the C# team willing to take a dependency on the internals of TaskAwaiter here? Previously, that class was opaque and there wasn't any compiler knowledge about it built-in. From the languages point of view that class could do anything at all when awaited. |
Won't this remove the method in question from an exception stack trace? Is there a way that this optimisation could be achieved whilst still preserving the stack trace? |
this optimization would improve the performance of an async method whose only await appears at the end. Not when producing debug code and not when nested in a try block.
@jaredpar @VSadov
The text was updated successfully, but these errors were encountered: