Why isn't async await improving performance? - asp.net-mvc

I watched this video : https://channel9.msdn.com/Events/TechDays/Techdays-2012-the-Netherlands/2287. So i tried to implement the usage of async/await in a controller. SO this is basicaly what I did :
public class HomeController : Controller
{
private static WebClient _webClient = new WebClient();
public async Task<ActionResult> IndexAsync()
{
var data = await _webClient.DownloadStringTaskAsync("http://stackoverflow.com/");
return View("Index", (object)data);
}
public ActionResult Index()
{
var data = _webClient.DownloadString("http://stackoverflow.com/");
return View("Index", (object)data);
}
}
Then I used Apache Benchmark and did the two following tests :
ab -n 100 -c 100 http://localhost:53446/Home/index
and
ab -n 100 -c 100 http://localhost:53446/Home/indexasync
And I got the exact same performance (I have 8 CPU core). Why is that ?

Async is not about performance. That's just categorically incorrect. In fact, an async request will often be less performant that sync, simply because there's additional overhead involved with async.
The reason to use async is about efficient resource-management and scale. A typical web server process will have around 1000 threads. This is often called the "max requests", as one thread general equals one request. If you have an 8 core CPU, you should ideally have a process per core (in IIS those are called "web workers"). So, theoretically, you'd have around 8000 threads total to work with.
That's quite a lot actually, though a modern web page consumes more requests than most people think. The page itself is one request, but that page will have images and external JS and CSS files, all of which generate a request, and will often utilize AJAX, for further requests. The point is that while 8000+ threads is still quite a lot to have in your pool, you could still very well run out if the server is under significant load.
Async merely gives you breathing room above that limit. In situations where the thread enters a wait-state, it can be returned to the pool to field other requests while whatever external action is being completed. The alternative is that the thread would just sit there idle (sync). That's really all there is to it. It's entirely about tasking those otherwise idle threads with some other bit of work, which could mean the difference between requests queuing up and timing out or being handled, even if slowly.

Running a load test that exhausts the thread pool is difficult to do on a local box. It's a lot easier to pretend the thread pool is exhausted by artificially restricting it, as I do in my gist:
protected void Application_Start()
{
int workerThreads, ioThreads;
ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
ThreadPool.SetMaxThreads(Environment.ProcessorCount, ioThreads);
...
}

There are a couple of reasons that stand out.
From Using Asynchronous Methods in ASP.NET MVC 4
the number of threads in the thread pool is limited (the default maximum for .NET 4.5 is 5,000). In large applications with high concurrency of long-running requests, all available threads might be busy. This condition is known as thread starvation.
So, running 100 request at a time will not even begin to starve your threads.
Also, a simple GET request will run very quickly. A test that performs an action that takes multiple seconds or even minutes would bear more obvious performance gains.

Related

BufferAsyncSink is serializing the stream

