runtime: Http server performance regression in net6 preview 7
Description
I have a small benchmark program that runs fast with netcore3.1, net5, net6 preview6 and prior (take 1~5 seconds), but the same program runs super slowly in net6 preview 7 (several minutes ~ infinite)
Configuration
Regression?
I can confirm it works fine on netcore3.1, net5, net6 preview 6 and prior
Data
It takes 1~5 seconds normally but several minutes ~ infinite with net6 preview 7
Analysis
N/A
Code
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
static class Program
{
private const string MimeType = "application/json";
private static readonly HttpClient s_client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) };
public static async Task Main(string[] args)
{
int n;
if (args.Length < 1 || !int.TryParse(args[0], out n))
{
n = 2000;
}
var port = 30000 + new Random().Next(10000);
var server = CreateWebHostBuilder(port).Build();
using var cts = new CancellationTokenSource();
_ = Task.Factory.StartNew(() => server.Start(), TaskCreationOptions.LongRunning);
var sum = 0;
var api = $"http://localhost:{port}/";
var tasks = new List<Task<int>>(n);
for (var i = 1; i <= n; i++)
{
tasks.Add(SendAsync(api, i));
}
foreach (var task in tasks)
{
sum += await task.ConfigureAwait(false);
}
Console.WriteLine(sum);
System.Environment.Exit(0);
}
private static async Task<int> SendAsync(string api, int value)
{
var payload = JsonConvert.SerializeObject(new Payload { Value = value });
while (true)
{
try
{
var content = new StringContent(payload, Encoding.UTF8, MimeType);
var response = await s_client.PostAsync(api, content).ConfigureAwait(false);
return int.Parse(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
}
catch { }
}
}
private static IWebHostBuilder CreateWebHostBuilder(int port)
{
return WebHost.CreateDefaultBuilder()
.SuppressStatusMessages(true)
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
})
.UseKestrel(options =>
{
options.Limits.MaxRequestBodySize = null;
options.ListenLocalhost(port);
})
.UseStartup<Startup>();
}
}
public sealed class MyController : Controller
{
[Route("/")]
public async Task<int> PostAsync()
{
using var sr = new StreamReader(Request.Body);
var bodyText = await sr.ReadToEndAsync().ConfigureAwait(false);
var payload = JsonConvert.DeserializeObject<Payload>(bodyText);
return payload.Value;
}
}
public class Payload
{
[JsonProperty("value")]
public int Value { get; set; }
}
public sealed class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().AddApplicationPart(Assembly.GetExecutingAssembly());
}
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 25 (19 by maintainers)
Putting it into 6.0 milestone where it was addressed.
@Tratcher see the PR from @stephentoub above.
We did a bunch of refactoring around connection creation and pooling, and introduced a dumb bug where connections were getting created synchronously even when using async APIs.
@ManickaP, can you try reverting https://github.com/dotnet/runtime/pull/56966 and see if the problem comes back? Based on the dump you shared (which shows a lot of threads stuck doing synchronous connects) plus your comment about it not reproducing when client and server were in different processes, I suspect that PR fixed a large portion of the issue.