Click here to monitor SSC
  • Av rating:
  • Total votes: 20
  • Total comments: 11
Jon Smith

The .NET 4.5 async/await feature in Promise and Practice

28 July 2014
Equivalence Classes & Cliques

The .NET 4.5 async/await feature provides an opportunity for improving the scalability and performance of applications, particularly where tasks are more effectively done in parallel. The question is: do the scalability gains come at a cost of slowing individual methods? In this article Jon Smith investigates this issue by conducting a side-by-side evaluation of the standard synchronous methods and the new async methods in real applications.

The performance of async/await in Entity Framework 6 and ASP.NET MVC5

No one wants to build a slow, unresponsive application, but it takes extra development time and effort to make applications run fast. Someone, therefore, has to make a decision on how much time and effort should be applied to maximizing performance. The release of the .NET 4.5 async/await feature has radically changed the effort/reward ratio of one small, but important part of the performance trade-off. It is now much easier to write asynchronous code in C#.

In this article I will explain what async/await can do to improve performance and scalability and will provide links to more documentation. I will then spend the bulk of the article going through my own experience of developing an open-source library which has normal, synchronous, and asynchronous versions of each method. I end by sharing with you the actual performance gains/losses I obtained in real applications using synchronous and asynchronous versions of each method and my views on where the new async/await  feature can be used with most gain.

The performance problems that async/await solve

The async/await feature solves three performance or scalability problems:

  1. They can make your application handle more users.
    Most multi-user applications, such as web sites, use a thread for each user session. There are a lot of threads in the thread pool, but if lot of users try to access the site then they can run out, which in turn leads to blocking. Also there is a memory cost for each thread started, see Using Asynchronous Methods in ASP.NET MVC 4
    If you have requests that access an external resource such as a database or a web API then async  frees up the thread while it is waiting. This means you will use fewer threads, and so avoid reaching the maximum number of threads so quickly and use less memory as well.
  2. You can process multiple I/O bound methods in parallel
    If, for example, you needed to access a number of remote services either in legacy systems or over the web and then combine their results to show the user. If this were done synchronously, the process would take the sum of the access times for each service. With the new async/await feature you can easily run them all in parallel and then it takes only as long at the longest access-time. See an example at Performing Multiple Operations in Parallel .
  3. You can make your interface more responsive to the user.
    If you have a request on a site that is slow you can make the site more responsive by starting the action asynchronously and returning to the user before the action has finished. There are some issues around handling errors if the asynchronous task fails, but for places where your application interfaces to an external system that is slow this can be worth the effort.

In reality you could have done all of these enhancements before the async/await feature came along using the previous .NET asynchronous methods. However these previous async implementations were complicated and not very obvious, so developing asynchronous code with these older methods was hard work. What the new async/await does is make writing asynchronous methods really simple using just a few lines of code. The only down side of this is there are a few subtleties you need to know about which I will point out later.

If you would like more information on async/await then here are two useful Microsoft links.

And on Simple-Talk

Other libraries updated to support async/await

I should also mention that many other system functions within .NET have now been updated to have async versions of their methods such as HttpClient with methods like GetStringAsync. Also a lot of other libraries and frameworks have also been updated such as Microsoft’s Entity Framework 6 (EF6), ASP.NET MVC4 and 5, SignalR 2 etc.

This sweeping change is important as it means that async can now be used throughout an application. In this article I will spend some time looking at using async/await in EF6 and MVC5 applications.

My experience of using async 

I was personally very pleased to see the new feature because I build data modelling and visualisation applications. These applications have three groups of features that particularly benefit from using asynchronous methods:

  • Complex database calls that take seconds, in a few cases minutes, to return.
  • Access to RESTful geocoding web services that are slow to respond.
  • Long-running modelling actions that need to run for some minutes.

I did my research and I have built an open-source project called GenericServices which contains both synchronous, i.e. normal, and async versions of each method so I am really interested in the performance difference.

A Database Example

Let us start with a simple example using EF6 with ASP.NET MVC5. Below are two methods that return a list of Posts in a fictitious blog site. The first is the normal, synchronous method and the second is the asynchronous version.

 