I am using Flux.create() to bridge from listener type API (custom HTTP server) to Flux stream. Very simple code, basically:
Flux.create(listener::setSink, FluxSink.OverflowStrategy.BUFFER)
... bunch of operators
.subscribe();
And inside listener:
sink.next(item);
Everything is working fine, but after some troubleshooting I found out that BufferAsyncSink that is created by FluxCreate (for BUFFER strategy) is basically serializing downstream processing.
#Override
public FluxSink<T> next(T t) {
queue.offer(t);
drain();
return this;
}
This adds item to the queue, and then proceeds to drain the queue.
However, this code only allows single thread to drain the queue.
void drain() {
if (WIP.getAndIncrement(this) != 0) {
return;
}
I don't understand why is this limitation is here. Why can't multiple threads drain the queue? In fact, why is the queue even needed?
I know I can offload next item to another thread pool with publishOn/subscribeOn, but we already have a thread pool for our HTTP server and it would be more optimal if thread that received HTTP request would continue processing flux until its down. We have backpressure built into our HTTP server so we don't need Flux to handle it.
I looked all over Reactor documentation and can't find another way to create Flux that would not serialize the stream.
Anybody have any suggestions?

Does await Task.Delay; really enable web server to process more simultaneous requests?

From Pro Asynchrnous Programming with .Net:
for (int nTry = 0; nTry < 3; nTry++)
{
try
{
AttemptOperation();
break;
}
catch (OperationFailedException) { }
Thread.Sleep(2000);
}
While sleeping, the thread doesn’t consume any CPU-based resources,
but the fact that the thread is alive means that it is still consuming
memory resources. On a desktop application this is probably no big
deal, but on a server application, having lots of threads sleeping is
not ideal because if more work arrives on the server, it may have to
spin up more threads, increasing memory pressure and adding additional
resources for the OS to manage.
Ideally, instead of putting the thread to sleep, you would like to
simply give it up, allowing the thread to be free to serve other
requests. When you are ready to continue using CPU resources again,
you can obtain a thread ( not necessarily the same one ) and continue
processing. You can solve this problem by not putting the thread to
sleep, but rather using await on a Task that is deemed to be completed
in a given period.
for (int nTry = 0; nTry < 3; nTry++)
{
try
{
AttemptOperation();
break;
}
catch (OperationFailedException) { }
await Task.Delay(2000);
}
I don't follow author's reasoning. While it's true that calling await Task.Delay will release this thread ( which is processing a request ), but it's also true that task created by Task.Delay will occupy some other thread to run on. So does this code really enable server to process more simultaneous requests or is the text wrong?!
Task.Delay does not occupy some other thread. It gives you a task without blocking. It starts a timer that completes that task in its callback. The timer does not use any thread while waiting.
It is a common myth that async actions like delays or IO just push work to a different thread. They do not. They use OS facilities to truly use zero threads while the operation is in progress. (They obviously need to use some thread to initiate and complete the operation.)
If async was just pushing work to a different thread it would be mostly useless. It's value would be just to keep the UI responsive in client apps. On the server it would only cause harm. It is not so.
The value of async IO is to reduce memory usag (less thread stacks), context switching and thread-pool utilization.
The async version of the code you posted would scale to literally tens of thousands of concurrent requests (if you increase the ASP.NET limits appropriately, which is a simple web.config change) with small memory usage.

How to release DataSnap memory once connections are closed?

I have a TCP/IP DataSnap server running as a service [Session based LifeCycle] that continuously chews up memory and never comes back to the starting memory size even when there are no connections to it.
In order to eliminate My code as the culprit, I have modeled up a basic TCP/IP DataSnap server running as VCL [Session based LifeCycle] that serves a Server Method class [TDSServerModule] which only contains basic mathematical functions using native data types [no objects to create or free].
When I connect to said DataSnap server with a very thin client I get the same results.
The Memory Usage continuously grows with each connection and sporadically grows when executing the server side methods from the client. Once the connections are closed the DataSnap Server never reduces its Memory Usage [even when left running without connections for 8 hrs].
Any suggestions as to why this occurs or more importantly how to curtail it?
I am using RAD Studio XE2 Update 4 HotFix 1.
Let me quote a "must read" article about DataSnap. This is about XE3 but i hope the code here would work for XE2 as well.
Memory consumption
One of the issues that I observed was related to memory consumption. Why Datasnap server consumes so much memory if the method called does absolutely nothing?
Maybe I don’t know how to explain exactly but i will try. Basically the DataSnap creates a session for each HTTP connection that it receives. This session will be destroyed after 20 minutes, in other words, on the first 20 minutes of the test the memory consumption will only go up, after that it has the tendency of stabilize itself. I really have no idea why Datasnap does this. In a REST application I don’t see much sense in these sessions with a default configuration. Of course, sessions can be helpful, but i can’t understand why it’s a default configuration. Indeed, DataSnap doesn’t have a configuration for that. It appears like you just have to use this session control, without being able to choose otherwise (There is no documentation). The MORMot framework has a session control too but it’s configurable and doesn’t consumes so much memory.
Anyway, there is a way around this problem. Daniele Teti wrote an article on his blog, take a look. The solution that I will show here was placed by him on his blog. Thanks Daniele.
uses System.StrUtils, DataSnap.DSSession, Data.DBXPlatform;
function TServerMethods1.HelloWorld: String;
begin
Result := 'Hello World';
GetInvocationMetaData.CloseSession := True;
end;
After running this method the session will close and memory consumption will be lower. Of course still exists an overhead for creating and destroying this session.
So it seems the best course for you is ending every server method with explicit memory cleanup, if that was possible in XE2. Then you'd better read thosee articles again and prepare for future scalability challenges.
http://www.danieleteti.it/2012/12/15/datasnap-concurrency-problems-and-update1/
http://robertocschneiders.wordpress.com/2013/01/09/datasnap-analysis-based-on-speed-stability-tests-part-2/
I added the below method and called it from the "TWebModule1::WebModuleBeforeDispatch" event. It eliminated the memory consumption and actually allow the idle REST service to return to a state of no-session memory. DataSnap definitely needs to work on this issue.
// ---------------------------------------------------------------------------
/// <summary> Memory Restoration. DataSnap opens a session for each call
/// even when the service is set for invocation.
/// Sessions are building up consuming memory and seem not to be freed.
/// See: https://stackoverflow.com/questions/17748300/how-to-release-datasnap-memory-once-connections-are-closed
/// </summary>
/// <remarks> Iterates session in the session manager and closes then terminates
/// any session that has been idle for over 10 seconds.
/// </remarks>
/// <returns> void
/// </returns>
// ---------------------------------------------------------------------------
void TWebModule1::CloseIdleSessions()
{
TDSSessionManager* sessMgr = TDSSessionManager::Instance;
int sessCount = sessMgr->GetSessionCount();
WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "Session Count: " + IntToStr(sessCount));
TStringList* sessKeys = new TStringList;
sessMgr->GetOpenSessionKeys(sessKeys);
WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "Session Keys Count: " + IntToStr(sessKeys->Count));
TDSSession* sess = NULL;
for(int index = 0; index < sessKeys->Count; index++)
{
String sessKey = sessKeys->Strings[index];
sess = sessMgr->Session[sessKey];
unsigned elapsed = (int)sess->ElapsedSinceLastActvity();
if(elapsed > 10000)
{
WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "CloseSession TerminateSession Key: " + sessKey);
sessMgr->CloseSession(sessKey);
sessMgr->TerminateSession(sessKey);
}
sess = NULL;
}
delete sessKeys;
sessMgr = NULL;
}
You should check the Lifecycle property on TDSServerclass component on your servercontainer. It provides a way to determine the way the session is handled. It defaults to session. Setting it to invokation will free the session after each call (invokation). This will of course mean you have no state. This would be oke in a typical REST server though.
if you still have memory consumption growing. put the following line in your dpr unit.
ReportMemoryLeaksOnShutdown := True;
your application will then show you the memory leaks it has on closing of your datasnap server.

