diff --git a/build/dependencies.props b/build/dependencies.props
index dd71368d5..890bf8b0e 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -42,7 +42,7 @@
10.0.0-*
2.1.35
- 0.7.0
+ 0.8.1
2.0.0-beta4.22272.1
0.2.0-alpha.24114.2
1.9.0
diff --git a/src/Benchmarks/Benchmarks.csproj b/src/Benchmarks/Benchmarks.csproj
index a54490711..329c6f15d 100644
--- a/src/Benchmarks/Benchmarks.csproj
+++ b/src/Benchmarks/Benchmarks.csproj
@@ -62,7 +62,7 @@
-
+
diff --git a/src/BenchmarksApps.sln b/src/BenchmarksApps.sln
index df1921ad0..22ed89b00 100644
--- a/src/BenchmarksApps.sln
+++ b/src/BenchmarksApps.sln
@@ -266,12 +266,12 @@ Global
{8DF3A6BB-E8C5-4FAA-839A-D185C9F93CD5} = {D8A014FB-3C99-4831-9FFB-F4A89A48D8BD}
{9E4AF835-2A78-4012-B0B5-9DA41ACDC716} = {D8A014FB-3C99-4831-9FFB-F4A89A48D8BD}
{20757830-EA66-4962-BDBB-A101A2062A2C} = {D8A014FB-3C99-4831-9FFB-F4A89A48D8BD}
- {07C0B18B-9738-4349-A8DF-3E88D3DF90AE} = {9E4AF835-2A78-4012-B0B5-9DA41ACDC716}
+ {07C0B18B-9738-4349-A8DF-3E88D3DF90AE} = {B6DB234C-8F80-4160-B95D-D70AFC444A3D}
{E68B58F8-40EA-49EA-A126-0B67F2BE7343} = {B6DB234C-8F80-4160-B95D-D70AFC444A3D}
{ACA43671-AD28-4F72-AAAB-6C32B388C2F0} = {B6DB234C-8F80-4160-B95D-D70AFC444A3D}
{D8F11F87-823F-4864-926D-5F66448A5C13} = {B6DB234C-8F80-4160-B95D-D70AFC444A3D}
{3D2573DE-CE7A-4CB8-A980-8C8636EE059E} = {398A40DA-FE1D-4B4D-A580-A33E29885553}
- {31B61CD7-4CF6-464F-B418-04C700A17CB9} = {6A69DE6C-07A6-4ABE-A4D2-0F983A33BBF8}
+ {31B61CD7-4CF6-464F-B418-04C700A17CB9} = {B6DB234C-8F80-4160-B95D-D70AFC444A3D}
{D6616E03-A2DA-4929-AD28-595ECC4C004D} = {B6DB234C-8F80-4160-B95D-D70AFC444A3D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
diff --git a/src/BenchmarksApps/DistributedCache/DistributedCache.csproj b/src/BenchmarksApps/DistributedCache/DistributedCache.csproj
index 0469e9620..561258d5d 100644
--- a/src/BenchmarksApps/DistributedCache/DistributedCache.csproj
+++ b/src/BenchmarksApps/DistributedCache/DistributedCache.csproj
@@ -1,6 +1,6 @@
-
+
- net7.0
+ net8.0
Exe
enable
enable
diff --git a/src/BenchmarksApps/Grpc/GrpcHttpApiServer/Server/Program.cs b/src/BenchmarksApps/Grpc/GrpcHttpApiServer/Server/Program.cs
index f0b85f6af..8fb22b55c 100644
--- a/src/BenchmarksApps/Grpc/GrpcHttpApiServer/Server/Program.cs
+++ b/src/BenchmarksApps/Grpc/GrpcHttpApiServer/Server/Program.cs
@@ -8,6 +8,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Console;
namespace Server
{
@@ -84,7 +85,10 @@ public static IHostBuilder CreateHostBuilder(string[] args)
if (Enum.TryParse(config["LogLevel"], out var logLevel))
{
Console.WriteLine($"Console Logging enabled with level '{logLevel}'");
- loggerFactory.AddConsole(o => o.TimestampFormat = "ss.ffff ").SetMinimumLevel(logLevel);
+ loggerFactory
+ .AddConsole()
+ .AddConsoleFormatter(o => o.TimestampFormat = "ss.ffff")
+ .SetMinimumLevel(logLevel);
}
})
.UseDefaultServiceProvider((context, options) =>
diff --git a/src/BenchmarksApps/Grpc/GrpcHttpApiServer/Server/Server.csproj b/src/BenchmarksApps/Grpc/GrpcHttpApiServer/Server/Server.csproj
index 49a4b9e2d..5a6683d22 100644
--- a/src/BenchmarksApps/Grpc/GrpcHttpApiServer/Server/Server.csproj
+++ b/src/BenchmarksApps/Grpc/GrpcHttpApiServer/Server/Server.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/src/BenchmarksApps/HelloWorldMvc/HelloWorldMvc.csproj b/src/BenchmarksApps/HelloWorldMvc/HelloWorldMvc.csproj
index 24f1b8ea7..acfe3326f 100644
--- a/src/BenchmarksApps/HelloWorldMvc/HelloWorldMvc.csproj
+++ b/src/BenchmarksApps/HelloWorldMvc/HelloWorldMvc.csproj
@@ -1,6 +1,6 @@
-
+
- net7.0
+ net8.0
Exe
\ No newline at end of file
diff --git a/src/BenchmarksApps/MapAction/MapAction.csproj b/src/BenchmarksApps/MapAction/MapAction.csproj
index cceeead16..c70916d37 100644
--- a/src/BenchmarksApps/MapAction/MapAction.csproj
+++ b/src/BenchmarksApps/MapAction/MapAction.csproj
@@ -1,6 +1,6 @@
-
+
- net7.0
+ net8.0
diff --git a/src/BenchmarksApps/SignalR/BenchmarkServer.csproj b/src/BenchmarksApps/SignalR/BenchmarkServer.csproj
index 60beb1ef5..578936fdc 100644
--- a/src/BenchmarksApps/SignalR/BenchmarkServer.csproj
+++ b/src/BenchmarksApps/SignalR/BenchmarkServer.csproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
diff --git a/src/BenchmarksApps/TechEmpower/BlazorSSR/BlazorSSR.csproj b/src/BenchmarksApps/TechEmpower/BlazorSSR/BlazorSSR.csproj
index 5407bdc2e..8c0fa575a 100644
--- a/src/BenchmarksApps/TechEmpower/BlazorSSR/BlazorSSR.csproj
+++ b/src/BenchmarksApps/TechEmpower/BlazorSSR/BlazorSSR.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/src/BenchmarksApps/TechEmpower/Minimal/Minimal.csproj b/src/BenchmarksApps/TechEmpower/Minimal/Minimal.csproj
index f03e6fb94..646892e45 100644
--- a/src/BenchmarksApps/TechEmpower/Minimal/Minimal.csproj
+++ b/src/BenchmarksApps/TechEmpower/Minimal/Minimal.csproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
enable
enable
latest
@@ -14,7 +14,7 @@
-
+
diff --git a/src/BenchmarksApps/TechEmpower/Minimal/Program.cs b/src/BenchmarksApps/TechEmpower/Minimal/Program.cs
index 287f7a035..9b0d160e4 100644
--- a/src/BenchmarksApps/TechEmpower/Minimal/Program.cs
+++ b/src/BenchmarksApps/TechEmpower/Minimal/Program.cs
@@ -5,6 +5,7 @@
using Minimal;
using Minimal.Database;
using Minimal.Models;
+using Minimal.Templates;
var builder = WebApplication.CreateBuilder(args);
@@ -37,12 +38,11 @@
app.MapGet("/db/result", async (Db db) => Results.Json(await db.LoadSingleQueryRow()));
-var createFortunesTemplate = RazorSlice.ResolveSliceFactory>("/Templates/Fortunes.cshtml");
var htmlEncoder = CreateHtmlEncoder();
app.MapGet("/fortunes", async (HttpContext context, Db db) => {
var fortunes = await db.LoadFortunesRows();
- var template = (RazorSliceHttpResult>)createFortunesTemplate(fortunes);
+ var template = (RazorSliceHttpResult>)Fortunes.Create(fortunes);
template.HtmlEncoder = htmlEncoder;
return template;
});
diff --git a/src/BenchmarksApps/TechEmpower/Mvc/Mvc.csproj b/src/BenchmarksApps/TechEmpower/Mvc/Mvc.csproj
index ea4a47118..f7b308ee1 100644
--- a/src/BenchmarksApps/TechEmpower/Mvc/Mvc.csproj
+++ b/src/BenchmarksApps/TechEmpower/Mvc/Mvc.csproj
@@ -11,17 +11,12 @@
-
-
-
-
-
-
+
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Caching.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Caching.cs
index 24896e59b..015125050 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Caching.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Caching.cs
@@ -6,7 +6,7 @@
namespace PlatformBenchmarks
{
- public partial class BenchmarkApplication
+ public sealed partial class BenchmarkApplication
{
private static async Task Caching(PipeWriter pipeWriter, int count)
{
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs
index 7f09d3e3c..5049e4630 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs
@@ -1,81 +1,81 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using RazorSlices;
-namespace PlatformBenchmarks
+namespace PlatformBenchmarks;
+
+public sealed partial class BenchmarkApplication
{
- public partial class BenchmarkApplication
- {
#if DATABASE
- private async Task FortunesRaw(PipeWriter pipeWriter)
- {
- await OutputFortunes(
- pipeWriter,
- await RawDb.LoadFortunesRows(),
- // To isolate template rendering from DB access, comment out the line above and uncomment the line below
- //await RawDb.LoadFortunesRowsNoDb(),
- FortunesTemplateFactory);
- }
-
- private async Task FortunesDapper(PipeWriter pipeWriter)
- {
- await OutputFortunes(pipeWriter, await DapperDb.LoadFortunesRows(), FortunesDapperTemplateFactory);
- }
-
- private async Task FortunesEf(PipeWriter pipeWriter)
- {
- await OutputFortunes(pipeWriter, await EfDb.LoadFortunesRows(), FortunesEfTemplateFactory);
- }
+ private async Task FortunesRaw(PipeWriter pipeWriter)
+ {
+ await OutputFortunes(
+ pipeWriter,
+ await RawDb.LoadFortunesRows(),
+ // To isolate template rendering from DB access, comment out the line above and uncomment the line below
+ //await RawDb.LoadFortunesRowsNoDb(),
+ Templates.FortunesUtf8.Create);
+ }
- private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, SliceFactory templateFactory)
- {
- // Render headers
- var preamble = """
- HTTP/1.1 200 OK
- Server: K
- Content-Type: text/html; charset=utf-8
- Transfer-Encoding: chunked
- """u8;
- var headersLength = preamble.Length + DateHeader.HeaderBytes.Length;
- var headersSpan = pipeWriter.GetSpan(headersLength);
- preamble.CopyTo(headersSpan);
- DateHeader.HeaderBytes.CopyTo(headersSpan[preamble.Length..]);
- pipeWriter.Advance(headersLength);
+ private async Task FortunesDapper(PipeWriter pipeWriter)
+ {
+ await OutputFortunes(pipeWriter, await DapperDb.LoadFortunesRows(), Templates.FortunesUtf8.Create);
+ }
- // Render body
- var template = templateFactory(model);
- // Kestrel PipeWriter span size is 4K, headers above already written to first span & template output is ~1350 bytes,
- // so 2K chunk size should result in only a single span and chunk being used.
- var chunkedWriter = GetChunkedWriter(pipeWriter, chunkSizeHint: 2048);
- var renderTask = template.RenderAsync(chunkedWriter, null, HtmlEncoder);
+ private async Task FortunesEf(PipeWriter pipeWriter)
+ {
+ await OutputFortunes(pipeWriter, await EfDb.LoadFortunesRows(), Templates.FortunesEf.Create);
+ }
- if (renderTask.IsCompletedSuccessfully)
- {
- renderTask.GetAwaiter().GetResult();
- EndTemplateRendering(chunkedWriter, template);
- return ValueTask.CompletedTask;
- }
+ private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, Func templateFactory)
+ {
+ // Render headers
+ var preamble = """
+ HTTP/1.1 200 OK
+ Server: K
+ Content-Type: text/html; charset=utf-8
+ Transfer-Encoding: chunked
+ """u8;
+ var headersLength = preamble.Length + DateHeader.HeaderBytes.Length;
+ var headersSpan = pipeWriter.GetSpan(headersLength);
+ preamble.CopyTo(headersSpan);
+ DateHeader.HeaderBytes.CopyTo(headersSpan[preamble.Length..]);
+ pipeWriter.Advance(headersLength);
- return AwaitTemplateRenderTask(renderTask, chunkedWriter, template);
- }
+ // Render body
+ var template = templateFactory(model);
+ // Kestrel PipeWriter span size is 4K, headers above already written to first span & template output is ~1350 bytes,
+ // so 2K chunk size should result in only a single span and chunk being used.
+ var chunkedWriter = GetChunkedWriter(pipeWriter, chunkSizeHint: 2048);
+ var renderTask = template.RenderAsync(chunkedWriter, HtmlEncoder);
- private static async ValueTask AwaitTemplateRenderTask(ValueTask renderTask, ChunkedBufferWriter chunkedWriter, RazorSlice template)
+ if (renderTask.IsCompletedSuccessfully)
{
- await renderTask;
+ renderTask.GetAwaiter().GetResult();
EndTemplateRendering(chunkedWriter, template);
+ return ValueTask.CompletedTask;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void EndTemplateRendering(ChunkedBufferWriter chunkedWriter, RazorSlice template)
- {
- chunkedWriter.End();
- ReturnChunkedWriter(chunkedWriter);
- template.Dispose();
- }
-#endif
+ return AwaitTemplateRenderTask(renderTask, chunkedWriter, template);
}
+
+ private static async ValueTask AwaitTemplateRenderTask(ValueTask renderTask, ChunkedPipeWriter chunkedWriter, RazorSlice template)
+ {
+ await renderTask;
+ EndTemplateRendering(chunkedWriter, template);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void EndTemplateRendering(ChunkedPipeWriter chunkedWriter, RazorSlice template)
+ {
+ chunkedWriter.Complete();
+ ReturnChunkedWriter(chunkedWriter);
+ template.Dispose();
+ }
+#endif
}
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs
index 11f6c782a..f9066761f 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs
@@ -13,7 +13,7 @@
namespace PlatformBenchmarks
{
- public partial class BenchmarkApplication : IHttpConnection
+ public sealed partial class BenchmarkApplication : IHttpConnection
{
private State _state;
@@ -258,54 +258,44 @@ private static BufferWriter GetWriter(PipeWriter pipeWriter, int
=> new(new(pipeWriter), sizeHint);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ChunkedBufferWriter GetChunkedWriter(PipeWriter pipeWriter, int chunkSizeHint)
+ private static ChunkedPipeWriter GetChunkedWriter(PipeWriter pipeWriter, int chunkSizeHint)
{
var writer = ChunkedWriterPool.Get();
- writer.SetOutput(new WriterAdapter(pipeWriter), chunkSizeHint);
+ writer.SetOutput(pipeWriter, chunkSizeHint);
return writer;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void ReturnChunkedWriter(ChunkedBufferWriter writer) => ChunkedWriterPool.Return(writer);
+ private static void ReturnChunkedWriter(ChunkedPipeWriter writer) => ChunkedWriterPool.Return(writer);
- private struct WriterAdapter : IBufferWriter
+ private readonly struct WriterAdapter(PipeWriter writer) : IBufferWriter
{
- public PipeWriter Writer;
+ public readonly void Advance(int count)
+ => writer.Advance(count);
- public WriterAdapter(PipeWriter writer)
- => Writer = writer;
+ public readonly Memory GetMemory(int sizeHint = 0)
+ => writer.GetMemory(sizeHint);
- public void Advance(int count)
- => Writer.Advance(count);
-
- public Memory GetMemory(int sizeHint = 0)
- => Writer.GetMemory(sizeHint);
-
- public Span GetSpan(int sizeHint = 0)
- => Writer.GetSpan(sizeHint);
+ public readonly Span GetSpan(int sizeHint = 0)
+ => writer.GetSpan(sizeHint);
}
- private struct ParsingAdapter : IHttpRequestLineHandler, IHttpHeadersHandler
+ private readonly struct ParsingAdapter(BenchmarkApplication requestHandler) : IHttpRequestLineHandler, IHttpHeadersHandler
{
- public BenchmarkApplication RequestHandler;
-
- public ParsingAdapter(BenchmarkApplication requestHandler)
- => RequestHandler = requestHandler;
-
- public void OnStaticIndexedHeader(int index)
- => RequestHandler.OnStaticIndexedHeader(index);
+ public readonly void OnStaticIndexedHeader(int index)
+ => requestHandler.OnStaticIndexedHeader(index);
- public void OnStaticIndexedHeader(int index, ReadOnlySpan value)
- => RequestHandler.OnStaticIndexedHeader(index, value);
+ public readonly void OnStaticIndexedHeader(int index, ReadOnlySpan value)
+ => requestHandler.OnStaticIndexedHeader(index, value);
- public void OnHeader(ReadOnlySpan name, ReadOnlySpan value)
- => RequestHandler.OnHeader(name, value);
+ public readonly void OnHeader(ReadOnlySpan name, ReadOnlySpan value)
+ => requestHandler.OnHeader(name, value);
- public void OnHeadersComplete(bool endStream)
- => RequestHandler.OnHeadersComplete(endStream);
+ public readonly void OnHeadersComplete(bool endStream)
+ => requestHandler.OnHeadersComplete(endStream);
- public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span startLine)
- => RequestHandler.OnStartLine(versionAndMethod, targetPath, startLine);
+ public readonly void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span startLine)
+ => requestHandler.OnStartLine(versionAndMethod, targetPath, startLine);
}
}
}
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Json.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Json.cs
index f196bba23..3d973bdb5 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Json.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Json.cs
@@ -7,7 +7,7 @@
namespace PlatformBenchmarks
{
- public partial class BenchmarkApplication
+ public sealed partial class BenchmarkApplication
{
#if !DATABASE
private static ReadOnlySpan _jsonPreamble =>
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs
index d7fd1bd12..961bfc249 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.MultipleQueries.cs
@@ -8,7 +8,7 @@
namespace PlatformBenchmarks
{
- public partial class BenchmarkApplication
+ public sealed partial class BenchmarkApplication
{
private static async Task MultipleQueries(PipeWriter pipeWriter, int count)
{
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Plaintext.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Plaintext.cs
index 5f9edb358..90c9fab48 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Plaintext.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Plaintext.cs
@@ -5,7 +5,7 @@
namespace PlatformBenchmarks
{
- public partial class BenchmarkApplication
+ public sealed partial class BenchmarkApplication
{
private static ReadOnlySpan _plaintextPreamble =>
"HTTP/1.1 200 OK\r\n"u8 +
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs
index 5c00d87c8..f273a8192 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.SingleQuery.cs
@@ -7,7 +7,7 @@
namespace PlatformBenchmarks
{
- public partial class BenchmarkApplication
+ public sealed partial class BenchmarkApplication
{
private static async Task SingleQuery(PipeWriter pipeWriter)
{
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Updates.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Updates.cs
index 2d5243649..d3cf8c561 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Updates.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.Updates.cs
@@ -7,7 +7,7 @@
namespace PlatformBenchmarks
{
- public partial class BenchmarkApplication
+ public sealed partial class BenchmarkApplication
{
private static async Task Updates(PipeWriter pipeWriter, int count)
{
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.cs
index cc50a9b6b..816d03a61 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/BenchmarkApplication.cs
@@ -10,216 +10,208 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.Extensions.ObjectPool;
-using RazorSlices;
-namespace PlatformBenchmarks
+namespace PlatformBenchmarks;
+
+public sealed partial class BenchmarkApplication
{
- public sealed partial class BenchmarkApplication
+ public static ReadOnlySpan ApplicationName => "Kestrel Platform-Level Application"u8;
+
+ private static ReadOnlySpan _crlf => "\r\n"u8;
+ private static ReadOnlySpan _eoh => "\r\n\r\n"u8; // End Of Headers
+ private static ReadOnlySpan _http11OK => "HTTP/1.1 200 OK\r\n"u8;
+ private static ReadOnlySpan _http11NotFound => "HTTP/1.1 404 Not Found\r\n"u8;
+ private static ReadOnlySpan _headerServer => "Server: K"u8;
+ private static ReadOnlySpan _headerContentLength => "Content-Length: "u8;
+ private static ReadOnlySpan _headerContentLengthZero => "Content-Length: 0"u8;
+ private static ReadOnlySpan _headerContentTypeText => "Content-Type: text/plain"u8;
+ private static ReadOnlySpan _headerContentTypeJson => "Content-Type: application/json"u8;
+ private static ReadOnlySpan _headerContentTypeHtml => "Content-Type: text/html; charset=UTF-8"u8;
+
+ private static ReadOnlySpan _dbPreamble =>
+ "HTTP/1.1 200 OK\r\n"u8 +
+ "Server: K\r\n"u8 +
+ "Content-Type: application/json\r\n"u8 +
+ "Content-Length: "u8;
+
+ private static ReadOnlySpan _plainTextBody => "Hello, World!"u8;
+ private static ReadOnlySpan _contentLengthGap => " "u8;
+
+ public static RawDb RawDb { get; set; }
+ public static DapperDb DapperDb { get; set; }
+ public static EfDb EfDb { get; set; }
+
+ private static readonly DefaultObjectPool ChunkedWriterPool
+ = new(new ChunkedWriterObjectPolicy());
+
+ private sealed class ChunkedWriterObjectPolicy : IPooledObjectPolicy
{
- public static ReadOnlySpan ApplicationName => "Kestrel Platform-Level Application"u8;
-
- private static ReadOnlySpan _crlf => "\r\n"u8;
- private static ReadOnlySpan _eoh => "\r\n\r\n"u8; // End Of Headers
- private static ReadOnlySpan _http11OK => "HTTP/1.1 200 OK\r\n"u8;
- private static ReadOnlySpan _http11NotFound => "HTTP/1.1 404 Not Found\r\n"u8;
- private static ReadOnlySpan _headerServer => "Server: K"u8;
- private static ReadOnlySpan _headerContentLength => "Content-Length: "u8;
- private static ReadOnlySpan _headerContentLengthZero => "Content-Length: 0"u8;
- private static ReadOnlySpan _headerContentTypeText => "Content-Type: text/plain"u8;
- private static ReadOnlySpan _headerContentTypeJson => "Content-Type: application/json"u8;
- private static ReadOnlySpan _headerContentTypeHtml => "Content-Type: text/html; charset=UTF-8"u8;
-
- private static ReadOnlySpan _dbPreamble =>
- "HTTP/1.1 200 OK\r\n"u8 +
- "Server: K\r\n"u8 +
- "Content-Type: application/json\r\n"u8 +
- "Content-Length: "u8;
-
- private static ReadOnlySpan _plainTextBody => "Hello, World!"u8;
- private static ReadOnlySpan _contentLengthGap => " "u8;
-
- public static RawDb RawDb { get; set; }
- public static DapperDb DapperDb { get; set; }
- public static EfDb EfDb { get; set; }
-
- private static readonly DefaultObjectPool> ChunkedWriterPool
- = new(new ChunkedWriterObjectPolicy());
-
- private sealed class ChunkedWriterObjectPolicy : IPooledObjectPolicy>
- {
- public ChunkedBufferWriter Create() => new();
+ public ChunkedPipeWriter Create() => new();
- public bool Return(ChunkedBufferWriter writer)
- {
- writer.Reset();
- return true;
- }
+ public bool Return(ChunkedPipeWriter writer)
+ {
+ writer.Reset();
+ return true;
}
+ }
-#if DATABASE
- private readonly static SliceFactory> FortunesTemplateFactory = RazorSlice.ResolveSliceFactory>("/Templates/FortunesUtf8.cshtml");
- private readonly static SliceFactory> FortunesDapperTemplateFactory = RazorSlice.ResolveSliceFactory>("/Templates/FortunesUtf16.cshtml");
- private readonly static SliceFactory> FortunesEfTemplateFactory = RazorSlice.ResolveSliceFactory>("/Templates/FortunesEf.cshtml");
-#endif
+ [ThreadStatic]
+ private static Utf8JsonWriter t_writer;
- [ThreadStatic]
- private static Utf8JsonWriter t_writer;
+ private static readonly JsonContext SerializerContext = JsonContext.Default;
- private static readonly JsonContext SerializerContext = JsonContext.Default;
+ [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)]
+ [JsonSerializable(typeof(JsonMessage))]
+ [JsonSerializable(typeof(CachedWorld[]))]
+ [JsonSerializable(typeof(World[]))]
+ private partial class JsonContext : JsonSerializerContext
+ {
+ }
- [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)]
- [JsonSerializable(typeof(JsonMessage))]
- [JsonSerializable(typeof(CachedWorld[]))]
- [JsonSerializable(typeof(World[]))]
- private partial class JsonContext : JsonSerializerContext
- {
- }
+ public static class Paths
+ {
+ public static ReadOnlySpan Json => "/json"u8;
+ public static ReadOnlySpan Plaintext => "/plaintext"u8;
+ public static ReadOnlySpan SingleQuery => "/db"u8;
+ public static ReadOnlySpan FortunesRaw => "/fortunes"u8;
+ public static ReadOnlySpan FortunesDapper => "/fortunes/dapper"u8;
+ public static ReadOnlySpan FortunesEf => "/fortunes/ef"u8;
+ public static ReadOnlySpan Updates => "/updates/"u8;
+ public static ReadOnlySpan MultipleQueries => "/queries/"u8;
+ public static ReadOnlySpan Caching => "/cached-worlds/"u8;
+ }
- public static class Paths
- {
- public static ReadOnlySpan Json => "/json"u8;
- public static ReadOnlySpan Plaintext => "/plaintext"u8;
- public static ReadOnlySpan SingleQuery => "/db"u8;
- public static ReadOnlySpan FortunesRaw => "/fortunes"u8;
- public static ReadOnlySpan FortunesDapper => "/fortunes/dapper"u8;
- public static ReadOnlySpan FortunesEf => "/fortunes/ef"u8;
- public static ReadOnlySpan Updates => "/updates/"u8;
- public static ReadOnlySpan MultipleQueries => "/queries/"u8;
- public static ReadOnlySpan Caching => "/cached-worlds/"u8;
- }
+ private RequestType _requestType;
+ private int _queries;
- private RequestType _requestType;
- private int _queries;
+ public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span startLine)
+ {
+ _requestType = versionAndMethod.Method == HttpMethod.Get ? GetRequestType(startLine.Slice(targetPath.Offset, targetPath.Length), ref _queries) : RequestType.NotRecognized;
+ }
- public void OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span startLine)
+ private static RequestType GetRequestType(ReadOnlySpan path, ref int queries)
+ {
+#if !DATABASE
+ if (path.Length == 10 && path.SequenceEqual(Paths.Plaintext))
{
- _requestType = versionAndMethod.Method == HttpMethod.Get ? GetRequestType(startLine.Slice(targetPath.Offset, targetPath.Length), ref _queries) : RequestType.NotRecognized;
+ return RequestType.PlainText;
}
-
- private static RequestType GetRequestType(ReadOnlySpan path, ref int queries)
+ else if (path.Length == 5 && path.SequenceEqual(Paths.Json))
{
-#if !DATABASE
- if (path.Length == 10 && path.SequenceEqual(Paths.Plaintext))
- {
- return RequestType.PlainText;
- }
- else if (path.Length == 5 && path.SequenceEqual(Paths.Json))
- {
- return RequestType.Json;
- }
+ return RequestType.Json;
+ }
#else
- if (path.Length == 3 && path[0] == '/' && path[1] == 'd' && path[2] == 'b')
- {
- return RequestType.SingleQuery;
- }
- if (path[1] == 'f')
- {
- return path.Length switch
- {
- 9 when path.SequenceEqual(Paths.FortunesRaw) => RequestType.FortunesRaw,
- 16 when path.SequenceEqual(Paths.FortunesDapper) => RequestType.FortunesDapper,
- 12 when path.SequenceEqual(Paths.FortunesEf) => RequestType.FortunesEf,
- _ => RequestType.NotRecognized
- };
- }
- if (path.Length >= 15 && path[1] == 'c' && path.StartsWith(Paths.Caching))
- {
- queries = ParseQueries(path.Slice(15));
- return RequestType.Caching;
- }
- if (path.Length >= 9 && path[1] == 'u' && path.StartsWith(Paths.Updates))
- {
- queries = ParseQueries(path.Slice(9));
- return RequestType.Updates;
- }
- if (path.Length >= 9 && path[1] == 'q' && path.StartsWith(Paths.MultipleQueries))
+ if (path.Length == 3 && path[0] == '/' && path[1] == 'd' && path[2] == 'b')
+ {
+ return RequestType.SingleQuery;
+ }
+ if (path[1] == 'f')
+ {
+ return path.Length switch
{
- queries = ParseQueries(path.Slice(9));
- return RequestType.MultipleQueries;
- }
-#endif
- return RequestType.NotRecognized;
+ 9 when path.SequenceEqual(Paths.FortunesRaw) => RequestType.FortunesRaw,
+ 16 when path.SequenceEqual(Paths.FortunesDapper) => RequestType.FortunesDapper,
+ 12 when path.SequenceEqual(Paths.FortunesEf) => RequestType.FortunesEf,
+ _ => RequestType.NotRecognized
+ };
+ }
+ if (path.Length >= 15 && path[1] == 'c' && path.StartsWith(Paths.Caching))
+ {
+ queries = ParseQueries(path.Slice(15));
+ return RequestType.Caching;
+ }
+ if (path.Length >= 9 && path[1] == 'u' && path.StartsWith(Paths.Updates))
+ {
+ queries = ParseQueries(path.Slice(9));
+ return RequestType.Updates;
}
+ if (path.Length >= 9 && path[1] == 'q' && path.StartsWith(Paths.MultipleQueries))
+ {
+ queries = ParseQueries(path.Slice(9));
+ return RequestType.MultipleQueries;
+ }
+#endif
+ return RequestType.NotRecognized;
+ }
#if !DATABASE
- private void ProcessRequest(ref BufferWriter writer)
+ private void ProcessRequest(ref BufferWriter writer)
+ {
+ if (_requestType == RequestType.PlainText)
{
- if (_requestType == RequestType.PlainText)
- {
- PlainText(ref writer);
- }
- else if (_requestType == RequestType.Json)
- {
- Json(ref writer, Writer);
- }
- else
- {
- Default(ref writer);
- }
+ PlainText(ref writer);
}
-#else
-
- private static int ParseQueries(ReadOnlySpan parameter)
+ else if (_requestType == RequestType.Json)
{
- if (!Utf8Parser.TryParse(parameter, out int queries, out _))
- {
- queries = 1;
- }
- else
- {
- queries = Math.Clamp(queries, 1, 500);
- }
-
- return queries;
+ Json(ref writer, Writer);
}
-
- private Task ProcessRequestAsync() => _requestType switch
- {
- RequestType.FortunesRaw => FortunesRaw(Writer),
- RequestType.FortunesDapper => FortunesDapper(Writer),
- RequestType.FortunesEf => FortunesEf(Writer),
- RequestType.SingleQuery => SingleQuery(Writer),
- RequestType.Caching => Caching(Writer, _queries),
- RequestType.Updates => Updates(Writer, _queries),
- RequestType.MultipleQueries => MultipleQueries(Writer, _queries),
- _ => Default(Writer)
- };
-
- private static Task Default(PipeWriter pipeWriter)
+ else
{
- var writer = GetWriter(pipeWriter, sizeHint: _defaultPreamble.Length + DateHeader.HeaderBytes.Length);
Default(ref writer);
- writer.Commit();
- return Task.CompletedTask;
}
-#endif
- private static ReadOnlySpan _defaultPreamble =>
- "HTTP/1.1 200 OK\r\n"u8 +
- "Server: K"u8 + "\r\n"u8 +
- "Content-Type: text/plain"u8 +
- "Content-Length: 0"u8;
+ }
+#else
- private static void Default(ref BufferWriter writer)
+ private static int ParseQueries(ReadOnlySpan parameter)
+ {
+ if (!Utf8Parser.TryParse(parameter, out int queries, out _))
{
- writer.Write(_defaultPreamble);
-
- // Date header
- writer.Write(DateHeader.HeaderBytes);
+ queries = 1;
}
-
- private enum RequestType
+ else
{
- NotRecognized,
- PlainText,
- Json,
- FortunesRaw,
- FortunesDapper,
- FortunesEf,
- SingleQuery,
- Caching,
- Updates,
- MultipleQueries
+ queries = Math.Clamp(queries, 1, 500);
}
+
+ return queries;
+ }
+
+ private Task ProcessRequestAsync() => _requestType switch
+ {
+ RequestType.FortunesRaw => FortunesRaw(Writer),
+ RequestType.FortunesDapper => FortunesDapper(Writer),
+ RequestType.FortunesEf => FortunesEf(Writer),
+ RequestType.SingleQuery => SingleQuery(Writer),
+ RequestType.Caching => Caching(Writer, _queries),
+ RequestType.Updates => Updates(Writer, _queries),
+ RequestType.MultipleQueries => MultipleQueries(Writer, _queries),
+ _ => Default(Writer)
+ };
+
+ private static Task Default(PipeWriter pipeWriter)
+ {
+ var writer = GetWriter(pipeWriter, sizeHint: _defaultPreamble.Length + DateHeader.HeaderBytes.Length);
+ Default(ref writer);
+ writer.Commit();
+ return Task.CompletedTask;
+ }
+#endif
+ private static ReadOnlySpan _defaultPreamble =>
+ "HTTP/1.1 200 OK\r\n"u8 +
+ "Server: K"u8 + "\r\n"u8 +
+ "Content-Type: text/plain"u8 +
+ "Content-Length: 0"u8;
+
+ private static void Default(ref BufferWriter writer)
+ {
+ writer.Write(_defaultPreamble);
+
+ // Date header
+ writer.Write(DateHeader.HeaderBytes);
+ }
+
+ private enum RequestType
+ {
+ NotRecognized,
+ PlainText,
+ Json,
+ FortunesRaw,
+ FortunesDapper,
+ FortunesEf,
+ SingleQuery,
+ Caching,
+ Updates,
+ MultipleQueries
}
}
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/ChunkedBufferWriter.cs b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/ChunkedPipeWriter.cs
similarity index 85%
rename from src/BenchmarksApps/TechEmpower/PlatformBenchmarks/ChunkedBufferWriter.cs
rename to src/BenchmarksApps/TechEmpower/PlatformBenchmarks/ChunkedPipeWriter.cs
index 66c74b964..5f6a378f7 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/ChunkedBufferWriter.cs
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/ChunkedPipeWriter.cs
@@ -2,35 +2,40 @@
using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
+using System.IO.Pipelines;
using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
namespace PlatformBenchmarks;
-internal sealed class ChunkedBufferWriter : IBufferWriter where TWriter : IBufferWriter
+internal sealed class ChunkedPipeWriter : PipeWriter
{
private const int DefaultChunkSizeHint = 2048;
private static readonly StandardFormat DefaultHexFormat = GetHexFormat(DefaultChunkSizeHint);
private static ReadOnlySpan ChunkTerminator => "\r\n"u8;
- private TWriter _output;
+ private PipeWriter _output;
private int _chunkSizeHint;
private StandardFormat _hexFormat = DefaultHexFormat;
private Memory _currentFullChunk;
private Memory _currentChunk;
private int _buffered;
+ private long _unflushedBytes;
private bool _ended = false;
public Memory Memory => _currentChunk;
- public TWriter Output => _output;
+ public PipeWriter Output => _output;
public int Buffered => _buffered;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void SetOutput(TWriter output, int chunkSizeHint = DefaultChunkSizeHint)
+ public void SetOutput(PipeWriter output, int chunkSizeHint = DefaultChunkSizeHint)
{
_buffered = 0;
+ _unflushedBytes = 0;
_chunkSizeHint = chunkSizeHint;
_output = output;
@@ -41,6 +46,7 @@ public void SetOutput(TWriter output, int chunkSizeHint = DefaultChunkSizeHint)
public void Reset()
{
_buffered = 0;
+ _unflushedBytes = 0;
_output = default;
_ended = false;
_hexFormat = DefaultHexFormat;
@@ -48,16 +54,21 @@ public void Reset()
_currentChunk = default;
}
+ public override bool CanGetUnflushedBytes => true;
+
+ public override long UnflushedBytes => _unflushedBytes;
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Advance(int count)
+ public override void Advance(int count)
{
ThrowIfEnded();
_buffered += count;
+ _unflushedBytes += count;
_currentChunk = _currentChunk[count..];
}
- public Memory GetMemory(int sizeHint = 0)
+ public override Memory GetMemory(int sizeHint = 0)
{
ThrowIfEnded();
@@ -68,9 +79,14 @@ public Memory GetMemory(int sizeHint = 0)
return _currentChunk;
}
- public Span GetSpan(int sizeHint = 0) => GetMemory(sizeHint).Span;
+ public override Span GetSpan(int sizeHint = 0) => GetMemory(sizeHint).Span;
+
+ public override void CancelPendingFlush()
+ {
+ _output.CancelPendingFlush();
+ }
- public void End()
+ public override void Complete(Exception exception = null)
{
ThrowIfEnded();
@@ -79,6 +95,17 @@ public void End()
_ended = true;
}
+ public override ValueTask FlushAsync(CancellationToken cancellationToken = default)
+ {
+ CommitCurrentChunk(isFinal: false);
+
+ var flushTask = _output.FlushAsync(cancellationToken);
+
+ _unflushedBytes = 0;
+
+ return flushTask;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static StandardFormat GetHexFormat(int maxValue)
{
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/PlatformBenchmarks.csproj b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/PlatformBenchmarks.csproj
index 0a5159f33..d7c3442de 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/PlatformBenchmarks.csproj
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/PlatformBenchmarks.csproj
@@ -1,7 +1,7 @@
- net7.0;net8.0
+ net8.0;net9.0
Exe
true
true
@@ -22,19 +22,14 @@
-
-
-
-
-
-
+
-
+
diff --git a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/Properties/launchSettings.json b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/Properties/launchSettings.json
index 89d1445f7..b6dbfb86f 100644
--- a/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/Properties/launchSettings.json
+++ b/src/BenchmarksApps/TechEmpower/PlatformBenchmarks/Properties/launchSettings.json
@@ -7,7 +7,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
- "applicationUrl": "https://localhost:53705;http://localhost:53706"
+ "applicationUrl": "http://localhost:53705;http://localhost:53706"
}
}
}
\ No newline at end of file
diff --git a/src/BenchmarksApps/TechEmpower/RazorPages/RazorPages.csproj b/src/BenchmarksApps/TechEmpower/RazorPages/RazorPages.csproj
index 92f3d4e14..a72c2b522 100644
--- a/src/BenchmarksApps/TechEmpower/RazorPages/RazorPages.csproj
+++ b/src/BenchmarksApps/TechEmpower/RazorPages/RazorPages.csproj
@@ -1,7 +1,7 @@
-
+
- net7.0;net8.0
+ net8.0;net9.0
enable
enable
preview
@@ -12,17 +12,12 @@
-
-
-
-
-
-
+
diff --git a/src/BenchmarksApps/TodosApi/TodosApi.csproj b/src/BenchmarksApps/TodosApi/TodosApi.csproj
index 47d7a541e..fcc66b17b 100644
--- a/src/BenchmarksApps/TodosApi/TodosApi.csproj
+++ b/src/BenchmarksApps/TodosApi/TodosApi.csproj
@@ -14,7 +14,6 @@
-
@@ -28,12 +27,16 @@
-
+
+
+
+
+
-
+
-
+
diff --git a/src/Downstream/Downstream.csproj b/src/Downstream/Downstream.csproj
index 3f208e3b5..4f9c3c8bc 100644
--- a/src/Downstream/Downstream.csproj
+++ b/src/Downstream/Downstream.csproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
latest
diff --git a/src/TcpClient/TcpClient.csproj b/src/TcpClient/TcpClient.csproj
index 0f02f80f0..cef235c61 100644
--- a/src/TcpClient/TcpClient.csproj
+++ b/src/TcpClient/TcpClient.csproj
@@ -2,7 +2,7 @@
Exe
- net7.0
+ net8.0