Copy Kestrel perf changes (#7480)

This commit is contained in:
BrennanConroy 2019-02-13 16:31:37 -08:00 committed by GitHub
parent d1aa53721b
commit 9de42d516e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 63 additions and 28 deletions

View File

@ -13,6 +13,7 @@
<Compile Include="$(SignalRSharedSourceRoot)PipeWriterStream.cs" Link="PipeWriterStream.cs" /> <Compile Include="$(SignalRSharedSourceRoot)PipeWriterStream.cs" Link="PipeWriterStream.cs" />
<Compile Include="$(SignalRSharedSourceRoot)ReflectionHelper.cs" Link="ReflectionHelper.cs" /> <Compile Include="$(SignalRSharedSourceRoot)ReflectionHelper.cs" Link="ReflectionHelper.cs" />
<Compile Include="$(SignalRSharedSourceRoot)TimerAwaitable.cs" Link="Internal\TimerAwaitable.cs" /> <Compile Include="$(SignalRSharedSourceRoot)TimerAwaitable.cs" Link="Internal\TimerAwaitable.cs" />
<Compile Include="$(SignalRSharedSourceRoot)ValueTaskExtensions.cs" Link="Internal\ValueTaskExtensions.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -15,8 +15,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
private const byte ByteLF = (byte)'\n'; private const byte ByteLF = (byte)'\n';
private const byte ByteColon = (byte)':'; private const byte ByteColon = (byte)':';
private static readonly byte[] _dataPrefix = Encoding.UTF8.GetBytes("data: "); private static ReadOnlySpan<byte> _dataPrefix => new byte[] { (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)':', (byte)' ' };
private static readonly byte[] _sseLineEnding = Encoding.UTF8.GetBytes("\r\n"); private static ReadOnlySpan<byte> _sseLineEnding => new byte[] { (byte)'\r', (byte)'\n' };
private static readonly byte[] _newLine = Encoding.UTF8.GetBytes(Environment.NewLine); private static readonly byte[] _newLine = Encoding.UTF8.GetBytes(Environment.NewLine);
private InternalParseState _internalParserState = InternalParseState.ReadMessagePayload; private InternalParseState _internalParserState = InternalParseState.ReadMessagePayload;

View File

@ -13,24 +13,25 @@ namespace Microsoft.AspNetCore.Http.Connections
{ {
public static class NegotiateProtocol public static class NegotiateProtocol
{ {
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
private const string ConnectionIdPropertyName = "connectionId"; private const string ConnectionIdPropertyName = "connectionId";
private static readonly byte[] ConnectionIdPropertyNameBytes = Encoding.UTF8.GetBytes(ConnectionIdPropertyName); private static ReadOnlySpan<byte> ConnectionIdPropertyNameBytes => new byte[] { (byte)'c', (byte)'o', (byte)'n', (byte)'n', (byte)'e', (byte)'c', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'I', (byte)'d' };
private const string UrlPropertyName = "url"; private const string UrlPropertyName = "url";
private static readonly byte[] UrlPropertyNameBytes = Encoding.UTF8.GetBytes(UrlPropertyName); private static ReadOnlySpan<byte> UrlPropertyNameBytes => new byte[] { (byte)'u', (byte)'r', (byte)'l' };
private const string AccessTokenPropertyName = "accessToken"; private const string AccessTokenPropertyName = "accessToken";
private static readonly byte[] AccessTokenPropertyNameBytes = Encoding.UTF8.GetBytes(AccessTokenPropertyName); private static ReadOnlySpan<byte> AccessTokenPropertyNameBytes => new byte[] { (byte)'a', (byte)'c', (byte)'c', (byte)'e', (byte)'s', (byte)'s', (byte)'T', (byte)'o', (byte)'k', (byte)'e', (byte)'n' };
private const string AvailableTransportsPropertyName = "availableTransports"; private const string AvailableTransportsPropertyName = "availableTransports";
private static readonly byte[] AvailableTransportsPropertyNameBytes = Encoding.UTF8.GetBytes(AvailableTransportsPropertyName); private static ReadOnlySpan<byte> AvailableTransportsPropertyNameBytes => new byte[] { (byte)'a', (byte)'v', (byte)'a', (byte)'i', (byte)'l', (byte)'a', (byte)'b', (byte)'l', (byte)'e', (byte)'T', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'p', (byte)'o', (byte)'r', (byte)'t', (byte)'s' };
private const string TransportPropertyName = "transport"; private const string TransportPropertyName = "transport";
private static readonly byte[] TransportPropertyNameBytes = Encoding.UTF8.GetBytes(TransportPropertyName); private static ReadOnlySpan<byte> TransportPropertyNameBytes => new byte[] { (byte)'t', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'p', (byte)'o', (byte)'r', (byte)'t' };
private const string TransferFormatsPropertyName = "transferFormats"; private const string TransferFormatsPropertyName = "transferFormats";
private static readonly byte[] TransferFormatsPropertyNameBytes = Encoding.UTF8.GetBytes(TransferFormatsPropertyName); private static ReadOnlySpan<byte> TransferFormatsPropertyNameBytes => new byte[] { (byte)'t', (byte)'r', (byte)'a', (byte)'n', (byte)'s', (byte)'f', (byte)'e', (byte)'r', (byte)'F', (byte)'o', (byte)'r', (byte)'m', (byte)'a', (byte)'t', (byte)'s' };
private const string ErrorPropertyName = "error"; private const string ErrorPropertyName = "error";
private static readonly byte[] ErrorPropertyNameBytes = Encoding.UTF8.GetBytes(ErrorPropertyName); private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
// Used to detect ASP.NET SignalR Server connection attempt // Used to detect ASP.NET SignalR Server connection attempt
private const string ProtocolVersionPropertyName = "ProtocolVersion"; private const string ProtocolVersionPropertyName = "ProtocolVersion";
private static readonly byte[] ProtocolVersionPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolVersionPropertyName); private static ReadOnlySpan<byte> ProtocolVersionPropertyNameBytes => new byte[] { (byte)'P', (byte)'r', (byte)'o', (byte)'t', (byte)'o', (byte)'c', (byte)'o', (byte)'l', (byte)'V', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' };
public static void WriteResponse(NegotiationResponse response, IBufferWriter<byte> output) public static void WriteResponse(NegotiationResponse response, IBufferWriter<byte> output)
{ {
@ -114,15 +115,15 @@ namespace Microsoft.AspNetCore.Http.Connections
if (memberName.SequenceEqual(UrlPropertyNameBytes)) if (memberName.SequenceEqual(UrlPropertyNameBytes))
{ {
url = reader.ReadAsString(UrlPropertyNameBytes); url = reader.ReadAsString(UrlPropertyName);
} }
else if (memberName.SequenceEqual(AccessTokenPropertyNameBytes)) else if (memberName.SequenceEqual(AccessTokenPropertyNameBytes))
{ {
accessToken = reader.ReadAsString(AccessTokenPropertyNameBytes); accessToken = reader.ReadAsString(AccessTokenPropertyName);
} }
else if (memberName.SequenceEqual(ConnectionIdPropertyNameBytes)) else if (memberName.SequenceEqual(ConnectionIdPropertyNameBytes))
{ {
connectionId = reader.ReadAsString(ConnectionIdPropertyNameBytes); connectionId = reader.ReadAsString(ConnectionIdPropertyName);
} }
else if (memberName.SequenceEqual(AvailableTransportsPropertyNameBytes)) else if (memberName.SequenceEqual(AvailableTransportsPropertyNameBytes))
{ {
@ -144,7 +145,7 @@ namespace Microsoft.AspNetCore.Http.Connections
} }
else if (memberName.SequenceEqual(ErrorPropertyNameBytes)) else if (memberName.SequenceEqual(ErrorPropertyNameBytes))
{ {
error = reader.ReadAsString(ErrorPropertyNameBytes); error = reader.ReadAsString(ErrorPropertyName);
} }
else if (memberName.SequenceEqual(ProtocolVersionPropertyNameBytes)) else if (memberName.SequenceEqual(ProtocolVersionPropertyNameBytes))
{ {
@ -215,7 +216,7 @@ namespace Microsoft.AspNetCore.Http.Connections
if (memberName.SequenceEqual(TransportPropertyNameBytes)) if (memberName.SequenceEqual(TransportPropertyNameBytes))
{ {
availableTransport.Transport = reader.ReadAsString(TransportPropertyNameBytes); availableTransport.Transport = reader.ReadAsString(TransportPropertyName);
} }
else if (memberName.SequenceEqual(TransferFormatsPropertyNameBytes)) else if (memberName.SequenceEqual(TransferFormatsPropertyNameBytes))
{ {

View File

@ -14,6 +14,7 @@
<Compile Include="$(SignalRSharedSourceRoot)WebSocketExtensions.cs" Link="WebSocketExtensions.cs" /> <Compile Include="$(SignalRSharedSourceRoot)WebSocketExtensions.cs" Link="WebSocketExtensions.cs" />
<Compile Include="$(SignalRSharedSourceRoot)StreamExtensions.cs" Link="StreamExtensions.cs" /> <Compile Include="$(SignalRSharedSourceRoot)StreamExtensions.cs" Link="StreamExtensions.cs" />
<Compile Include="$(SignalRSharedSourceRoot)DuplexPipe.cs" Link="DuplexPipe.cs" /> <Compile Include="$(SignalRSharedSourceRoot)DuplexPipe.cs" Link="DuplexPipe.cs" />
<Compile Include="$(SignalRSharedSourceRoot)ValueTaskExtensions.cs" Link="ValueTaskExtensions.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -6,6 +6,7 @@ using System.Buffers;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Internal;
namespace System.IO.Pipelines namespace System.IO.Pipelines
{ {

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
@ -70,19 +71,19 @@ namespace Microsoft.AspNetCore.Internal
} }
} }
public static string ReadAsString(this ref Utf8JsonReader reader, byte[] propertyName) public static string ReadAsString(this ref Utf8JsonReader reader, string propertyName)
{ {
reader.Read(); reader.Read();
if (reader.TokenType != JsonTokenType.String) if (reader.TokenType != JsonTokenType.String)
{ {
throw new InvalidDataException($"Expected '{Encoding.UTF8.GetString(propertyName)}' to be of type {JsonTokenType.String}."); throw new InvalidDataException($"Expected '{propertyName}' to be of type {JsonTokenType.String}.");
} }
return reader.GetString(); return reader.GetString();
} }
public static int? ReadAsInt32(this ref Utf8JsonReader reader, byte[] propertyName) public static int? ReadAsInt32(this ref Utf8JsonReader reader, string propertyName)
{ {
reader.Read(); reader.Read();
@ -93,7 +94,7 @@ namespace Microsoft.AspNetCore.Internal
if (reader.TokenType != JsonTokenType.Number) if (reader.TokenType != JsonTokenType.Number)
{ {
throw new InvalidDataException($"Expected '{Encoding.UTF8.GetString(propertyName)}' to be of type {JsonTokenType.Number}."); throw new InvalidDataException($"Expected '{propertyName}' to be of type {JsonTokenType.Number}.");
} }
return reader.GetInt32(); return reader.GetInt32();

View File

@ -0,0 +1,28 @@
// 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.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Internal
{
internal static class ValueTaskExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task GetAsTask(this in ValueTask<FlushResult> valueTask)
{
// Try to avoid the allocation from AsTask
if (valueTask.IsCompletedSuccessfully)
{
// Signal consumption to the IValueTaskSource
valueTask.GetAwaiter().GetResult();
return Task.CompletedTask;
}
else
{
return valueTask.AsTask();
}
}
}
}

View File

@ -17,16 +17,17 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
/// </summary> /// </summary>
public static class HandshakeProtocol public static class HandshakeProtocol
{ {
// Use C#7.3's ReadOnlySpan<byte> optimization for static data https://vcsjones.com/2019/02/01/csharp-readonly-span-bytes-static/
private const string ProtocolPropertyName = "protocol"; private const string ProtocolPropertyName = "protocol";
private static readonly byte[] ProtocolPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolPropertyName); private static ReadOnlySpan<byte> ProtocolPropertyNameBytes => new byte[] { (byte)'p', (byte)'r', (byte)'o', (byte)'t', (byte)'o', (byte)'c', (byte)'o', (byte)'l' };
private const string ProtocolVersionPropertyName = "version"; private const string ProtocolVersionPropertyName = "version";
private static readonly byte[] ProtocolVersionPropertyNameBytes = Encoding.UTF8.GetBytes(ProtocolVersionPropertyName); private static ReadOnlySpan<byte> ProtocolVersionPropertyNameBytes => new byte[] { (byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' };
private const string MinorVersionPropertyName = "minorVersion"; private const string MinorVersionPropertyName = "minorVersion";
private static readonly byte[] MinorVersionPropertyNameBytes = Encoding.UTF8.GetBytes(MinorVersionPropertyName); private static ReadOnlySpan<byte> MinorVersionPropertyNameBytes => new byte[] { (byte)'m', (byte)'i', (byte)'n', (byte)'o', (byte)'r', (byte)'V', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n' };
private const string ErrorPropertyName = "error"; private const string ErrorPropertyName = "error";
private static readonly byte[] ErrorPropertyNameBytes = Encoding.UTF8.GetBytes(ErrorPropertyName); private static ReadOnlySpan<byte> ErrorPropertyNameBytes => new byte[] { (byte)'e', (byte)'r', (byte)'r', (byte)'o', (byte)'r' };
private const string TypePropertyName = "type"; private const string TypePropertyName = "type";
private static readonly byte[] TypePropertyNameBytes = Encoding.UTF8.GetBytes(TypePropertyName); private static ReadOnlySpan<byte> TypePropertyNameBytes => new byte[] { (byte)'t', (byte)'y', (byte)'p', (byte)'e' };
private static ConcurrentDictionary<IHubProtocol, ReadOnlyMemory<byte>> _messageCache = new ConcurrentDictionary<IHubProtocol, ReadOnlyMemory<byte>>(); private static ConcurrentDictionary<IHubProtocol, ReadOnlyMemory<byte>> _messageCache = new ConcurrentDictionary<IHubProtocol, ReadOnlyMemory<byte>>();
@ -128,11 +129,11 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
} }
else if (memberName.SequenceEqual(ErrorPropertyNameBytes)) else if (memberName.SequenceEqual(ErrorPropertyNameBytes))
{ {
error = reader.ReadAsString(ErrorPropertyNameBytes); error = reader.ReadAsString(ErrorPropertyName);
} }
else if (memberName.SequenceEqual(MinorVersionPropertyNameBytes)) else if (memberName.SequenceEqual(MinorVersionPropertyNameBytes))
{ {
minorVersion = reader.ReadAsInt32(MinorVersionPropertyNameBytes); minorVersion = reader.ReadAsInt32(MinorVersionPropertyName);
} }
else else
{ {
@ -183,11 +184,11 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
if (memberName.SequenceEqual(ProtocolPropertyNameBytes)) if (memberName.SequenceEqual(ProtocolPropertyNameBytes))
{ {
protocol = reader.ReadAsString(ProtocolPropertyNameBytes); protocol = reader.ReadAsString(ProtocolPropertyName);
} }
else if (memberName.SequenceEqual(ProtocolVersionPropertyNameBytes)) else if (memberName.SequenceEqual(ProtocolVersionPropertyNameBytes))
{ {
protocolVersion = reader.ReadAsInt32(ProtocolVersionPropertyNameBytes); protocolVersion = reader.ReadAsInt32(ProtocolVersionPropertyName);
} }
else else
{ {

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Channels; using System.Threading.Channels;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
namespace Microsoft.AspNetCore.SignalR namespace Microsoft.AspNetCore.SignalR