Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does asynchronous model really give benefits in throughput against properly configured synchronous?

Everybody knows that asynchrony gives you "better throughput", "scalability", and more efficient in terms of resources consumption. I also thought this (simplistic) way before doing an experiment below. It basically tells that if we take into account all the overhead for asynchronous code and compare it against properly configured synchronous code it yields little to no performance/throughput/resource consumption advantages.

The question: Does asynchronous code actually perform so much better comparing to the synchronous code with correctly configured thread pool? May be my performance tests are flawed in some dramatic way?

Test setup: Two ASP.NET Web API methods with JMeter trying to call them with 200 threads thread group (30 seconds rump up time).

[HttpGet]
[Route("async")]
public async Task<string> AsyncTest()
{
    await Task.Delay(_delayMs);

    return "ok";
}

[HttpGet]
[Route("sync")]
public string SyncTest()
{
    Thread.Sleep(_delayMs);

    return "ok";
}

Here is response time (log scale). Notice how synchronous code becomes faster when Thread Pool injected enough threads. If we were to set up Thread Pool beforehand (via SetMinThreads) it would outperform async right from the start.

response time

What about resources consumption you would ask. "Thread has big cost in terms of CPU time scheduling, context switching and RAM footprint". Not so fast. Threads scheduling and context switching is efficient. As far as the stack usage goes thread does not instantly consume the RAM, but rather just reserve virtual address space and commit only a tiny fraction which is actually needed.

Let's look at what the data says. Even with bigger amount threads sync version has smaller memory footprint (working set which maps into the physical memory).

stats-1

stats-2

UPDATE. I want to post the results of follow-up experiment which should be more representational since avoids some biases of the first one.

First of all, the results of the first experiment are taken using IIS Express, which is basically dev time server, so I needed to move away from that. Also, considering the feedback I've isolated load generation machine from the server (two Azure VMs in the same network). I've also discovered that some IIS threading limits are from hard to impossible to violate and ended up switching to ASP.NET WebAPI self-hosting to eliminate IIS from the variables as well. Note that memory footprints/CPU times are radically different with this test, please do not compare numbers across the different test runs as setups are totally different (hosting, hardware, machines setup). Additionally, when I moved to another machines and another hosting solution the Thread Pool strategy changed (it is dynamic) and injection rate increased.

Settings: Delay 100ms, 200 JMeter "users", 30 sec ramp-up time.

response-time-2

stats-2-1

stats-2-2

I want to conclude these experiments with the following: Yes, under some specific (more laboratory like) circumstances it's possible to get comparable results for sync vs. async, but in real world cases where workload can not be 100% predictable and workload is uneven we inevitably will hit some kind of threading limits: either server side limits, or Thread Pool grow limits (and bear in mind that thread pool management is automatic mechanism with not always easily predictable properties). Additionally, sync version does have a bigger memory footprint (both working set, and way bigger virtual memory size). As far as CPU consumption is concerned async also wins (CPU time per request metric).

On IIS with default settings the situation is even more dramatic: synchronous version is order(s) of magnitude slower (and smaller throughput) due to quite tight limit on threads count - 20 per CPU.

PS. Do use asynchronous pipelines for IO! [... sigh of relief...]

like image 764
Eugene D. Gubenkov Avatar asked Apr 24 '19 05:04

Eugene D. Gubenkov


People also ask

What is the benefit of using asynchronous calls instead of using synchronous calls?

Async improves responsiveness If such an activity is blocked in a synchronous process, the entire application must wait. In an asynchronous process, the application can continue with other work that doesn't depend on the web resource until the potentially blocking task finishes.

Is asynchronous better than synchronous programming?

In these cases, asynchronous programming helps the app screens load faster, improving the user experience. Synchronous programming, on the other hand, is advantageous for developers. Quite simply, synchronous programming is much easier to code.

Is asynchronous faster than synchronous?

Synchronous transmission is fast. Asynchronous transmission is slow.

Does asynchronous programming improve performance?

For applications with many tasks, programmers can consider using async programming. It allows one or more tasks to progress independently, rather than sequentially. The user benefits from increased responsiveness and improved overall performance.


1 Answers

Everybody knows that asynchrony gives you "better throughput", "scalability", and more efficient in terms of resources consumption.

Scalability, yes. Throughput: it depends. Each asynchronous request is slower than the equivalent synchronous request, so you would only see a throughput benefit when scalability comes into play (i.e., there are more requests than threads available).

Does asynchronous code actually perform so much better comparing to the synchronous code with correctly configured thread pool?

Well, the catch there is "correctly configured thread pool". What you're assuming is that you can 1) predict your load, and 2) have a server big enough to handle it using one thread per request. For many (most?) real-world production scenarios, either or both of these are not true.

From my article on async ASP.NET:

Why not just increase the size of the thread pool [instead of using async]? The answer is twofold: Asynchronous code scales both further and faster than blocking thread pool threads.

First, asynchronous code scales further than synchronous code. With more realistic example code, the total scalability of ASP.NET servers (stress tested) showed a multiplicative increase. In other words, an asynchronous server could handle several times the number of continuous requests as a synchronous server (with both thread pools turned up to the maximum for that hardware). However, these experiments (not done by me) were done on a expected "realistic baseline" for average ASP.NET apps. I don't how the same results would carry over to a noop string return.

Second, asynchronous code scales faster than synchronous code. This one is pretty obvious; synchronous code scales fine up to the number of thread pool threads, but then can't scale faster than the thread injection rate. So you get that really slow response to a sudden heavy load, as shown in the beginning of your response time graph.

I think the work you've done is interesting; I am particularly surprised at the memory usage differences (or rather, lack of difference). I'd love to see you work this into a blog post. Recommendations:

  • Use ASP.NET Core for your tests. The old ASP.NET had only a partially-asynchronous pipeline; ASP.NET Core would be necessary for a more "pure" comparison of sync vs async.
  • Don't test locally; there are a lot of caveats when doing that. I'd recommend choosing a VM size (or single-instance Docker container or whatever) and testing in the cloud for repeatability.
  • Also try stress testing in addition to load testing. Keep increasing load until the server is totally overwhelmed, and see how both the async and sync servers respond.

As a final reminder (also from my article):

Bear in mind that asynchronous code does not replace the thread pool. This isn’t thread pool or asynchronous code; it’s thread pool and asynchronous code. Asynchronous code allows your application to make optimum use of the thread pool. It takes the existing thread pool and turns it up to 11.

like image 189
Stephen Cleary Avatar answered Sep 29 '22 21:09

Stephen Cleary