9

I have set-up the examples from this MSDN article Using Asynchronous Methods in ASP.NET MVC 4 and have done some benchmarking to see what I come up with.

Server Configuration:

  • Windows 7 Professional (x64)
  • IIS 7.5
  • Intel Core i7-2600S @ 2.80HGz
  • 8GB Memory
  • AppPool > Maximum Worker Processes: 10

I set-up 2 controllers Sync and Async and did some testing using a loader tool for benchmarking. The loader tool just sends 50-60 constant requests for one minute. Each controller is calling the same webservice 3 times. The code for each is below:


SYNC:

public ActionResult Sync()
{
    var g1 = GetGizmos("url1");
    var g2 = GetGizmos("url2");
    var g3 = GetGizmos("url3");
    return Content("");
}
    
public object GetGizmos(string uri)
{
    using (WebClient webClient = new WebClient())
    {
        return JsonConvert.DeserializeObject(
         webClient.DownloadString(uri)
        );
    }
}

sync

ASYNC:

public async Task<ActionResult> Async()
{
    var g1 = GetGizmosAsync("url1");
    var g2 = GetGizmosAsync("url2");
    var g3 = GetGizmosAsync("url3");
    var a1 = await g1;
    var a2 = await g2;
    var a3 = await g3;  
    return Content("");
}


public async Task<object> GetGizmosAsync(string uri)
{            
    using (HttpClient httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(uri);
        return (await response.Content.ReadAsAsync<object>());
    }
}
    

async

First question, does anyone know why Async is taking longer, running less and causing timeouts, whereas sync version isn't? I would think using Async for this would be faster, no timeouts, etc. Just doesn't seem right, am I doing something wrong here? What can be done to improve/fix it?

Second question, Using WebRequests in general, is there a way to speed that up? I have set the following in my global.asax but still unsure if the usage is correct:

System.Net.ServicePointManager.DefaultConnectionLimit = 1000;

Also, any other suggestions to help speed up an application performing these types of tasts will be very helpful.

Community
  • 1
  • 1
Control Freak
  • 12,965
  • 30
  • 94
  • 145
  • 2
    Is the backend service even scalable? Adding concurrency does not help if you're bottlenecked anyway. – usr Oct 03 '14 at 18:15
  • 1
    What URL are you getting gizmos from? Could that URL be throttling concurrent requests, or getting into thread locks when multiple requests are made to it? – StriplingWarrior Oct 03 '14 at 18:15
  • 1
    How well does your backend scale? Have you disabled session state? And have you adjusted IIS' queue size (Application Pools | Advanced Settings | Queue Length -- 5000 is recommended for async)? – Stephen Cleary Oct 03 '14 at 18:16
  • @usr What is the backend service? – Control Freak Oct 03 '14 at 18:21
  • @StriplingWarrior Its hitting an api on elasticsearch served on windows. (Note: The elasticsearch server was barely at 2% cpu during all this) – Control Freak Oct 03 '14 at 18:22
  • @Stephen Cleary I always disable session state and upped the Max Worker Threads, I did this to 10, however the Queue length is at default of 1000, haven't tryed that. Is that for webrequests in general? Should I up that to 5000 and try again? – Control Freak Oct 03 '14 at 18:23
  • It's hitting ElasticSearch on windows w/ Java, which doesn't use IIS, it makes its own port node at :9200 and runs by itself basically as its own service. – Control Freak Oct 03 '14 at 18:30
  • Replace the web service call by a sleep(1000) to make sure the web service is not at fault. You should see the async version become 3x faster. Actually, async is a misnomer. It is faster because it runs work in parallel. The same speedup would be achieved with threads. – usr Oct 03 '14 at 18:38
  • @usr well i changed the api to http://www.geonames.org/export/JSON-webservices.html to help narrow down the cause, but still get similar results. I will try what you said, would I do `Task.Delay(1000)` or `Task.Sleep(1000)`? – Control Freak Oct 03 '14 at 18:44
  • Both, in the appropriate version. – usr Oct 03 '14 at 18:50
  • @usr Sorry, but .Sleep() isn't valid. I put .Delay in GetWidgetsAsync but don't know how to delay the sync method, GetWidgets, care to show me how? I put .Delay in there too but it doesnt delay anything. – Control Freak Oct 03 '14 at 19:00
  • Try doing all the same stuff with a console app rather than an MVC app, just to test whether the back-end service (or the way .NET is connecting to it) is actually the bottleneck – StriplingWarrior Oct 03 '14 at 20:03
  • @StriplingWarrior If I it make a console app how am I supposed load test it? In MVC if I load it up one time, it's faster with async (not much), but the OP is about being under load. – Control Freak Oct 04 '14 at 04:58
  • Just search for "async sleep .net". – usr Oct 04 '14 at 08:13

1 Answers1

2

I think the clue is in your comparison

webClient.DownloadString(uri)

to

var response = await httpClient.GetAsync(uri);
return (await response.Content.ReadAsAsync<object>());

maybe you can try

webclient.DownloadStringAsync(uri)

and you could optimize your async code to

await Task.Run(() => {
    // just run your sync code here
    var g1 = GetGizmos("url1");
    var g2 = GetGizmos("url2");
    var g3 = GetGizmos("url3");
    return Content("");
});

It is enough to have one async yielding point here.

Refer to this answer for details about async: Do you have to put Task.Run in a method to make it async?

Community
  • 1
  • 1
Tuan
  • 893
  • 1
  • 9
  • 24