To wait or not to wait inside an AsyncController's Async method

I've seen 2 flavors of working with asyncronous operations in mvc controllers.
First:
public void GetNewsAsync()
{
AsyncManager.OutstandingOperations.Increment();
using (ManualResetEvent mre = new ManualResetEvent(false))
{
//Perform the actual operation in a worker thread
ThreadPool.QueueUserWorkItem((object _mre) =>
{
//do some work in GetFeed that takes a long time
var feed = GetFeed();
AsyncManager.Parameters["Feed"] = feed;
AsyncManager.OutstandingOperations.Decrement();
mre.Set();
}, mre);
//Wait for the worker thread to finish
mre.WaitOne(TimeSpan.FromSeconds(SomeNumberOfSecondsToWait));
}
}
Second:
public void GetNewsAsync()
{
AsyncManager.OutstandingOperations.Increment();
//Perform the actual operation in a worker thread
ThreadPool.QueueUserWorkItem((object x) =>
{
//do some work in GetFeed that takes a long time
var feed = GetFeed();
AsyncManager.Parameters["Feed"] = feed;
AsyncManager.OutstandingOperations.Decrement();
}, null);
}
The first blocks GetNewsAsync for SomeNumberOfSecondsToWait, the second does not. Both perform the work inside a of a worker thread and the results passed to GetNewsCompleted.
So my question is, which is the correct way to handle an Ajax call to GetNews; Wait, or don't wait?
I don't know where did you see the first example but that's a total anti-pattern that completely defeats the purpose of an asynchronous controller. The whole point of an asynchronous operation is to execute asynchronously and free the main thread as fast as possible.
This being said if GetFeed is a blocking call (which is what its name supposes it is) you get strictly 0 benefit from an asyncrhonous controller so the second example is also wrong for me. You could use a standard synchronous controller action in this case. With the second example you draw a thread from the pool and instead of blocking inside the main thread you block inside the other thread so the net effect is almost the same (in reality it's worse) if you had used a standard synchronous controller action.
So both those examples will bring more overhead than any benefit.
Where asynchronous controllers are useful is when you have some I/O intensive API such as a database or web service call where you could take advantage of IO Completion Ports. The following article provides a good example of this scenario. The newsService used there is providing real asynchronous methods and there is no blocking during the I/O network call. No worker thread being jeopardized.
I would also recommend you reading the following article. Even if it is for classic WebForms it still contains some very useful information.

Ideal way to pull data (JSON) from a service using Monotouch and iOS?

I have an iPhone app which is pulling just about all it's data from ASP.NET MVC services.
Basically just returning JSON.
When I run the app in the simulator, the data is pulled down very fast etc.. however when I use the actual device (3G or WiFi) it's extremely slow. To the point that the app crashes for taking too long.
a) Should I not be calling a service from the FinishedLaunching method in AppDelegate?
b) Am I calling the service incorrectly?
The method I'm using goes something like this:
public static JsonValue GetJsonFromURL(string url) {
var request = (HttpWebRequest)WebRequest.Create (url);
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
using(var response = (HttpWebResponse)request.GetResponse()) {
using(var streamReader = new StreamReader(response.GetResponseStream())) {
return JsonValue.Load(streamReader);
}
}
}
Is there a better or quicker way I should be querying a service? I've read about doing things on different threads or performing async calls to not lock the UI, but I'm not sure what the best approach or how that code would work.
a) Should I not be calling a service from the FinishedLaunching method in AppDelegate?
You get limited time to get your application up and running, i.e. returning from FinishedLaunching or the iOS watchdog will kill your application. That's about 17 seconds total (but could vary between devices/iOS versions).
Anything that takes some time is better done in another thread, launched from FinishedLaunching. It's even more important if you use networking services as you cannot be sure how much time (or even if) you'll get an answer.
b) Am I calling the service incorrectly?
That looks fine. However remember that the simulator has a faster access to the network (likely), much more RAM and CPU power. Large data set can take a lot of memory / CPU time to decode.
Running from another thread will, at least, cover the extra time required. It can be as simple as adding the code (below) inside your FinishedLaunching.
ThreadPool.QueueUserWorkItem (delegate {
window.BeginInvokeOnMainThread (delegate {
// run your code
});
});
You can have a look at how Touch.Unit does it by looking at its TouchRunner.cs source file.
note: you might want to test not using (asking) for compressed data since the time/memory to decompress it might not be helpful on devices (compared to the simulator). Actual testing needed to confirm ;)

Resources