knownmethods optimizations

This commit is contained in:
arespr 2017-03-02 03:10:00 +01:00
parent cb6059c143
commit b89415d9b2
2 changed files with 132 additions and 20 deletions

View File

@ -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<ulong, ulong, HttpMethod, int>[] _knownMethods = new Tuple<ulong, ulong, HttpMethod, int>[8];
private readonly static Tuple<ulong, ulong, HttpMethod, int, bool>[] _knownMethods = new Tuple<ulong, ulong, HttpMethod, int, bool>[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<ulong, ulong, HttpMethod, int, bool>(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<ulong, ulong, HttpMethod, int, bool>(_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<ulong>(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;
}
}

View File

@ -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<byte> data = _methodGet;
return GetKnownMethod(data);
}
[Benchmark(OperationsPerInvoke = loops * 10)]
public int GetKnownMethod_CONNECT()
{
Span<byte> data = _methodConnect;
return GetKnownMethod(data);
}
[Benchmark(OperationsPerInvoke = loops * 10)]
public int GetKnownMethod_DELETE()
{
Span<byte> data = _methodDelete;
return GetKnownMethod(data);
}
[Benchmark(OperationsPerInvoke = loops * 10)]
public int GetKnownMethod_HEAD()
{
Span<byte> data = _methodHead;
return GetKnownMethod(data);
}
[Benchmark(OperationsPerInvoke = loops * 10)]
public int GetKnownMethod_PATCH()
{
Span<byte> data = _methodPatch;
return GetKnownMethod(data);
}
[Benchmark(OperationsPerInvoke = loops * 10)]
public int GetKnownMethod_POST()
{
Span<byte> data = _methodPost;
return GetKnownMethod(data);
}
[Benchmark(OperationsPerInvoke = loops * 10)]
public int GetKnownMethod_PUT()
{
Span<byte> data = _methodPut;
return GetKnownMethod(data);
}
[Benchmark(OperationsPerInvoke = loops * 10)]
public int GetKnownMethod_OPTIONS()
{
Span<byte> data = _methodOptions;
return GetKnownMethod(data);
}
[Benchmark(OperationsPerInvoke = loops * 10)]
public int GetKnownMethod_TRACE()
{
Span<byte> data = _methodTrace;
return GetKnownMethod(data);
}
private int GetKnownMethod(Span<byte> data)
{
int len = 0;
HttpMethod method;
Span<byte> 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<byte> 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);