public static string GetNumPosts(SampleWebAppDb db)

{

    var numPosts = db.Posts.Count();

    return string.Format("The total number of Posts is {0}", numPosts);

}

private static async Task<string> GetNumPostsAsync(SampleWebAppDb db)

{

    var numPosts = await db.Posts.CountAsync();

    return string.Format("The total number of Posts is {0}", numPosts);

}

You’ll notice immediately how similar they are. I have highlighted the changes in the async version in yellow. The async version is not hard to write and it is easy to understand. Before async/await you would have needed two methods for the async version, linked by an event.

Here are the two different calls inside an ASP.NET MVC web application, again with the changes in the async version in highlighted in yellow.

 

public ActionResult NumPosts()

{

    using (var db = new SampleWebAppDb())

        //The object cast is to stop the View using the string as a view name

        return View((object)GetNumPosts(db));

}

 

public async Task<ActionResult> NumPosts()

{

    using (var db = new SampleWebAppDb())

        //The object cast is to stop the View using the string as a view name

        return View((object)await GetNumPostsAsync(db));

}

This shows how simple it is to implement async methods with async/await. Previously you would have needed two methods in MVC for handling async access, again linked by an event and a few other obscure methods. (See Dino Esposito’s article in 2012 on async in ASP.NET before .NET 4.5 https://www.simple-talk.com/dotnet/asp.net/asp.net-go-async-or-sink/).

A web service example

Although the interface with the database is a common use for asynchronous methods, the place where async/await will really shine is on slow web services. Here is an async example of an access to the BBC’s trial radio schedule service. See the full code at GitHub: JonPSmith/SampleMvcWebApp

public async Task<string> GetSchedule(DateTime day)

{

    var request = WebRequest.CreateHttp(FormUrl(day));

    var response = await request.GetResponseAsync();

    using (var stIn = new StreamReader(response.GetResponseStream()))

        return await stIn.ReadToEndAsync();

}

This service is fairly slow at around 500ms. so you gain significant scalability improvements by using an async task because it releases the thread for other sessions while it is waiting.

Things I learnt about async/await

Here are a few things I learnt alone the way which might help you get started.

1. You need to go Async all the way

I found async/await difficult to get started with because you can only use an await inside a method that is marked with async and returning a Task or Task<>. Then the only way of using that method is to use an await, which needs a async … This all felt somewhat recursive until I realised that the async needed to go all the way up the chain.

This means that once you have an async method somewhere then you are going to have async methods all the way up to the top. In the examples here the ‘top’ is an ASP.NET MVC5 application, which can handle async. Many other Microsoft frameworks have similarly been updated to take async methods, and thankfully my favourite Unit Testing framework, NUnit, also supports async too.

2. You can mark a method as async even if an async method might not get called

This goes with the first item. If you might conditionally call an async method then the calling method still has to be marked as async. However if you do not call that method then the method is run synchronously and returns a Task which does not wait for anything, which is just want you want.

3. Do not have a void return from async method

Watch out for having a void return on an async method. It is valid, but it is a rather special use (see the link below to Stephen Cleary’s article for more). However I found to my cost that when you are converting code to async it’s easy to forget to replace the void return with Task, and then you get into all sorts of problems. I did this inside a SignalR Hub and my application had a strange error that was really hard to find.

4. Watch out for your context

When starting an async task it copies the current ‘context’ so it can restore the context when it exits. This means if you are running in a Windows 8 and ASP.NET system the code can refer to the items it needs to show the results. However this context can be large, so if you have async methods that complete without needing the context then append .ConfigureAwait(false) to the end of the async method you are calling.

I recommend an excellent article by the expert, Stephen Cleary, which deals with all these points and more. The article is called  ‘Best Practices in Asynchronous Programming’

It is simpler, but is it better?

Although it is now really simple now write async methods, do they improve performance? I found a number of articles written by well-known Microsoft people that said there was a big setup cost to async methods and you should only use this where needed. However a number of these were old, i.e. before EF6 was released. I therefore emailed one of the main Microsoft people and his reply was “With EF6/async, you use async everywhere – that changes everything”.

I was already doing extensive performance comparisons to ensure my open-source GenericServices library is fast. I therefore extended my testing to side-by-side comparisons of sync and async versions of the EF’s CRUD (Create, Read, Update and Delete) features. I found the results to be much better than I expected.

In the next two sections I show the side-by-side performance of synchnonous and async methods on a EF6 database and then the same database EF6 running in an ASP.NET MVC5 application. If you just want the recommendations based on these findings then you can skip the next two sections.

A. Side-by side Performance of EF6

For my testing I built a blog database consisting of three classes: Blog, Post and Tag. The diagram below shows the data classes and their relationships.

In this article the tests are done on the Post class, which is the more complex than the Tag or Post. Also, for update I assume a disconnected update, i.e. as would happen on a web site, where the original Post is read and then specific properties are updated before they are written back to the database.

The raw comparison of the sync and async EF6 methods was done inside an NUnit test running inside Visual Studio 2013 on my development PC which has an Intel i7 processor clocked at 3.31GHz. The database is localDb running SQL server 2012. I ran 1000 tests on a database filled with 1000 rows and averaged out the time for each method. The list below gives the average time for a single operation in milliseconds.

Sync (ms) Async (ms) Diff Notes
List, Direct 2.80 7.80 279% Just reads Post class
List, DTO 16.80 21.00 125% Reads Post, Blog and Tags
Create 10.40 8.80 85% Reads Tags and write Post
Update 15.70 9.70 62% Reads Post, Tags and write Post
Delete 0.90 1.10 122% Simple state update

My analysis of these results is as follows:

  1. Async does not have much of an overhead
    I was surprised that the async versions were so fast. Lots of blog posts warn about async being slow, but on the simplest method, which was listing the Post class it was only 5ms slower. That, I think is impressive. However in the unit tests the context it was saving was small (see earlier in the article to learn more about context) and it also caches the context so one off methods might take longer. I look at single reads later in the real-world section below.
  2. Some of the async methods are faster!
    You can see that the async version of create and the update are faster (see blue percentages). Why is that? I am not totally sure, but I think it is because there are multiple database accesses and because I am using Task.WhenAll method which allows parallel running. I would be interested in anyone’s insights on this.

Note the test code is part of my GenericServices open-source project so you can look at the actual code. The Performance test method is called Perf21NCompareEfSyncAndAsync1000Ok and can be found on GitHub at GitHub: JonPSmith/GenericServices

B. Side-by side Performance of EF 6 in ASP.NET MVC5 web site

Raw figures as shown above are interesting, but it is real-world performance that matters. I have a companion MVC5 web site called SampleMvcWebApp, also open-source, which I try things out on. This has the same Blog, Post, Tag format described at the start, but with only a few entries (default 17). I have this MVC web app on different hosting environments, plus internally:

  1. A shared site running Windows Server 2012 R2 through a low-cost hosting company in the UK called WebWiz.
  2. A Windows Azure account using a standard single instance.

My tests are done using ab, the Apache HTTP server benchmarking tool. This allows web site performance tests with different mixes or concurrent users. I used the benchmark tool to read the posts list (17 entries) in two modes: a) one user trying 100 times, b) 50 users trying twice, all at the same time. The results are shown below with the Sync (ms) and Async (ms) columns showing the average total time for one list operation. Note that the SD (ms) column holds the standard deviation, which is the amount of variability in the time that each list took.

