Introduction
Using async-await is part and parcel of a .NET developer life. We love to use methods with the “Async” suffix. Async-await is indeed a powerful mechanism that can improve the software scalability. Thanks to the async-await we are able to instruct the .NET runtime to return the thread to the thread pool instead of blocking the thread when it comes to the IO operation.
You might have heard the “async eliding” term before. Long story short it means to use the “await” keyword at the very top of the async-chain invocations. The question is why on earth would I use that? What are the benefits and drawbacks of the approach? In this post I will show you what are the benefits of the async eliding approach.
Async eliding
Let’s consider the following example. We are told to create an application. After long hours, days or even weeks of designing the solution architecture you ultimately decided to have three layers in your application: Layer1, Layer2 and Layer3. The Layer3 is the one that communicates with e.g. database. Layer1 and Layer2 are just a middleware. Invocation chain is pretty straightforward and looks like this:
This approach is the common one and is called “async all the way down”. However, if the Layer1 and Layer2 are just a middleware and do nothing but invoking next layers we can consider async eliding. Async eliding approach looks like this:
We get rid of two awaits. Thanks to that the Layer1.GimmeDataAsync and Layer2.GimmeDataAsync methods are not marked as async. They just return the Task that represents the DB operation. Having methods like that the compiler doesn’t generate the async state machine for these two methods. Alright, but what are the benefits?
Benefits – allocation and performance
There are two main benefits related to the async eliding approach: allocation and performance. To measure the benefits I’ve used the BenchmarkDotNet library. Let’s take a look at the benchmark results:
The benchmark shows that the execution time is almost 2 times faster for the async eliding. The allocation is also much better as it allocated 144 bytes less. But don’t be fooled by the benchmark results. In the part 2 I will discuss the drawbacks of the async eliding approach, as they unfortunately exist and they might overweight the benefits (actually they do).
As always I’ve prepared an example and you can find it on my github, project: async-eliding-part-1, test project: async-eliding-part-1-tests, benchmark project: async-eliding-part-1-benchmark. The async-eliding-part-1 consists of the structures (Layer classes) that were mentioned before. The async-eliding-part-1-tests consists of two unit tests that ensure the same results for both approaches. The async-eliding-part-1-benchmark consists of the benchmark methods. To run the benchmark you should just open terminal inside the async-eliding-part-1-benchmark project and run: dotnet run -c Release
Summary
Despite the faster execution and less allocation the recommended way of async-await usage is to start with the classic async-await approach (without eliding). The eliding itself might be useful if you consider some extra optimization on the application’s hot paths.
Have a nice day, bye!
Be First to Comment