Originally trying to create an HTTP endpoint that would remain open for a long time (until a remote service executes and finished, then return the result to the original caller), I hit some concurrency issues: this endpoint would only execute a small number of times concurrently (like 10 or so, whereas I'd expect hundreds if not more).
I then narrowed down my code to a test endpoint that merely returns after a certain amount of MS you give it via the URL. This method should, in theory, give maximum concurrency, but it doesn't happen neither when running under an IIS on a Windows 10 desktop PC nor when running on a Windows 2012 Server.
This is the test Web API endpoint:
[Route("throughput/raw")]
[HttpGet]
public async Task<IHttpActionResult> TestThroughput(int delay = 0)
{
await Task.Delay(delay);
return Ok();
}
And this is a simple test app:
class Program
{
static readonly HttpClient HttpClient = new HttpClient();
static readonly ConcurrentBag<long> Stats = new ConcurrentBag<long>();
private static Process _currentProcess;
private static string url = "http://local.api/test/throughput/raw?delay=0";
static void Main()
{
// Warm up
var dummy = HttpClient.GetAsync(url).Result;
Console.WriteLine("Warm up finished.");
Thread.Sleep(500);
// Get current process for later
_currentProcess = Process.GetCurrentProcess();
for (var i = 1; i <= 100; i++)
{
Thread t = new Thread(Proc);
t.Start();
}
Console.ReadKey();
Console.WriteLine($"Total requests: {Stats.Count}\r\nAverage time: {Stats.Average()}ms");
Console.ReadKey();
}
static async void Proc()
{
Stopwatch sw = Stopwatch.StartNew();
sw.Start();
await HttpClient.GetAsync(url);
sw.Stop();
Stats.Add(sw.ElapsedMilliseconds);
Console.WriteLine($"Thread finished at {sw.ElapsedMilliseconds}ms. Total threads running: {_currentProcess.Threads.Count}");
}
}
The results I get are these:
Warm up finished.
Thread finished at 118ms. Total threads running: 32
Thread finished at 114ms. Total threads running: 32
Thread finished at 130ms. Total threads running: 32
Thread finished at 110ms. Total threads running: 32
Thread finished at 115ms. Total threads running: 32
Thread finished at 117ms. Total threads running: 32
Thread finished at 119ms. Total threads running: 32
Thread finished at 112ms. Total threads running: 32
Thread finished at 163ms. Total threads running: 32
Thread finished at 134ms. Total threads running: 32
...
...
Some more
...
...
Thread finished at 4511ms. Total threads running: 32
Thread finished at 4504ms. Total threads running: 32
Thread finished at 4500ms. Total threads running: 32
Thread finished at 4507ms. Total threads running: 32
Thread finished at 4504ms. Total threads running: 32
Thread finished at 4515ms. Total threads running: 32
Thread finished at 4502ms. Total threads running: 32
Thread finished at 4528ms. Total threads running: 32
Thread finished at 4538ms. Total threads running: 32
Thread finished at 4535ms. Total threads running: 32
So:
I'm not sure why are there only 32 threads running (I assume it's related to the number of cores on my machine although sometimes the number is 34 and anyway it should be much more I think).
The main issue I'm trying to tackle: The running time goes up as more calls are created, whereas I'd expect it to remain relatively constant.
What am I missing here? I'd expect an ASP.NET site (API in this case but it doesn't matter), running on a Windows Server (so no artificial concurrency limit is applied) to handle all these concurrent requests just fine and not increase the response time. I believe the response time is increased because threads are capped on the server side so subsequent HTTP calls wait for their turn. I'd also expect more than 32/34 threads running on the client (test) application.
I also tried to tweak machine.config without much success but I think that even the default should give much more throughput.