Repository navigation
Conversation
BufferExtensions.WriteNumeric inlines values < 1000, but larger values (most real Content-Length values) went through a [ThreadStatic] scratch array, a div/mod-by-10 loop and a span copy. Format directly into the output span with ulong.TryFormat (UTF-8) instead, and only fall back to a stackalloc'd temporary buffer when the value must be split across spans. Adds BufferWriterWriteNumericBenchmark / BufferWriterWriteAsciiBenchmark. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The required frozen review bundle was not supplied, preventing validation of revision provenance, routed guidance completeness, and existing-feedback deduplication.
No open issues 🎉
What changed in this PR
This PR optimizes Kestrel’s HTTP/1.1 Content-Length formatting for values ≥ 1000 while retaining the existing inline path for smaller values.
Changes:
- Replaces thread-local scratch storage and manual digit conversion with direct UTF-8
ulong.TryFormat, using a stack-allocated fallback across span boundaries. - Adds
scopedspan parameters soBufferWritercan accept stack-allocated input. - Adds numeric and ASCII-writing microbenchmarks.
| File | Description |
|---|---|
| src/Shared/ServerInfrastructure/BufferWriter.cs | Marks span inputs as scoped to support stack-allocated buffers. |
| src/Shared/ServerInfrastructure/BufferExtensions.cs | Uses direct numeric formatting with a separate cross-span fallback. |
| src/Servers/Kestrel/perf/Microbenchmarks/BufferWriterExtensionsBenchmark.cs | Adds benchmarks for numeric and ASCII writes. |
Review effort: Balanced
|
/azp run aspnetcore-ci The only CI failure is 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
No pipelines are associated with this pull request. |
|
/azp run 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
Command 'run
**🤖' is not supported by Azure Pipelines.
See additional documentation. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run aspnetcore-components-e2e The only failure is 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
No pipelines are associated with this pull request. |
|
CI status: I pushed an empty commit to retrigger CI. On that run, the Windows Server x64 platform tests that failed last time ( The only failure on this run is 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
… leg) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Pushed an empty commit to retrigger CI. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
…buffer Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/review |
Use ulong.TryFormat for Content-Length values >= 1000 in Kestrel
Faster HTTP/1.1 Content-Length header writing for values >= 1000
Description
BufferExtensions.WriteNumeric(used byHttpResponseHeaders.CopyToFastto writeContent-Length) handles values < 1000 inline, but anything larger, which covers most real response bodies, went throughWriteNumericMultiWrite. That path reads a[ThreadStatic]scratch array, runs a div/mod-by-10 loop, and then copies the digits into the output.This change formats directly into the output span with
ulong.TryFormat(UTF-8, invariant). Only when the remaining span is too small does it fall back to formatting into the existing[ThreadStatic]scratch array (unchanged frommain) and then callingBufferWriter.Write. That fallback lives in its own non-inlined method, and it keeps the existing behavior of filling the tail of the current span. The change adds nounsafecode,stackalloc,Unsafe/MemoryMarshalor custom vectorization; it uses only the BCLTryFormatAPI.BufferWriter.csis untouched.No public API changes (no
PublicAPI.*.txtedits) and no behavior change: the output bytes are identical tomainfor every value.I also added
BufferWriterWriteNumericBenchmark(and aBufferWriterWriteAsciiBenchmarkcompanion) to Kestrel's Microbenchmarks.Benchmark
BufferWriterWriteNumericBenchmark.WriteNumeric(BDN InProcess, 2 vCPU Linux VM, 3 interleaved base/PR rounds, 1000 ops per invoke; re-measured after removingstackalloc):No allocations before or after.
Tests
Microsoft.AspNetCore.Server.Kestrel.Core.Tests: 9907 passed, 13 failed. All 13 failures are the AddressBinder/KestrelServerTests IPv6 tests, which fail the same way onmainin this environment. The existingPipelineExtensionTests(WritesNumericToAscii,WritesNumericToAsciiFastPath,WritesNumericAcrossSpanBoundaries) pass. After the stackalloc removal, I re-ranPipelineExtensionTests|HttpResponseHeadersTests|ChunkWriterTests: 200 passed.🤖 Posted by ASP.NET Core Perf 2 (AI Agent)