runtime: HttpClient Performance Slow Compared to .NET 4.7
I have conducted a basic performance test of HttpClient
in .NET Core 2.0 versus HttpClient
in .NET Framework 4.7 and have noticed a gap in performance. The .NET Framework 4.7 HttpClient/WebClient
outperforms .NET Core’s HttpClient
by ~1.5-2x in terms of how long it takes to complete a batch of n requests.
Testing
The test is a console app (run in release mode on Windows 7 16GB 3.5GHz) with identical code for .NET Core/Framework that follows this sequence:
- Create a single, shared HttpClient instance with a maximum of n (for testing n=10,100) connections.
// .NET Core 2.0
var httpClient = new HttpClient(new HttpClientHandler { MaxConnectionsPerServer = 100 });
// .NET Framework 4.7
ServicePointManager.DefaultConnectionLimit = 100;
var httpClient = new HttpClient();
// .NET Framework 4.7 - WebClient instance is created ONCE PER REQUEST
ServicePointManager.DefaultConnectionLimit = 100;
var webClient = new WebClient();
- Start 10,000 simulataneous requests and time how long each request takes to complete + how long all take to complete. Here is code demonstrating how requests are made / timed.
private void RunTest(int count)
{
// requests is a list of Request data structures used to store individual request latency / responses
var requests = Enumerable.Range(0, count).Select(j => CreateRequest(j)).ToList();
// this stopwatch is for duration of all requests
var stopwatch = Stopwatch.StartNew();
// start all requests and wait for completion
var requestTasks = requests.Select(MakeRequest).ToArray();
Task.WaitAll(requestTasks);
stopwatch.Stop();
// total run time = stopwatch.ElapsedMilliseconds
}
private Task MakeRequest(Request request)
{
var stopwatch = Stopwatch.StartNew();
var response = await httpClient.GetStringAsync(request.Url);
stopwatch.Stop();
// save request duration and response
request.DurationMs = stopwatch.ElapsedMilliseconds;
request.ResponseId = ParseResponse(response);
}
I am testing against a basic python server that is well under capacity to rule out any server side bottlenecks. The server returns a simple JSON response containing an id
field which is used to validate the HttpClient responses.
{ "id": "some_identifier" }
JSON deserialization / response validation is NOT included in performance stats.
Results
These are average statistics (in milliseconds) of 5 separate runs of 10,000 requests each. It is worth mentioning that the actual time spent on each request was much less with .NET Core’s HttpClient, it’s just that the batch of requests takes longer to complete as a whole vs .NET Framework.
framework | total run time (ms) | avg req time (ms) | median req time (ms) | total time spent on requests (s) |
---|---|---|---|---|
.NET Core 2.0 | 5614 | 216 | 282 | 777 |
.NET 4.7 | 3055 | 1355 | 1339 | 10,585 |
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 6
- Comments: 35 (12 by maintainers)
It can’t come too soon, in my experience so far, .net core performance is unacceptable for production use, compared even to .net framework, Let’s not bother comparing it to Node, Java, Python, etc, where it gets it’s ars soundly handed to itself repeatedly.
We just tried and we can confirm that .NET Core 2.1 Preview 1 is way more performant than 2.0 under linux using docker containers, it now takes around 90ms for
HttpClient
to call our microservices, where it tooks ~400ms using 2.0.5.Congrats, we are now eagerly waiting for the 2.1 GA version. 😃
It’s possible it’s using it “incorrectly”. If it’s creating and disposing a
new HttpClient()
instance on every access, that is going to be more expensive on .NET Core. In .NET Framework,HttpClient
is a thin wrapper aroundHttpWebRequest
, with the connection pool managed separately, so disposing of theHttpClient
instance won’t tear down connections in the connection pool. In .NET Core, the handler owned byHttpClient
owns the connection pool, and if you donew HttpClient()
, it’s creating a new handler that it owns. It’s possible the slow down you’re seeing is because, due to the wayHttpClient
is being used, it’s creating a new connection on every access in .NET Core and it’s not in .NET Framework.@BKlippel Can you share the data you’re basing that on? Have you tried using .Net Core 2.1 RC1 to see if it’s an improvement?
Means nothing. I’d like to see the results of a performance profiler hooked directly up to tests against the project in question. As is, the stuff you’ve linked means nothing. Unless you’re going to submit a PR or learn the basics of programming, please stop making noise on repositories that thousands of people are following.
@sgf, you’ve been going around commenting on a variety of issues, making broad negative claims about performance, with nothing to substantiate those claims. If you’re hitting a performance issue, please open a new issue that includes a repro of the problem and specifics about what the exact problem is.
@soualid you can try 2.1 right now. It has the new networking stack mentioned above, which is faster, esp. on Linux.
COMPlus_UseManagedHttpClientHandler=true
System.Net.Http.UseManagedHttpClientHandler=true
Ohhh snap got called out sup dog