Host+ action Sync (ms) SD (ms) Async (ms) SD (ms)
WebWiz
- List Posts ave(100), one user 90 4 95 9
- List Posts ave(100) , 50 users 1065 450 1050 450
Azure, standard
- List Posts ave(100), one user 110 11 120 50
- List Posts ave(100) , 50 users 1200 500 1200 500

Note that you can try the WebWiz site yourself as it is live at http://samplemvcwebapp.net/

My analysis of these results is as follows:

  1. Async listing is slightly slower for one user.
     There is a small difference, less than 10%, between the sync and async versions. However this must all be due to the use of async as the data is the same. It amounts to 5 to 10 milliseconds, which is a little more than we saw in the raw EF figures earlier.
  2. Using Async seems to have had no effect on the scalability of the web site.
    You will see that the figures for 50 simultaneous users are almost the same for both sync and async methods. That makes sense as the database is small and the query very simple so there is almost no waiting for the database (raw numbers for the database access part on small databases is 2 to 3 milliseconds). Clearly with much more complex data accesses then async would start to pull away in terms of scalability of the web site because the thread would be freed up for longer while the database is doing its work.
  3. Most of the time is spent in the MVC/Http part of the process.
    If you look at the timings for one request captured using Google Chrome’s developer tools you can see the parts of the timeline below:

