Optimized parsing GET verb and version (#1399)
* Optimized parsing GET verb * optimized http version parsing * Added microbenchmarks for GetKnownMethod and GetKnownVersion
This commit is contained in:
parent
64b6563249
commit
c56de066d3
|
|
@ -1,4 +1,4 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
|
|
@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
|||
private readonly static ulong _httpConnectMethodLong = GetAsciiStringAsLong("CONNECT ");
|
||||
private readonly static ulong _httpDeleteMethodLong = GetAsciiStringAsLong("DELETE \0");
|
||||
private readonly static ulong _httpGetMethodLong = GetAsciiStringAsLong("GET \0\0\0\0");
|
||||
private const uint _httpGetMethodInt = 542393671; // retun of GetAsciiStringAsInt("GET "); const results in better codegen
|
||||
private readonly static ulong _httpHeadMethodLong = GetAsciiStringAsLong("HEAD \0\0\0");
|
||||
private readonly static ulong _httpPatchMethodLong = GetAsciiStringAsLong("PATCH \0\0");
|
||||
private readonly static ulong _httpPostMethodLong = GetAsciiStringAsLong("POST \0\0\0");
|
||||
|
|
@ -26,8 +27,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
|||
private readonly static ulong _httpOptionsMethodLong = GetAsciiStringAsLong("OPTIONS ");
|
||||
private readonly static ulong _httpTraceMethodLong = GetAsciiStringAsLong("TRACE \0\0");
|
||||
|
||||
private readonly static ulong _http10VersionLong = GetAsciiStringAsLong("HTTP/1.0");
|
||||
private readonly static ulong _http11VersionLong = GetAsciiStringAsLong("HTTP/1.1");
|
||||
private const ulong _http10VersionLong = 3471766442030158920; // GetAsciiStringAsLong("HTTP/1.0"); const results in better codegen
|
||||
private const ulong _http11VersionLong = 3543824036068086856; // GetAsciiStringAsLong("HTTP/1.1"); const results in better codegen
|
||||
|
||||
private readonly static ulong _mask8Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff });
|
||||
private readonly static ulong _mask7Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 });
|
||||
|
|
@ -60,6 +61,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
|||
return *(ulong*)ptr;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static uint GetAsciiStringAsInt(string str)
|
||||
{
|
||||
Debug.Assert(str.Length == 4, "String must be exactly 4 (ASCII) characters long.");
|
||||
|
||||
var bytes = Encoding.ASCII.GetBytes(str);
|
||||
|
||||
fixed (byte* ptr = &bytes[0])
|
||||
{
|
||||
return *(uint*)ptr;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static ulong GetMaskAsLong(byte[] bytes)
|
||||
{
|
||||
Debug.Assert(bytes.Length == 8, "Mask must be exactly 8 bytes long.");
|
||||
|
|
@ -173,27 +187,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool GetKnownMethod(this Span<byte> span, out string knownMethod)
|
||||
{
|
||||
knownMethod = null;
|
||||
if (span.Length < sizeof(ulong))
|
||||
if (span.TryRead<uint>(out var possiblyGet))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong value = span.Read<ulong>();
|
||||
if ((value & _mask4Chars) == _httpGetMethodLong)
|
||||
{
|
||||
knownMethod = HttpMethods.Get;
|
||||
return true;
|
||||
}
|
||||
foreach (var x in _knownMethods)
|
||||
{
|
||||
if ((value & x.Item1) == x.Item2)
|
||||
if (possiblyGet == _httpGetMethodInt)
|
||||
{
|
||||
knownMethod = x.Item3;
|
||||
knownMethod = HttpMethods.Get;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (span.TryRead<ulong>(out var value))
|
||||
{
|
||||
foreach (var x in _knownMethods)
|
||||
{
|
||||
if ((value & x.Item1) == x.Item2)
|
||||
{
|
||||
knownMethod = x.Item3;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
knownMethod = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -244,32 +259,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool GetKnownVersion(this Span<byte> span, out string knownVersion)
|
||||
{
|
||||
knownVersion = null;
|
||||
|
||||
if (span.Length < sizeof(ulong))
|
||||
if (span.TryRead<ulong>(out var version))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var value = span.Read<ulong>();
|
||||
if (value == _http11VersionLong)
|
||||
{
|
||||
knownVersion = Http11Version;
|
||||
}
|
||||
else if (value == _http10VersionLong)
|
||||
{
|
||||
knownVersion = Http10Version;
|
||||
}
|
||||
|
||||
if (knownVersion != null)
|
||||
{
|
||||
if (span[sizeof(ulong)] != (byte)'\r')
|
||||
if (version == _http11VersionLong)
|
||||
{
|
||||
knownVersion = Http11Version;
|
||||
}
|
||||
else if (version == _http10VersionLong)
|
||||
{
|
||||
knownVersion = Http10Version;
|
||||
}
|
||||
else
|
||||
{
|
||||
knownVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (span[sizeof(ulong)] == (byte)'\r')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return knownVersion != null;
|
||||
knownVersion = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
// 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;
|
||||
using System.Text;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
||||
{
|
||||
public class KnownStrings
|
||||
{
|
||||
static byte[] _method = Encoding.UTF8.GetBytes("GET ");
|
||||
static byte[] _version = Encoding.UTF8.GetBytes("HTTP/1.1\r\n");
|
||||
const int loops = 1000;
|
||||
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownMethod_GET()
|
||||
{
|
||||
int len = 0;
|
||||
string method;
|
||||
Span<byte> data = _method;
|
||||
for (int i = 0; i < loops; i++) {
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
data.GetKnownMethod(out method);
|
||||
len += method.Length;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = loops * 10)]
|
||||
public int GetKnownVersion_HTTP1_1()
|
||||
{
|
||||
int len = 0;
|
||||
string version;
|
||||
Span<byte> data = _version;
|
||||
for (int i = 0; i < loops; i++) {
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
data.GetKnownVersion(out version);
|
||||
len += version.Length;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
{
|
||||
BenchmarkRunner.Run<PipeThroughput>();
|
||||
}
|
||||
if (type.HasFlag(BenchmarkType.KnownStrings))
|
||||
{
|
||||
BenchmarkRunner.Run<KnownStrings>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +53,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
RequestParsing = 1,
|
||||
Writing = 2,
|
||||
Throughput = 4,
|
||||
KnownStrings = 8,
|
||||
// add new ones in powers of two - e.g. 2,4,8,16...
|
||||
|
||||
All = uint.MaxValue
|
||||
|
|
|
|||
Loading…
Reference in New Issue