From 08a8b387183e6e95535dad4598242456a3a5a7b7 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 25 Feb 2020 15:37:04 +1300 Subject: [PATCH] Eliminate allocation during HTTP2 path validation (#19273) --- .../Kestrel/Core/src/Internal/Http2/Http2Stream.cs | 13 +++++++++++-- .../Kestrel/Core/src/Internal/Http3/Http3Stream.cs | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index 0daad7a04d..7a6b75de8b 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -317,9 +317,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 try { + const int MaxPathBufferStackAllocSize = 256; + // The decoder operates only on raw bytes - var pathBuffer = new byte[pathSegment.Length].AsSpan(); - for (int i = 0; i < pathSegment.Length; i++) + Span pathBuffer = pathSegment.Length <= MaxPathBufferStackAllocSize + // A constant size plus slice generates better code + // https://github.com/dotnet/aspnetcore/pull/19273#discussion_r383159929 + ? stackalloc byte[MaxPathBufferStackAllocSize].Slice(0, pathSegment.Length) + // TODO - Consider pool here for less than 4096 + // https://github.com/dotnet/aspnetcore/pull/19273#discussion_r383604184 + : new byte[pathSegment.Length]; + + for (var i = 0; i < pathSegment.Length; i++) { var ch = pathSegment[i]; // The header parser should already be checking this diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs index 01c0234fe6..6518d00afd 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs @@ -691,9 +691,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3 try { + const int MaxPathBufferStackAllocSize = 256; + // The decoder operates only on raw bytes - var pathBuffer = new byte[pathSegment.Length].AsSpan(); - for (int i = 0; i < pathSegment.Length; i++) + Span pathBuffer = pathSegment.Length <= MaxPathBufferStackAllocSize + // A constant size plus slice generates better code + // https://github.com/dotnet/aspnetcore/pull/19273#discussion_r383159929 + ? stackalloc byte[MaxPathBufferStackAllocSize].Slice(0, pathSegment.Length) + // TODO - Consider pool here for less than 4096 + // https://github.com/dotnet/aspnetcore/pull/19273#discussion_r383604184 + : new byte[pathSegment.Length]; + + for (var i = 0; i < pathSegment.Length; i++) { var ch = pathSegment[i]; // The header parser should already be checking this