C. Effect of async/await on web Scalability

As explained in the introduction the primary reason for using async methods is to improve web scalability, i.e. the application is able to handle more users because async methods release threads while waiting. Because of the design of async methods we know this is true, but the question is: how much does this help in the real world?

I should start by saying that the main focus on my investigations was to estimate the impact of using async methods. For this reason I looked at individual database access method, which by their nature are compute bound and therefore rarely wait for any resources. You will have seen this in the figures in the last section, as fifty simultaneous users using async data access was exactly the same time for sync and async methods.

Therefore to test scalability I used two method, one synchronous and one async, that delayed by 500 milliseconds. I ran this on the WebWiz hosted site using the ab, the Apache HTTP server benchmarking tool, looking at twenty simultaneous users. The async accesses where fairly consistent at 560 milliseconds (ms.), which is what I expected, i.e. no slow down for multiple users because the async delay method is releasing the thread quickly. On the other hand the sync version was very variable with the quickest being 800 ms. and the longest taking over 2 seconds. This shows that the web site is slowing down even with just twenty users when a synchronous delay method is used. A simple test but I think it shows the power of async.

I would recommend two videos by Microsoft people that provide much better explanations and demos of the effect of using async methods on web site performance and scalability:

Recommendations based on real-world tests

Taking the output of the test tests above and some additional tests I have performed on async tasks (also available to try under the menu item ‘Actions’ on the site SampleMvcWebApp) then here are my recommendations based on these results.

  1. Database – worth using async, but not a lot of gain for simple accesses
    The overhead of using async is small: in my real world tests something like five milliseconds, maybe ten if the context is large. Running side-by-side testing on an ASP.NET MVC5 shows a small difference between sync and async method for small database accesses, i.e. they approximately took about the same time and there was little gain on scalability. This is because, for small database accesses, most of the time is spent computing rather than waiting.
  2. Web services, external APIs, etc. – definitely use async.
    Async is definitely worth having for slow web services, APIs etc. This is because the thread is released for a long time, hence allowing the application to handle more users.
  3. Parallel running of Web services, etc.no-brainer, use async every time.
    Using parallelism to improve performance is great if the task at hand can run in parallel, but this sort of problem does not happen often. I do have a few cases that would have benefitted from this: I had lots of processing to do on results from a geo web service. By using parallelism I could process the current result while fetching the next result. There is also a link to an example in the introduction of this article.
  4. What about compute-bound tasks? – the documentation says use async
    Async/await is now the recommended way of running any multi-tasking code, whether its I/O bound or compute bound task. Async/await is simpler to write and it has a better interface than some of the older tasking methods such as BackgroundWorker. See Microsoft documentation Asynchronous Programming with Async and Await (C# and Visual Basic): Threads

Any down sides?

The one down side I should talk about is debugging. Async tasks are definitely more complicated to debug. For complex problems I sometimes drop back to normal sync versions first to ensure the basic idea is right before going back to async. I have found Unit Testing helps a lot and both NUnit and MSTest (and I expect others) now support async methods.

This isn’t a down side, but more a warning that async/await is not a golden bullet. Async/await is just one of many patterns and techniques to help you improve the responsiveness and scalability of your applications. If you haven’t looked at caching, compression, CDNs, etc. then they will definitely gain you more than swapping all your database accesses to async/await. See the timing trace from the ASP.NET MVC5 test web site above to prove this point.

Dino Esposito is currently running a series on Simple Talk about scalability. Check out his first post at ASP.NET Scalability: the Good, the Bad and the Hype and follow on the articles as they go.

Conclusions

I think the new async/await and supporting library changes in .NET 4.5 makes the job of implement tasking/parallelism so easy that I expect to see it used a lot in the future. For some situations it will make a big effect on performance and scalability of applications. In other situations it may slow the operation down a small amount, so you need to decide whether that matters or not.

The addition of async methods in Entity Framework 6 and in many other Microsoft application frameworks makes using async/awaits an even more compelling proposition. This is definitely a good step forward and I am already enjoying using it.

I have developed two open-source projects which make full use of async/await that you might find useful to look at. They are:
1. SampleMvcWebApp ASP.NET MVC5 web application
This is a live site with a fictitious blog and some examples of long-running actions. See http://samplemvcwebapp.net/ for live site and https://github.com/JonPSmith/SampleMvcWebApp for source on GitHub.
2. GenericServices .NET library
GenericServices is a .NET class library which helps developers build a service layer.  SampleMvcWebApp uses this library extensively. GenericServices is available on GitHub at https://github.com/JonPSmith/GenericServices

Jon Smith

Author profile:

Jon Smith is a software architect and developer who is focused on web-based business applications that have a strong database content, using the Microsoft technologies on the server and various JavaScript/Html libraries at the front end. He has also worked on a number of healthcare-related projects involving mathematical modelling and data visualisation. Follow Jon on his technical blog site, http://www.thereformedprogrammer.net/ and find out about his healthcare modelling work at http://selectiveanalytics.com/

Search for other articles by Jon Smith

Rate this article:   Avg rating: from a total of 20 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Uncessary usage of async/await
Posted by: Felipe Leusin (not signed in)
Posted on: Tuesday, July 29, 2014 at 11:53 AM
Message: You should consider dropping the async/await here:

Public static async Task<ICollection<SimplePostDto>> ListPostAsync(this SampleWebAppDb db)
{
return await db.Set<Post>().AsNoTracking()
....
}

It's better to just return the task since you're not using the result in the method.

Subject: RE: Uncessary usage of async/await
Posted by: JoNSmith (view profile)
Posted on: Wednesday, July 30, 2014 at 1:49 AM
Message: Hi Felipe,

Thank you for pointing that out. While I could remove the unnecessary await I do not think that shows what I wanted to. I have therefore sent an update to the Simple-Talk editor with a simpler and hopefully clearer piece of code.

The new example uses the data from the async call to produce a new result which it returns. This more clearly shows that even if you have called await in the inner method you still need to call await again in the calling method.

Thanks for spotting that and helping to make the article clearer for others. I hope the rest of the article made sense to you and was helpful.

Subject: Scalability?
Posted by: DalSoft.co.uk (not signed in)
Posted on: Wednesday, July 30, 2014 at 5:44 AM
Message: Great post your point on avoiding async void is probably the best advice you can give.

You mention performance but not scalability, making use of async will allow you to scale vertically and is probably worth comparing too.

Subject: RE: Scalability?
Posted by: JoNSmith (view profile)
Posted on: Wednesday, July 30, 2014 at 6:22 AM
Message: Hi DalSoft,

Thanks for your comment. Glad that comment is useful - yes, that void caused some non-obvious problems that were hard to find.

I started this project wanting to see what impact async had on EF commands, as various articles seemed to be saying different things. Therefore the emphasis of my work was on the performance side in real-world situations.

I did look at scalability and I tried Steve Sanderson's tests (see http://channel9.msdn.com/Events/TechDays/Techdays-2012-the-Netherlands/2287 - link also in the article). His type of tests worked when I ran them on my local development machine but not on real web sites. I found that live web sites have loads of threads (WebWiz has 8192 threads), so I think they ran out of compute power before threads. I hope to do more, but I thought what I had was useful to people.

Subject: Really great article
Posted by: @RickAndMSFT (not signed in)
Posted on: Wednesday, July 30, 2014 at 11:45 AM
Message: Thanks for posting this excellent blog
--Rick Anderson

Subject: RE: Really great article
Posted by: JoNSmith (view profile)
Posted on: Wednesday, July 30, 2014 at 12:23 PM
Message: Hi Rick,

Thanks for that comment. Your original email about 'EF 6 changing everything' spurring me into this project and I am really pleased by the performance of EF6 async.

Subject: Good article, but incorrect terminology
Posted by: Rob Levine (not signed in)
Posted on: Monday, August 4, 2014 at 1:10 AM
Message: The article itself was an interesting and informative read, but I found it jarring that you kept referring to async /await as "commands".

Both async and await could be decsibed as "keywords". Async could also be described as a "modifier", and arguably "await" could be described as an expression (at least, "await" followed by the call itself could be).

I don't think either can be described as "commands" though. In fact, I don't believe there are any language constructs in C# that are "commands".

For me, this confusion (and an initial suspicion that you might be talking about them from the point of view of some sort of command pattern) distracted from an otherwise good piece.

It might seem like a small detail, but most articles at this level do get this sort of language correct, so I thought I'd raise it so it can be amended.

(Ed: Thanks for that. Jon has now altered the article in line with your suggestions.)

Subject: RE: Good article, but incorrect terminology
Posted by: JoNSmith (view profile)
Posted on: Monday, August 4, 2014 at 2:34 AM
Message: Hi Rob,

Thanks for your feedback. I think you have a point. Having just edited the article I do seem to have used the word 'command' quite a bit. As you pointed out it is hard to come up for a simple term as sometimes it’s a keyword, sometimes it’s a method etc. I think I fell back on command as it isn't a .NET term, but as you point out it does have other connotations.

I have done a fairly extensive edit and sent it to the Simple-Talk Editor. I have tried to use a more specific term, like feature, method etc. where possible but I have used 'action' a few times where it is not so clear. I hope this makes the article more readable.

As I say, thanks for pointing that out. I hope the article is clearer once the changes are up. Let me know.

(Ed:They're changed now. We'll change the title once we have a redirect in place for the old title!)

Subject: RE: Scalability?
Posted by: JoNSmith (view profile)
Posted on: Tuesday, August 12, 2014 at 8:51 AM
Message: I had a couple of comments about scalability so I have added a new section specifically on that topic. See the section entitled 'C. Effect of async/await on web Scalability'.

My scalability tests were very simple but I have included two links to videos that have more detailed analysis of the positive effect that async methods have on scalability.

I hope people will find that useful.

Subject: "You need to go Async all the way "
Posted by: plevin (view profile)
Posted on: Monday, December 29, 2014 at 4:48 AM
Message: Look here
https://dotnetfiddle.net/zwGVbT

Actually you should not go Async all the way
Some part of code could be sync but operating with tasks (SyncMethod in this sample)

Subject: Re: "You need to go Async all the way "
Posted by: JoNSmith (view profile)
Posted on: Wednesday, December 31, 2014 at 2:19 AM
Message: Hi plevin,

That is a useful comment and I agree. My "go Async all the way" comment was short and did not cover the case you describe. Your method works because you pass the Task back which therefore keeps the task context, but a simple sync method without the Task could cause problems.

For a much fuller look at this topic, including an example of what problems can happen if you mix sync/async, I recommend this blog post http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx from the Microsoft Parallel Programming team.

 

Top Rated

Rethinking the Practicalities of Recursion
 We all love recursion right up to the point of actually using it in production code. Why? Recursion... Read more...

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

Prototyping Desktop Deblector
 Deblector is an open-source debugging add-in for .NET Reflector; the Reflector team investigated... Read more...

.NET Reflector Through the Looking Glass: The Pudding
 There a number of ways in which Reflector, either by itself or with an Addin, allows you to analyse... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Build and Deploy a .NET COM Assembly
 Phil Wilson demonstrates how to build and deploy a .NET COM assembly using best practices, and how to... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.