codecadwallader / codemaid

CodeMaid is an open source Visual Studio extension to cleanup and simplify our C#, C++, F#, VB, PHP, PowerShell, JSON, XAML, XML, ASP, HTML, CSS, LESS, SCSS, JavaScript and TypeScript coding.
http://www.codemaid.net
GNU Lesser General Public License v3.0
1.88k stars 353 forks source link

Remove unnecessary async and await code #989

Open heku opened 1 year ago

heku commented 1 year ago

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:

  1. 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
  2. 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 -
codecadwallader commented 1 year ago

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?

heku commented 1 year ago

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