diff --git a/frameworks/CSharp/aspnetcore/README.md b/frameworks/CSharp/aspnetcore/README.md index 2e0ddeab3b4..00092de375d 100644 --- a/frameworks/CSharp/aspnetcore/README.md +++ b/frameworks/CSharp/aspnetcore/README.md @@ -6,5 +6,5 @@ See [.NET Core](http://dot.net) and [ASP.NET Core](https://github.com/dotnet/asp **Language** -* C# 8.0 +* C# 13.0 diff --git a/frameworks/CSharp/aspnetcore/benchmark_config.json b/frameworks/CSharp/aspnetcore/benchmark_config.json index 426f94ea5f7..831a9f27d81 100644 --- a/frameworks/CSharp/aspnetcore/benchmark_config.json +++ b/frameworks/CSharp/aspnetcore/benchmark_config.json @@ -30,6 +30,7 @@ "json_url": "/json", "db_url": "/db", "query_url": "/queries/", + "fortune_url": "/fortunes", "update_url": "/updates/", "cached_query_url": "/cached-worlds/", "port": 8080, diff --git a/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj b/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj index 01ed70de876..12f3446d8b7 100644 --- a/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj +++ b/frameworks/CSharp/aspnetcore/src/Minimal/Minimal.csproj @@ -9,7 +9,7 @@ - + diff --git a/frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj b/frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj index 83105ed34be..68c70002683 100644 --- a/frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj +++ b/frameworks/CSharp/aspnetcore/src/Mvc/Mvc.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Caching.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Caching.cs index eb5fff0199e..a99586c96e2 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Caching.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/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/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Fortunes.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Fortunes.cs index 52a0939559c..0279754a128 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Fortunes.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Fortunes.cs @@ -1,7 +1,7 @@ // 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. -#if !AOT +using System; using System.IO.Pipelines; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -9,7 +9,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private async Task FortunesRaw(PipeWriter pipeWriter) { @@ -19,7 +19,7 @@ await RawDb.LoadFortunesRows(), FortunesTemplateFactory); } - private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, SliceFactory templateFactory) + private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, Func> templateFactory) { // Render headers var preamble = """ @@ -39,7 +39,7 @@ private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, Sl // 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); + var renderTask = template.RenderAsync(chunkedWriter, HtmlEncoder); if (renderTask.IsCompletedSuccessfully) { @@ -51,18 +51,17 @@ private ValueTask OutputFortunes(PipeWriter pipeWriter, TModel model, Sl return AwaitTemplateRenderTask(renderTask, chunkedWriter, template); } - private static async ValueTask AwaitTemplateRenderTask(ValueTask renderTask, ChunkedBufferWriter chunkedWriter, RazorSlice template) + private static async ValueTask AwaitTemplateRenderTask(ValueTask renderTask, ChunkedPipeWriter chunkedWriter, RazorSlice template) { await renderTask; EndTemplateRendering(chunkedWriter, template); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void EndTemplateRendering(ChunkedBufferWriter chunkedWriter, RazorSlice template) + private static void EndTemplateRendering(ChunkedPipeWriter chunkedWriter, RazorSlice template) { - chunkedWriter.End(); + chunkedWriter.Complete(); ReturnChunkedWriter(chunkedWriter); template.Dispose(); } } -#endif diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.HttpConnection.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.HttpConnection.cs index cbd375cb61b..892ecc48457 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.HttpConnection.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.HttpConnection.cs @@ -12,7 +12,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication : IHttpConnection +public sealed partial class BenchmarkApplication : IHttpConnection { private State _state; @@ -193,15 +193,15 @@ 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 { diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Json.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Json.cs index 5babe2b61eb..dbfea6b0454 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Json.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Json.cs @@ -9,7 +9,7 @@ namespace PlatformBenchmarks; -public partial class BenchmarkApplication +public sealed partial class BenchmarkApplication { private readonly static uint _jsonPayloadSize = (uint)JsonSerializer.SerializeToUtf8Bytes( new JsonMessage { message = "Hello, World!" }, diff --git a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.MultipleQueries.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.MultipleQueries.cs index 0541e20de1f..82ea5a4a8a2 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.MultipleQueries.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/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/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Plaintext.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Plaintext.cs index 6e4c4eec1c8..57c90e64e92 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Plaintext.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Plaintext.cs @@ -7,7 +7,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/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.SingleQuery.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.SingleQuery.cs index 350eb43ad13..dd8add86fab 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.SingleQuery.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/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/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Updates.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Updates.cs index 6962aa0cf2c..66bfdba1858 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.Updates.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/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/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.cs b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.cs index e256e958c75..2db511586a3 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/BenchmarkApplication.cs @@ -10,9 +10,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.Extensions.ObjectPool; -#if !AOT +using Platform.Templates; using RazorSlices; -#endif namespace PlatformBenchmarks { @@ -42,26 +41,24 @@ public sealed partial class BenchmarkApplication public static RawDb RawDb { get; set; } - private static readonly DefaultObjectPool> ChunkedWriterPool + private static readonly DefaultObjectPool ChunkedWriterPool = new(new ChunkedWriterObjectPolicy()); - private sealed class ChunkedWriterObjectPolicy : IPooledObjectPolicy> + private sealed class ChunkedWriterObjectPolicy : IPooledObjectPolicy { - public ChunkedBufferWriter Create() => new(); + public ChunkedPipeWriter Create() => new(); - public bool Return(ChunkedBufferWriter writer) + public bool Return(ChunkedPipeWriter writer) { writer.Reset(); return true; } } -#if !AOT #if NPGSQL - private readonly static SliceFactory> FortunesTemplateFactory = RazorSlice.ResolveSliceFactory>("/Templates/FortunesUtf8.cshtml"); + private readonly static Func, RazorSlice>> FortunesTemplateFactory = FortunesUtf8.Create; #else - private readonly static SliceFactory> FortunesTemplateFactory = RazorSlice.ResolveSliceFactory>("/Templates/FortunesUtf16.cshtml"); -#endif + private readonly static Func, RazorSlice>> FortunesTemplateFactory = FortunesUtf16.Create; #endif [ThreadStatic] @@ -167,9 +164,7 @@ private bool ProcessRequest(ref BufferWriter writer) private Task ProcessRequestAsync() => _requestType switch { -#if !AOT RequestType.FortunesRaw => FortunesRaw(Writer), -#endif RequestType.SingleQuery => SingleQuery(Writer), RequestType.Caching => Caching(Writer, _queries), RequestType.Updates => Updates(Writer, _queries), diff --git a/frameworks/CSharp/aspnetcore/src/Platform/ChunkedBufferWriter.cs b/frameworks/CSharp/aspnetcore/src/Platform/ChunkedPipeWriter.cs similarity index 85% rename from frameworks/CSharp/aspnetcore/src/Platform/ChunkedBufferWriter.cs rename to frameworks/CSharp/aspnetcore/src/Platform/ChunkedPipeWriter.cs index 700519edb58..5a1dd80c678 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/ChunkedBufferWriter.cs +++ b/frameworks/CSharp/aspnetcore/src/Platform/ChunkedPipeWriter.cs @@ -5,35 +5,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; @@ -44,6 +49,7 @@ public void SetOutput(TWriter output, int chunkSizeHint = DefaultChunkSizeHint) public void Reset() { _buffered = 0; + _unflushedBytes = 0; _output = default; _ended = false; _hexFormat = DefaultHexFormat; @@ -51,16 +57,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(); @@ -71,9 +82,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(); @@ -82,6 +98,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/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj b/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj index fa716bda4c7..7db51dc3ec7 100644 --- a/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj +++ b/frameworks/CSharp/aspnetcore/src/Platform/Platform.csproj @@ -6,7 +6,6 @@ true preview 38063504-d08c-495a-89c9-daaad2f60f31 - AOT;$(DefineConstants) @@ -19,15 +18,12 @@ - + - + - - $(MSBuildThisFileDirectory)Templates/**;$(DefaultItemExcludes) -