From d9f9596d4783c06006ac9a81c2e1fa4fdb5a7f3e Mon Sep 17 00:00:00 2001 From: "Ram.Type-0" Date: Thu, 6 Mar 2025 04:49:14 +0900 Subject: [PATCH 1/2] Faster C# http-server implementation --- bench/algorithm/http-server/1.cs | 13 ++++------- bench/algorithm/http-server/2-http2.cs | 32 ++++++++++++++++++-------- bench/algorithm/http-server/2-http3.cs | 30 ++++++++++++++++-------- bench/algorithm/http-server/2.cs | 32 ++++++++++++++++++-------- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/bench/algorithm/http-server/1.cs b/bench/algorithm/http-server/1.cs index 097da6ddf..0a6a67902 100644 --- a/bench/algorithm/http-server/1.cs +++ b/bench/algorithm/http-server/1.cs @@ -3,6 +3,7 @@ using System.IO; using System.Net; using System.Net.Http; +using System.Net.Http.Json; using System.Reflection; using System.Text; using System.Text.Json; @@ -63,14 +64,13 @@ public static async Task Main(string[] args) private static async Task SendAsync(string api, int value) { // await Task.Yield(); - var payload = JsonSerializer.Serialize(new Payload { Value = value }); + var payload = new Payload { Value = value }; while (true) { try { - var content = new StringContent(payload, Encoding.UTF8); - var response = await s_client.PostAsync(api, content).ConfigureAwait(false); - return int.Parse(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); + var response = await s_client.PostAsJsonAsync(api, payload).ConfigureAwait(false); + return int.Parse(await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false)); } catch { } } @@ -102,11 +102,8 @@ private static IHostBuilder CreateWebHostBuilder(int port) => public sealed class MyController : Controller { [Route("/")] - public async Task PostAsync() + public int Post([FromBody] Payload payload) { - using var sr = new StreamReader(Request.Body); - var bodyText = await sr.ReadToEndAsync().ConfigureAwait(false); - var payload = JsonSerializer.Deserialize(bodyText); return payload.Value; } } diff --git a/bench/algorithm/http-server/2-http2.cs b/bench/algorithm/http-server/2-http2.cs index 35f8fe800..c2dd63a1c 100644 --- a/bench/algorithm/http-server/2-http2.cs +++ b/bench/algorithm/http-server/2-http2.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Net; using System.Net.Http; +using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Logging; @@ -43,13 +47,22 @@ public static async Task Main(string[] args) var port = 30000 + new Random().Next(10000); var app = CreateWebApplication(port); - app.MapPost("/", async ctx => + app.MapPost("/", async (HttpResponse response, [FromBody] Payload payload) => { - using var sr = new StreamReader(ctx.Request.Body); - var bodyText = await sr.ReadToEndAsync().ConfigureAwait(false); - var payload = JsonSerializer.Deserialize(bodyText); - ctx.Response.StatusCode = 200; - await ctx.Response.BodyWriter.WriteAsync(Encoding.UTF8.GetBytes(payload.Value.ToString())).ConfigureAwait(false); + + if (payload.Value.TryFormat(response.BodyWriter.GetSpan(16), out n)) + { + response.StatusCode = StatusCodes.Status200OK; + + response.BodyWriter.Advance(n); + await response.BodyWriter.FlushAsync().ConfigureAwait(false); + } + else + { + throw new UnreachableException(); + } + + }); using var serverTask = app.RunAsync(); @@ -70,14 +83,13 @@ public static async Task Main(string[] args) private static async Task SendAsync(string api, int value) { - var payload = JsonSerializer.Serialize(new Payload { Value = value }); + var payload = new Payload { Value = value }; while (true) { try { - var content = new StringContent(payload, Encoding.UTF8); - var response = await s_client.PostAsync(api, content).ConfigureAwait(false); - return int.Parse(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); + var response = await s_client.PostAsJsonAsync(api, payload).ConfigureAwait(false); + return int.Parse(await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false)); } catch (Exception e) { diff --git a/bench/algorithm/http-server/2-http3.cs b/bench/algorithm/http-server/2-http3.cs index d9c055494..1d9cdd9ad 100644 --- a/bench/algorithm/http-server/2-http3.cs +++ b/bench/algorithm/http-server/2-http3.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Net; using System.Net.Http; +using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Logging; @@ -43,13 +47,22 @@ public static async Task Main(string[] args) var port = 30000 + new Random().Next(10000); var app = CreateWebApplication(port); - app.MapPost("/", async ctx => + app.MapPost("/", async (HttpResponse response, [FromBody] Payload payload) => { - using var sr = new StreamReader(ctx.Request.Body); - var bodyText = await sr.ReadToEndAsync().ConfigureAwait(false); - var payload = JsonSerializer.Deserialize(bodyText); - ctx.Response.StatusCode = 200; - await ctx.Response.BodyWriter.WriteAsync(Encoding.UTF8.GetBytes(payload.Value.ToString())).ConfigureAwait(false); + + if (payload.Value.TryFormat(response.BodyWriter.GetSpan(16), out n)) + { + response.StatusCode = StatusCodes.Status200OK; + + response.BodyWriter.Advance(n); + await response.BodyWriter.FlushAsync().ConfigureAwait(false); + } + else + { + throw new UnreachableException(); + } + + }); using var serverTask = app.RunAsync(); @@ -70,13 +83,12 @@ public static async Task Main(string[] args) private static async Task SendAsync(string api, int value) { - var payload = JsonSerializer.Serialize(new Payload { Value = value }); + var payload = new Payload { Value = value }; while (true) { try { - var content = new StringContent(payload, Encoding.UTF8); - var response = await s_client.PostAsync(api, content).ConfigureAwait(false); + var response = await s_client.PostAsJsonAsync(api, payload).ConfigureAwait(false); return int.Parse(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); } catch (Exception e) diff --git a/bench/algorithm/http-server/2.cs b/bench/algorithm/http-server/2.cs index 10d4b7dc1..ccd388ac7 100644 --- a/bench/algorithm/http-server/2.cs +++ b/bench/algorithm/http-server/2.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Net; using System.Net.Http; +using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; static class Program @@ -34,13 +38,22 @@ public static async Task Main(string[] args) var port = 30000 + new Random().Next(10000); var app = CreateWebApplication(port); - app.MapPost("/", async ctx => + app.MapPost("/", async(HttpResponse response, [FromBody] Payload payload) => { - using var sr = new StreamReader(ctx.Request.Body); - var bodyText = await sr.ReadToEndAsync().ConfigureAwait(false); - var payload = JsonSerializer.Deserialize(bodyText); - ctx.Response.StatusCode = 200; - await ctx.Response.BodyWriter.WriteAsync(Encoding.UTF8.GetBytes(payload.Value.ToString())).ConfigureAwait(false); + + if (payload.Value.TryFormat(response.BodyWriter.GetSpan(16), out n)) + { + response.StatusCode = StatusCodes.Status200OK; + + response.BodyWriter.Advance(n); + await response.BodyWriter.FlushAsync().ConfigureAwait(false); + } + else + { + throw new UnreachableException(); + } + + }); using var serverTask = app.RunAsync(); @@ -63,14 +76,13 @@ public static async Task Main(string[] args) private static async Task SendAsync(string api, int value) { // await Task.Yield(); - var payload = JsonSerializer.Serialize(new Payload { Value = value }); + var payload = new Payload { Value = value }; while (true) { try { - var content = new StringContent(payload, Encoding.UTF8); - var response = await s_client.PostAsync(api, content).ConfigureAwait(false); - return int.Parse(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); + var response = await s_client.PostAsJsonAsync(api, payload).ConfigureAwait(false); + return int.Parse(await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false)); } catch { } } From cc2c43fbbf03e74f30f17c42272e634cb4ea4f63 Mon Sep 17 00:00:00 2001 From: "Ram.Type-0" Date: Thu, 6 Mar 2025 04:56:34 +0900 Subject: [PATCH 2/2] Add missing replacement `ReadAsStringAsync` => `ReadAsByteArrayAsync` --- bench/algorithm/http-server/2-http3.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/algorithm/http-server/2-http3.cs b/bench/algorithm/http-server/2-http3.cs index 1d9cdd9ad..42e5c342d 100644 --- a/bench/algorithm/http-server/2-http3.cs +++ b/bench/algorithm/http-server/2-http3.cs @@ -89,7 +89,7 @@ private static async Task SendAsync(string api, int value) try { var response = await s_client.PostAsJsonAsync(api, payload).ConfigureAwait(false); - return int.Parse(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); + return int.Parse(await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false)); } catch (Exception e) {