Skip to content

Commit 348cc03

Browse files
authored
Fix some low hanging allocation fruit (#31115)
* Fix some low hanging allocation fruit - Use singleton pipe options - Use Task.Yield instead of AwaitableThreadPool on the server side - Change WebSocketMiddleware handshake helpers to remove enumerator allocations.
1 parent 746aeaf commit 348cc03

File tree

5 files changed

+54
-42
lines changed

5 files changed

+54
-42
lines changed

src/Middleware/WebSockets/src/HandshakeHelpers.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal static class HandshakeHelpers
1515
/// <summary>
1616
/// Gets request headers needed process the handshake on the server.
1717
/// </summary>
18-
public static readonly IEnumerable<string> NeededHeaders = new[]
18+
public static readonly string[] NeededHeaders = new[]
1919
{
2020
HeaderNames.Upgrade,
2121
HeaderNames.Connection,
@@ -34,7 +34,7 @@ internal static class HandshakeHelpers
3434
};
3535

3636
// Verify Method, Upgrade, Connection, version, key, etc..
37-
public static bool CheckSupportedWebSocketRequest(string method, IEnumerable<KeyValuePair<string, string>> headers)
37+
public static bool CheckSupportedWebSocketRequest(string method, List<KeyValuePair<string, string>> headers)
3838
{
3939
bool validUpgrade = false, validConnection = false, validKey = false, validVersion = false;
4040

src/SignalR/SignalR.slnf

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,72 @@
1-
{
1+
{
22
"solution": {
33
"path": "..\\..\\AspNetCore.sln",
4-
"projects" : [
5-
"src\\SignalR\\samples\\SignalRSamples\\SignalRSamples.csproj",
6-
"src\\SignalR\\samples\\SocialWeather\\SocialWeather.csproj",
7-
"src\\SignalR\\samples\\ClientSample\\ClientSample.csproj",
8-
"src\\SignalR\\samples\\WebSocketSample\\WebSocketSample.csproj",
9-
"src\\SignalR\\samples\\JwtClientSample\\JwtClientSample.csproj",
10-
"src\\SignalR\\clients\\ts\\FunctionalTests\\SignalR.Client.FunctionalTestApp.csproj",
4+
"projects": [
5+
"src\\DataProtection\\Abstractions\\src\\Microsoft.AspNetCore.DataProtection.Abstractions.csproj",
6+
"src\\DataProtection\\Cryptography.Internal\\src\\Microsoft.AspNetCore.Cryptography.Internal.csproj",
7+
"src\\DataProtection\\DataProtection\\src\\Microsoft.AspNetCore.DataProtection.csproj",
8+
"src\\FileProviders\\Embedded\\src\\Microsoft.Extensions.FileProviders.Embedded.csproj",
9+
"src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj",
1110
"src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj",
11+
"src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj",
1212
"src\\Hosting\\Server.IntegrationTesting\\src\\Microsoft.AspNetCore.Server.IntegrationTesting.csproj",
1313
"src\\Hosting\\TestHost\\src\\Microsoft.AspNetCore.TestHost.csproj",
14+
"src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj",
1415
"src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj",
16+
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
1517
"src\\Http\\Http.Abstractions\\src\\Microsoft.AspNetCore.Http.Abstractions.csproj",
18+
"src\\Http\\Http.Extensions\\src\\Microsoft.AspNetCore.Http.Extensions.csproj",
19+
"src\\Http\\Http.Features\\src\\Microsoft.AspNetCore.Http.Features.csproj",
1620
"src\\Http\\Http\\src\\Microsoft.AspNetCore.Http.csproj",
21+
"src\\Http\\Metadata\\src\\Microsoft.AspNetCore.Metadata.csproj",
22+
"src\\Http\\Routing.Abstractions\\src\\Microsoft.AspNetCore.Routing.Abstractions.csproj",
23+
"src\\Http\\Routing\\src\\Microsoft.AspNetCore.Routing.csproj",
24+
"src\\Http\\WebUtilities\\src\\Microsoft.AspNetCore.WebUtilities.csproj",
1725
"src\\Middleware\\CORS\\src\\Microsoft.AspNetCore.Cors.csproj",
26+
"src\\Middleware\\Diagnostics.Abstractions\\src\\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj",
1827
"src\\Middleware\\Diagnostics\\src\\Microsoft.AspNetCore.Diagnostics.csproj",
28+
"src\\Middleware\\HttpOverrides\\src\\Microsoft.AspNetCore.HttpOverrides.csproj",
1929
"src\\Middleware\\StaticFiles\\src\\Microsoft.AspNetCore.StaticFiles.csproj",
30+
"src\\Middleware\\WebSockets\\src\\Microsoft.AspNetCore.WebSockets.csproj",
2031
"src\\Security\\Authentication\\Cookies\\src\\Microsoft.AspNetCore.Authentication.Cookies.csproj",
2132
"src\\Security\\Authentication\\Core\\src\\Microsoft.AspNetCore.Authentication.csproj",
2233
"src\\Security\\Authentication\\JwtBearer\\src\\Microsoft.AspNetCore.Authentication.JwtBearer.csproj",
34+
"src\\Security\\Authorization\\Core\\src\\Microsoft.AspNetCore.Authorization.csproj",
35+
"src\\Security\\Authorization\\Policy\\src\\Microsoft.AspNetCore.Authorization.Policy.csproj",
36+
"src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj",
2337
"src\\Servers\\IIS\\IISIntegration\\src\\Microsoft.AspNetCore.Server.IISIntegration.csproj",
38+
"src\\Servers\\Kestrel\\Core\\src\\Microsoft.AspNetCore.Server.Kestrel.Core.csproj",
2439
"src\\Servers\\Kestrel\\Kestrel\\src\\Microsoft.AspNetCore.Server.Kestrel.csproj",
25-
"src\\SignalR\\samples\\JwtSample\\JwtSample.csproj",
40+
"src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj",
41+
"src\\SignalR\\clients\\csharp\\Client.Core\\src\\Microsoft.AspNetCore.SignalR.Client.Core.csproj",
42+
"src\\SignalR\\clients\\csharp\\Client\\src\\Microsoft.AspNetCore.SignalR.Client.csproj",
2643
"src\\SignalR\\clients\\csharp\\Client\\test\\FunctionalTests\\Microsoft.AspNetCore.SignalR.Client.FunctionalTests.csproj",
2744
"src\\SignalR\\clients\\csharp\\Client\\test\\UnitTests\\Microsoft.AspNetCore.SignalR.Client.Tests.csproj",
2845
"src\\SignalR\\clients\\csharp\\Http.Connections.Client\\src\\Microsoft.AspNetCore.Http.Connections.Client.csproj",
46+
"src\\SignalR\\clients\\ts\\FunctionalTests\\SignalR.Client.FunctionalTestApp.csproj",
2947
"src\\SignalR\\common\\Http.Connections.Common\\src\\Microsoft.AspNetCore.Http.Connections.Common.csproj",
3048
"src\\SignalR\\common\\Http.Connections\\src\\Microsoft.AspNetCore.Http.Connections.csproj",
3149
"src\\SignalR\\common\\Http.Connections\\test\\Microsoft.AspNetCore.Http.Connections.Tests.csproj",
50+
"src\\SignalR\\common\\Protocols.Json\\src\\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj",
3251
"src\\SignalR\\common\\Protocols.MessagePack\\src\\Microsoft.AspNetCore.SignalR.Protocols.MessagePack.csproj",
52+
"src\\SignalR\\common\\Protocols.NewtonsoftJson\\src\\Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson.csproj",
3353
"src\\SignalR\\common\\SignalR.Common\\src\\Microsoft.AspNetCore.SignalR.Common.csproj",
3454
"src\\SignalR\\common\\SignalR.Common\\test\\Microsoft.AspNetCore.SignalR.Common.Tests.csproj",
3555
"src\\SignalR\\common\\testassets\\Tests.Utils\\Microsoft.AspNetCore.SignalR.Tests.Utils.csproj",
3656
"src\\SignalR\\perf\\Microbenchmarks\\Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj",
57+
"src\\SignalR\\samples\\ClientSample\\ClientSample.csproj",
58+
"src\\SignalR\\samples\\JwtClientSample\\JwtClientSample.csproj",
59+
"src\\SignalR\\samples\\JwtSample\\JwtSample.csproj",
60+
"src\\SignalR\\samples\\SignalRSamples\\SignalRSamples.csproj",
61+
"src\\SignalR\\samples\\SocialWeather\\SocialWeather.csproj",
62+
"src\\SignalR\\samples\\WebSocketSample\\WebSocketSample.csproj",
3763
"src\\SignalR\\server\\Core\\src\\Microsoft.AspNetCore.SignalR.Core.csproj",
3864
"src\\SignalR\\server\\SignalR\\src\\Microsoft.AspNetCore.SignalR.csproj",
3965
"src\\SignalR\\server\\SignalR\\test\\Microsoft.AspNetCore.SignalR.Tests.csproj",
40-
"src\\SignalR\\common\\Protocols.NewtonsoftJson\\src\\Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson.csproj",
4166
"src\\SignalR\\server\\Specification.Tests\\src\\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj",
4267
"src\\SignalR\\server\\StackExchangeRedis\\src\\Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj",
4368
"src\\SignalR\\server\\StackExchangeRedis\\test\\Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj",
44-
"src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj",
45-
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
46-
"src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj",
47-
"src\\Http\\Http.Features\\src\\Microsoft.AspNetCore.Http.Features.csproj",
48-
"src\\Http\\Http.Extensions\\src\\Microsoft.AspNetCore.Http.Extensions.csproj",
49-
"src\\Security\\Authorization\\Core\\src\\Microsoft.AspNetCore.Authorization.csproj",
50-
"src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj",
51-
"src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj",
52-
"src\\Http\\WebUtilities\\src\\Microsoft.AspNetCore.WebUtilities.csproj",
53-
"src\\DataProtection\\DataProtection\\src\\Microsoft.AspNetCore.DataProtection.csproj",
54-
"src\\Middleware\\WebSockets\\src\\Microsoft.AspNetCore.WebSockets.csproj",
55-
"src\\Middleware\\Diagnostics.Abstractions\\src\\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj",
56-
"src\\DataProtection\\Cryptography.Internal\\src\\Microsoft.AspNetCore.Cryptography.Internal.csproj",
57-
"src\\DataProtection\\Abstractions\\src\\Microsoft.AspNetCore.DataProtection.Abstractions.csproj",
58-
"src\\Servers\\Kestrel\\Core\\src\\Microsoft.AspNetCore.Server.Kestrel.Core.csproj",
59-
"src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj",
60-
"src\\Http\\Routing\\src\\Microsoft.AspNetCore.Routing.csproj",
61-
"src\\Http\\Routing.Abstractions\\src\\Microsoft.AspNetCore.Routing.Abstractions.csproj",
62-
"src\\Security\\Authorization\\Policy\\src\\Microsoft.AspNetCore.Authorization.Policy.csproj",
63-
"src\\SignalR\\clients\\csharp\\Client\\src\\Microsoft.AspNetCore.SignalR.Client.csproj",
64-
"src\\SignalR\\clients\\csharp\\Client.Core\\src\\Microsoft.AspNetCore.SignalR.Client.Core.csproj",
65-
"src\\Middleware\\HttpOverrides\\src\\Microsoft.AspNetCore.HttpOverrides.csproj",
66-
"src\\SignalR\\common\\Protocols.Json\\src\\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj",
67-
"src\\Http\\Metadata\\src\\Microsoft.AspNetCore.Metadata.csproj",
68-
"src\\WebEncoders\\src\\Microsoft.Extensions.WebEncoders.csproj",
69-
"src\\FileProviders\\Embedded\\src\\Microsoft.Extensions.FileProviders.Embedded.csproj"
69+
"src\\WebEncoders\\src\\Microsoft.Extensions.WebEncoders.csproj"
7070
]
7171
}
72-
}
72+
}

src/SignalR/common/Http.Connections/src/HttpConnectionDispatcherOptions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System.Collections.Generic;
5+
using System.IO.Pipelines;
56
using Microsoft.AspNetCore.Authorization;
67

78
namespace Microsoft.AspNetCore.Http.Connections
@@ -15,6 +16,9 @@ public class HttpConnectionDispatcherOptions
1516
// There maybe the opportunity for performance gains by tuning this default.
1617
private const int DefaultPipeBufferSize = 32768;
1718

19+
private PipeOptions? _transportPipeOptions;
20+
private PipeOptions? _appPipeOptions;
21+
1822
/// <summary>
1923
/// Initializes a new instance of the <see cref="HttpConnectionDispatcherOptions"/> class.
2024
/// </summary>
@@ -63,5 +67,12 @@ public HttpConnectionDispatcherOptions()
6367
/// The default value is 0, the lowest possible protocol version.
6468
/// </summary>
6569
public int MinimumProtocolVersion { get; set; } = 0;
70+
71+
// We initialize these lazily based on the state of the options specified here.
72+
// Though these are mutable it's extremely rare that they would be mutated past the
73+
// call to initialize the routerware.
74+
internal PipeOptions TransportPipeOptions => _transportPipeOptions ??= new PipeOptions(pauseWriterThreshold: TransportMaxBufferSize, resumeWriterThreshold: TransportMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false);
75+
76+
internal PipeOptions AppPipeOptions => _appPipeOptions ??= new PipeOptions(pauseWriterThreshold: ApplicationMaxBufferSize, resumeWriterThreshold: ApplicationMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false);
6677
}
6778
}

src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ private async Task ExecuteApplication(ConnectionDelegate connectionDelegate)
542542

543543
// Jump onto the thread pool thread so blocking user code doesn't block the setup of the
544544
// connection and transport
545-
await AwaitableThreadPool.Yield();
545+
await Task.Yield();
546546

547547
// Running this in an async method turns sync exceptions into async ones
548548
await connectionDelegate(this);

src/SignalR/common/Http.Connections/src/Internal/HttpConnectionDispatcher.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,8 +718,9 @@ private static void CloneHttpContext(HttpContext context, HttpConnectionContext
718718

719719
private HttpConnectionContext CreateConnection(HttpConnectionDispatcherOptions options, int clientProtocolVersion = 0)
720720
{
721-
var transportPipeOptions = new PipeOptions(pauseWriterThreshold: options.TransportMaxBufferSize, resumeWriterThreshold: options.TransportMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false);
722-
var appPipeOptions = new PipeOptions(pauseWriterThreshold: options.ApplicationMaxBufferSize, resumeWriterThreshold: options.ApplicationMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false);
721+
var transportPipeOptions = options.TransportPipeOptions;
722+
var appPipeOptions = options.AppPipeOptions;
723+
723724
return _manager.CreateConnection(transportPipeOptions, appPipeOptions, clientProtocolVersion);
724725
}
725726

0 commit comments

Comments
 (0)