From b89415d9b2ea9bda711621bf9ac5a54f27dcb783 Mon Sep 17 00:00:00 2001 From: arespr Date: Thu, 2 Mar 2017 03:10:00 +0100 Subject: [PATCH] knownmethods optimizations --- .../Internal/Infrastructure/HttpUtilities.cs | 61 +++++++++---- .../KnownStrings.cs | 91 ++++++++++++++++++- 2 files changed, 132 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs index 719e27eff4..0ec137186e 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs @@ -35,20 +35,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure private readonly static ulong _mask5Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }); private readonly static ulong _mask4Chars = GetMaskAsLong(new byte[] { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }); - private readonly static Tuple[] _knownMethods = new Tuple[8]; + private readonly static Tuple[] _knownMethods = new Tuple[17]; private readonly static string[] _methodNames = new string[9]; static HttpUtilities() { - _knownMethods[0] = Tuple.Create(_mask4Chars, _httpPutMethodLong, HttpMethod.Put, 3); - _knownMethods[1] = Tuple.Create(_mask5Chars, _httpPostMethodLong, HttpMethod.Post, 4); - _knownMethods[2] = Tuple.Create(_mask5Chars, _httpHeadMethodLong, HttpMethod.Head, 4); - _knownMethods[3] = Tuple.Create(_mask6Chars, _httpTraceMethodLong, HttpMethod.Trace, 5); - _knownMethods[4] = Tuple.Create(_mask6Chars, _httpPatchMethodLong, HttpMethod.Patch, 5); - _knownMethods[5] = Tuple.Create(_mask7Chars, _httpDeleteMethodLong, HttpMethod.Delete, 6); - _knownMethods[6] = Tuple.Create(_mask8Chars, _httpConnectMethodLong, HttpMethod.Connect, 7); - _knownMethods[7] = Tuple.Create(_mask8Chars, _httpOptionsMethodLong, HttpMethod.Options, 7); + SetKnownMethod(_mask4Chars, _httpPutMethodLong, HttpMethod.Put, 3); + SetKnownMethod(_mask5Chars, _httpPostMethodLong, HttpMethod.Post, 4); + SetKnownMethod(_mask5Chars, _httpHeadMethodLong, HttpMethod.Head, 4); + SetKnownMethod(_mask6Chars, _httpTraceMethodLong, HttpMethod.Trace, 5); + SetKnownMethod(_mask6Chars, _httpPatchMethodLong, HttpMethod.Patch, 5); + SetKnownMethod(_mask7Chars, _httpDeleteMethodLong, HttpMethod.Delete, 6); + SetKnownMethod(_mask8Chars, _httpConnectMethodLong, HttpMethod.Connect, 7); + SetKnownMethod(_mask8Chars, _httpOptionsMethodLong, HttpMethod.Options, 7); + FillEmptyKnownMethods(); _methodNames[(byte)HttpMethod.Get] = HttpMethods.Get; _methodNames[(byte)HttpMethod.Put] = HttpMethods.Put; _methodNames[(byte)HttpMethod.Delete] = HttpMethods.Delete; @@ -60,6 +61,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure _methodNames[(byte)HttpMethod.Options] = HttpMethods.Options; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetKnownMethodIndex(ulong value) + { + var tmp = (int)value & 0x100604; + + return ((tmp >> 2) | (tmp >> 8) | (tmp >> 17)) & 0x0F; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void SetKnownMethod(ulong mask, ulong knownMethodUlong, HttpMethod knownMethod, int length) + { + _knownMethods[GetKnownMethodIndex(knownMethodUlong)] = new Tuple(mask, knownMethodUlong, knownMethod, length, true); + } + + private static void FillEmptyKnownMethods() + { + var knownMethods = _knownMethods; + var length = knownMethods.Length; + for (int i = 0; i < length; i++) + { + if (knownMethods[i] == null) + { + knownMethods[i] = new Tuple(_mask8Chars, 0ul, HttpMethod.Custom, 0, false); + } + } + } + private unsafe static ulong GetAsciiStringAsLong(string str) { Debug.Assert(str.Length == 8, "String must be exactly 8 (ASCII) characters long."); @@ -139,14 +167,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure if (span.TryRead(out var value)) { - foreach (var x in _knownMethods) + var key = GetKnownMethodIndex(value); + + var x = _knownMethods[key]; + + if (x != null && (value & x.Item1) == x.Item2) { - if ((value & x.Item1) == x.Item2) - { - method = x.Item3; - length = x.Item4; - return true; - } + method = x.Item3; + length = x.Item4; + return x.Item5; } } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/KnownStrings.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/KnownStrings.cs index f5fd841f90..95bb6a87ef 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/KnownStrings.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/KnownStrings.cs @@ -11,17 +11,98 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance { public class KnownStrings { - static byte[] _method = Encoding.UTF8.GetBytes("GET "); + static byte[] _methodGet = Encoding.UTF8.GetBytes("GET "); + static byte[] _methodConnect = Encoding.UTF8.GetBytes("CONNECT "); + static byte[] _methodDelete = Encoding.UTF8.GetBytes("DELETE "); + static byte[] _methodHead = Encoding.UTF8.GetBytes("HEAD "); + static byte[] _methodPatch = Encoding.UTF8.GetBytes("PATCH "); + static byte[] _methodPost = Encoding.UTF8.GetBytes("POST "); + static byte[] _methodPut = Encoding.UTF8.GetBytes("PUT "); + static byte[] _methodOptions = Encoding.UTF8.GetBytes("OPTIONS "); + static byte[] _methodTrace = Encoding.UTF8.GetBytes("TRACE "); + + + static byte[] _version = Encoding.UTF8.GetBytes("HTTP/1.1\r\n"); const int loops = 1000; [Benchmark(OperationsPerInvoke = loops * 10)] public int GetKnownMethod_GET() + { + Span data = _methodGet; + + return GetKnownMethod(data); + } + + [Benchmark(OperationsPerInvoke = loops * 10)] + public int GetKnownMethod_CONNECT() + { + Span data = _methodConnect; + + return GetKnownMethod(data); + } + + [Benchmark(OperationsPerInvoke = loops * 10)] + public int GetKnownMethod_DELETE() + { + Span data = _methodDelete; + + return GetKnownMethod(data); + } + [Benchmark(OperationsPerInvoke = loops * 10)] + public int GetKnownMethod_HEAD() + { + Span data = _methodHead; + + return GetKnownMethod(data); + } + + [Benchmark(OperationsPerInvoke = loops * 10)] + public int GetKnownMethod_PATCH() + { + Span data = _methodPatch; + + return GetKnownMethod(data); + } + [Benchmark(OperationsPerInvoke = loops * 10)] + public int GetKnownMethod_POST() + { + Span data = _methodPost; + + return GetKnownMethod(data); + } + [Benchmark(OperationsPerInvoke = loops * 10)] + public int GetKnownMethod_PUT() + { + Span data = _methodPut; + + return GetKnownMethod(data); + } + + [Benchmark(OperationsPerInvoke = loops * 10)] + public int GetKnownMethod_OPTIONS() + { + Span data = _methodOptions; + + return GetKnownMethod(data); + } + + [Benchmark(OperationsPerInvoke = loops * 10)] + public int GetKnownMethod_TRACE() + { + Span data = _methodTrace; + + return GetKnownMethod(data); + } + + + private int GetKnownMethod(Span data) { int len = 0; HttpMethod method; - Span data = _method; - for (int i = 0; i < loops; i++) { + + for (int i = 0; i < loops; i++) + { data.GetKnownMethod(out method, out var length); len += length; data.GetKnownMethod(out method, out length); @@ -46,13 +127,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance return len; } + [Benchmark(OperationsPerInvoke = loops * 10)] public int GetKnownVersion_HTTP1_1() { int len = 0; HttpVersion version; Span data = _version; - for (int i = 0; i < loops; i++) { + for (int i = 0; i < loops; i++) + { data.GetKnownVersion(out version, out var length); len += length; data.GetKnownVersion(out version, out length);