codemaid
codemaid copied to clipboard
Remove unnecessary async and await code
I'm going to pull a request to remove all unnecessary async and await code, i.e. update code as below
async Task Foo()
{
await Bar();
}
// to
Task Foo()
{
return Bar();
}
This will remove all unnecessary statemachine code generated by comipler, as a result:
- The dll size will be reduced.
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2023/3/18 16:49 513024 SteveCadwallader.CodeMaid.VS2022.dll
-a--- 2023/3/18 16:49 489984 SteveCadwallader.CodeMaid.VS2022 - after.dll
- The performance should be better.
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class TaskVsAwait
{
[Benchmark]
public Task ReturnTask() => Task.CompletedTask;
[Benchmark]
public async Task AwaitTask() => await Task.CompletedTask.ConfigureAwait(false);
}
| Method | Job | Runtime | Mean | Error | StdDev | Allocated |
|---|---|---|---|---|---|---|
| ReturnTask | DefaultJob | .NET 6.0 | 0.1301 ns | 0.0277 ns | 0.0350 ns | - |
| AwaitTask | DefaultJob | .NET 6.0 | 15.2448 ns | 0.3342 ns | 0.4461 ns | - |
| ReturnTask | .NET Framework 4.8 | .NET Framework 4.8 | 3.1150 ns | 0.0890 ns | 0.1126 ns | - |
| AwaitTask | .NET Framework 4.8 | .NET Framework 4.8 | 63.8919 ns | 1.3035 ns | 1.8694 ns | - |
This is very interesting, thank you heku. I've always preferred having the await call directly in the method because I feel like the call stack is cleaner for debugging purposes. I really appreciate that you provided some benchmarks to justify the difference. When an error is thrown, what is the difference on an example call stack?
Yes, you are right, call stacks are different, each await keeps current call site in the stack, while, return task directly hides it from call stack. Personally, I always prefer return task than await, I think the benefits on performance and code size more than debug experience. But given the difference, it's up to you to decide whether accept these changes, I have no problem.
private static async Task Main(string[] args)
{
Console.WriteLine(RuntimeInformation.FrameworkDescription);
try
{
await ReturnTask(); // line 15
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
try
{
await AwaitTask(); // line 25
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
private static Task ReturnTask() => ErrorTask(); // line 34
private static async Task AwaitTask() => await ErrorTask(); // line 36
private static Task ErrorTask() => Task.FromException(new Exception("error message")); // line 38
.NET Framework 4.8.9139.0
error message
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ConsoleNET48.Program.<Main>d__0.MoveNext() in C:\coderepos\Learn.NET\ConsoleNET48\Program.cs:line 15
error message
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ConsoleNET48.Program.<AwaitTask>d__2.MoveNext() in C:\coderepos\Learn.NET\ConsoleNET48\Program.cs:line 36
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ConsoleNET48.Program.<Main>d__0.MoveNext() in C:\coderepos\Learn.NET\ConsoleNET48\Program.cs:line 25
.NET 6.0.15
error message
at ConsoleNET6.Program.Main(String[] args) in C:\coderepos\Learn.NET\ConsoleNET6\Program.cs:line 15
error message
at ConsoleNET6.Program.AwaitTask() in C:\coderepos\Learn.NET\ConsoleNET6\Program.cs:line 36
at ConsoleNET6.Program.Main(String[] args) in C:\coderepos\Learn.NET\ConsoleNET6\Program.cs:line 25