Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 51d8f07

Browse files
author
Cesar Blum Silveira
committed
Further optimize by using a perfect hash lookup.
1 parent 1426c35 commit 51d8f07

File tree

8 files changed

+86
-133
lines changed

8 files changed

+86
-133
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"profiles": {}
3+
}

src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ protected bool TakeStartLine(SocketInput input)
641641
}
642642

643643
string method;
644-
if (!begin.GetHttpMethodString(out method))
644+
if (!begin.GetKnownString(scan, out method))
645645
{
646646
method = begin.GetAsciiString(scan);
647647
}
@@ -679,7 +679,7 @@ protected bool TakeStartLine(SocketInput input)
679679
}
680680

681681
string httpVersion;
682-
if (!begin.GetHttpVersionString(out httpVersion))
682+
if (!begin.GetKnownString(scan, out httpVersion))
683683
{
684684
httpVersion = begin.GetAsciiString(scan);
685685
}

src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public unsafe long PeekLong()
144144
{
145145
return -1;
146146
}
147-
else if (_block.End - _index >= 8)
147+
else if (_block.End - _index >= sizeof(long))
148148
{
149149
fixed (byte* ptr = _block.Array)
150150
{
@@ -158,7 +158,7 @@ public unsafe long PeekLong()
158158
else
159159
{
160160
var blockBytes = _block.End - _index;
161-
var nextBytes = 8 - blockBytes;
161+
var nextBytes = sizeof(long) - blockBytes;
162162

163163
if (_block.Next.End - _block.Next.Start < nextBytes)
164164
{
@@ -168,7 +168,7 @@ public unsafe long PeekLong()
168168
long blockLong;
169169
fixed (byte* ptr = _block.Array)
170170
{
171-
blockLong = *(long*)(ptr + _block.End - 8);
171+
blockLong = *(long*)(ptr + _block.End - sizeof(long));
172172
}
173173

174174
long nextLong;
@@ -177,7 +177,7 @@ public unsafe long PeekLong()
177177
nextLong = *(long*)(ptr + _block.Next.Start);
178178
}
179179

180-
return (blockLong >> (8 - blockBytes) * 8) | (nextLong << (8 - nextBytes) * 8);
180+
return (blockLong >> (sizeof(long) - blockBytes) * 8) | (nextLong << (sizeof(long) - nextBytes) * 8);
181181
}
182182
}
183183

src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2Extensions.cs

Lines changed: 38 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,37 @@ public static class MemoryPoolIterator2Extensions
2626
public const string Http10Version = "HTTP/1.0";
2727
public const string Http11Version = "HTTP/1.1";
2828

29-
private static long _httpConnectMethodLong = GetAsciiStringAsLong("CONNECT ");
30-
private static long _httpDeleteMethodLong = GetAsciiStringAsLong("DELETE ");
31-
private static long _httpGetMethodLong = GetAsciiStringAsLong("GET ");
32-
private static long _httpHeadMethodLong = GetAsciiStringAsLong("HEAD ");
33-
private static long _httpPatchMethodLong = GetAsciiStringAsLong("PATCH ");
34-
private static long _httpPostMethodLong = GetAsciiStringAsLong("POST ");
35-
private static long _httpPutMethodLong = GetAsciiStringAsLong("PUT ");
36-
private static long _httpOptionsMethodLong = GetAsciiStringAsLong("OPTIONS ");
37-
private static long _httpTraceMethodLong = GetAsciiStringAsLong("TRACE ");
29+
private static long _httpConnectMethodLong = GetAsciiStringAsLong("CONNECT\0");
30+
private static long _httpDeleteMethodLong = GetAsciiStringAsLong("DELETE\0\0");
31+
private static long _httpGetMethodLong = GetAsciiStringAsLong("GET\0\0\0\0\0");
32+
private static long _httpHeadMethodLong = GetAsciiStringAsLong("HEAD\0\0\0\0");
33+
private static long _httpPatchMethodLong = GetAsciiStringAsLong("PATCH\0\0\0");
34+
private static long _httpPostMethodLong = GetAsciiStringAsLong("POST\0\0\0\0");
35+
private static long _httpPutMethodLong = GetAsciiStringAsLong("PUT\0\0\0\0\0");
36+
private static long _httpOptionsMethodLong = GetAsciiStringAsLong("OPTIONS\0");
37+
private static long _httpTraceMethodLong = GetAsciiStringAsLong("TRACE\0\0\0");
3838

3939
private static long _http10VersionLong = GetAsciiStringAsLong("HTTP/1.0");
4040
private static long _http11VersionLong = GetAsciiStringAsLong("HTTP/1.1");
4141

42+
private const int PerfectHashDivisor = 37;
43+
private static Tuple<long, string>[] _knownStrings = new Tuple<long, string>[PerfectHashDivisor];
44+
45+
static MemoryPoolIterator2Extensions()
46+
{
47+
_knownStrings[_httpConnectMethodLong % PerfectHashDivisor] = Tuple.Create(_httpConnectMethodLong, HttpConnectMethod);
48+
_knownStrings[_httpDeleteMethodLong % PerfectHashDivisor] = Tuple.Create(_httpDeleteMethodLong, HttpDeleteMethod);
49+
_knownStrings[_httpGetMethodLong % PerfectHashDivisor] = Tuple.Create(_httpGetMethodLong, HttpGetMethod);
50+
_knownStrings[_httpHeadMethodLong % PerfectHashDivisor] = Tuple.Create(_httpHeadMethodLong, HttpHeadMethod);
51+
_knownStrings[_httpPatchMethodLong % PerfectHashDivisor] = Tuple.Create(_httpPatchMethodLong, HttpPatchMethod);
52+
_knownStrings[_httpPostMethodLong % PerfectHashDivisor] = Tuple.Create(_httpPostMethodLong, HttpPostMethod);
53+
_knownStrings[_httpPutMethodLong % PerfectHashDivisor] = Tuple.Create(_httpPutMethodLong, HttpPutMethod);
54+
_knownStrings[_httpOptionsMethodLong % PerfectHashDivisor] = Tuple.Create(_httpOptionsMethodLong, HttpOptionsMethod);
55+
_knownStrings[_httpTraceMethodLong % PerfectHashDivisor] = Tuple.Create(_httpTraceMethodLong, HttpTraceMethod);
56+
_knownStrings[_http10VersionLong % PerfectHashDivisor] = Tuple.Create(_http10VersionLong, Http10Version);
57+
_knownStrings[_http11VersionLong % PerfectHashDivisor] = Tuple.Create(_http11VersionLong, Http11Version);
58+
}
59+
4260
private unsafe static long GetAsciiStringAsLong(string str)
4361
{
4462
Debug.Assert(str.Length == 8, "String must be exactly 8 (ASCII) characters long.");
@@ -244,91 +262,32 @@ public static ArraySegment<byte> GetArraySegment(this MemoryPoolIterator2 start,
244262
return new ArraySegment<byte>(array, 0, length);
245263
}
246264

247-
public static bool GetHttpMethodString(this MemoryPoolIterator2 scan, out string httpMethod)
265+
public static bool GetKnownString(this MemoryPoolIterator2 begin, MemoryPoolIterator2 end, out string knownString)
248266
{
249-
httpMethod = null;
250-
var scanLong = scan.PeekLong();
267+
knownString = null;
268+
var inputLength = begin.GetLength(end);
251269

252-
if (scanLong == -1)
270+
if (inputLength > sizeof(long))
253271
{
254272
return false;
255273
}
256274

257-
if (((scanLong ^ _httpGetMethodLong) << 32) == 0)
258-
{
259-
httpMethod = HttpGetMethod;
260-
}
261-
else if (((scanLong ^ _httpPostMethodLong) << 24) == 0)
262-
{
263-
httpMethod = HttpPostMethod;
264-
}
265-
else if (((scanLong ^ _httpDeleteMethodLong) << 8) == 0)
266-
{
267-
httpMethod = HttpDeleteMethod;
268-
}
269-
else if (((scanLong ^ _httpHeadMethodLong) << 24) == 0)
270-
{
271-
httpMethod = HttpHeadMethod;
272-
}
273-
else if (((scanLong ^ _httpPutMethodLong) << 32) == 0)
274-
{
275-
httpMethod = HttpPutMethod;
276-
}
277-
else if (scanLong == _httpConnectMethodLong)
278-
{
279-
httpMethod = HttpConnectMethod;
280-
}
281-
else if (((scanLong ^ _httpPatchMethodLong) << 16) == 0)
282-
{
283-
httpMethod = HttpPatchMethod;
284-
}
285-
else if (scanLong == _httpOptionsMethodLong)
286-
{
287-
httpMethod = HttpOptionsMethod;
288-
}
289-
else if (((scanLong ^ _httpTraceMethodLong) << 16) == 0)
290-
{
291-
httpMethod = HttpTraceMethod;
292-
}
293-
294-
return httpMethod != null;
295-
}
296-
297-
public static bool GetHttpVersionString(this MemoryPoolIterator2 scan, out string httpVersion)
298-
{
299-
httpVersion = null;
300-
var scanLong = scan.PeekLong();
275+
var inputLong = begin.PeekLong();
301276

302-
if (scanLong == -1)
277+
if (inputLong == -1)
303278
{
304279
return false;
305280
}
306281

307-
if (scanLong == _http11VersionLong)
308-
{
309-
httpVersion = Http11Version;
310-
}
311-
else if (scanLong == _http10VersionLong)
312-
{
313-
httpVersion = Http10Version;
314-
}
282+
inputLong &= (long)(unchecked((ulong)~0) >> ((sizeof(long) - inputLength) * 8));
315283

316-
if (httpVersion != null)
284+
var value = _knownStrings[inputLong % PerfectHashDivisor];
285+
if (value != null && value.Item1 == inputLong)
317286
{
318-
for (int i = 0; i < httpVersion.Length; i++) scan.Take();
319-
320-
if (scan.Peek() == '\r')
321-
{
322-
return true;
323-
}
324-
else
325-
{
326-
httpVersion = null;
327-
return false;
328-
}
287+
knownString = value.Item2;
329288
}
330289

331-
return false;
290+
return knownString != null;
332291
}
333292
}
334293
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"profiles": {}
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"profiles": {}
3+
}

test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolIterator2Tests.cs

Lines changed: 30 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -185,65 +185,47 @@ public void PeekLongAtBlockBoundary(int blockBytes)
185185
}
186186

187187
[Theory]
188-
[InlineData("CONNECT / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpConnectMethod)]
189-
[InlineData("DELETE / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpDeleteMethod)]
190-
[InlineData("GET / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpGetMethod)]
191-
[InlineData("HEAD / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpHeadMethod)]
192-
[InlineData("PATCH / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpPatchMethod)]
193-
[InlineData("POST / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpPostMethod)]
194-
[InlineData("PUT / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpPutMethod)]
195-
[InlineData("OPTIONS / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpOptionsMethod)]
196-
[InlineData("TRACE / HTTP/1.1", true, MemoryPoolIterator2Extensions.HttpTraceMethod)]
197-
[InlineData("GET/ HTTP/1.1", false, null)]
198-
[InlineData("get / HTTP/1.1", false, null)]
199-
[InlineData("GOT / HTTP/1.1", false, null)]
200-
[InlineData("ABC / HTTP/1.1", false, null)]
201-
[InlineData("PO / HTTP/1.1", false, null)]
202-
[InlineData("PO ST / HTTP/1.1", false, null)]
203-
[InlineData("short", false, null)] // Less than 8 bytes input
204-
public void GetsHttpMethodString(string input, bool expectedResult, string expectedHttpMethod)
188+
[InlineData("CONNECT / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpConnectMethod)]
189+
[InlineData("DELETE / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpDeleteMethod)]
190+
[InlineData("GET / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpGetMethod)]
191+
[InlineData("HEAD / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpHeadMethod)]
192+
[InlineData("PATCH / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpPatchMethod)]
193+
[InlineData("POST / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpPostMethod)]
194+
[InlineData("PUT / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpPutMethod)]
195+
[InlineData("OPTIONS / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpOptionsMethod)]
196+
[InlineData("TRACE / HTTP/1.1", ' ', true, MemoryPoolIterator2Extensions.HttpTraceMethod)]
197+
[InlineData("HTTP/1.0\r", '\r', true, MemoryPoolIterator2Extensions.Http10Version)]
198+
[InlineData("HTTP/1.1\r", '\r', true, MemoryPoolIterator2Extensions.Http11Version)]
199+
[InlineData("GET/ HTTP/1.1", ' ', false, null)]
200+
[InlineData("get / HTTP/1.1", ' ', false, null)]
201+
[InlineData("GOT / HTTP/1.1", ' ', false, null)]
202+
[InlineData("ABC / HTTP/1.1", ' ', false, null)]
203+
[InlineData("PO / HTTP/1.1", ' ', false, null)]
204+
[InlineData("PO ST / HTTP/1.1", ' ', false, null)]
205+
[InlineData("HTTP/1.0_\r", '\r', false, null)]
206+
[InlineData("HTTP/1.1_\r", '\r', false, null)]
207+
[InlineData("HTTP/3.0\r", '\r', false, null)]
208+
[InlineData("http/1.0\r", '\r', false, null)]
209+
[InlineData("http/1.1\r", '\r', false, null)]
210+
[InlineData("short ", ' ', false, null)]
211+
public void GetsKnownString(string input, char endChar, bool expectedResult, string expectedKnownString)
205212
{
206213
// Arrange
207214
var block = _pool.Lease();
208215
var chars = input.ToCharArray().Select(c => (byte)c).ToArray();
209216
Buffer.BlockCopy(chars, 0, block.Array, block.Start, chars.Length);
210217
block.End += chars.Length;
211-
var scan = block.GetIterator();
212-
string httpMethod;
213-
214-
// Act
215-
var result = scan.GetHttpMethodString(out httpMethod);
216-
217-
// Assert
218-
Assert.Equal(expectedResult, result);
219-
Assert.Equal(expectedHttpMethod, httpMethod);
220-
}
221-
222-
[Theory]
223-
[InlineData("HTTP/1.0\r", true, MemoryPoolIterator2Extensions.Http10Version)]
224-
[InlineData("HTTP/1.1\r", true, MemoryPoolIterator2Extensions.Http11Version)]
225-
[InlineData("HTTP/1.0a", false, null)]
226-
[InlineData("HTTP/1.1a", false, null)]
227-
[InlineData("HTTP/3.0\r", false, null)]
228-
[InlineData("http/1.0\r", false, null)]
229-
[InlineData("http/1.1\r", false, null)]
230-
[InlineData("short", false, null)] // Less than 8 bytes input
231-
public void GetsHttpVersionString(string input, bool expectedResult, string expectedHttpVersion)
232-
{
233-
// Arrange
234-
var block = _pool.Lease();
235-
var chars = input.ToCharArray().Select(c => (byte)c).ToArray();
236-
Buffer.BlockCopy(chars, 0, block.Array, block.Start, chars.Length);
237-
block.End += chars.Length;
238-
var scan = block.GetIterator();
239-
string httpVersion;
218+
var begin = block.GetIterator();
219+
var end = begin;
220+
end.Seek(endChar);
221+
string knownString;
240222

241223
// Act
242-
var result = scan.GetHttpVersionString(out httpVersion);
224+
var result = begin.GetKnownString(end, out knownString);
243225

244226
// Assert
245227
Assert.Equal(expectedResult, result);
246-
Assert.Equal(expectedHttpVersion, httpVersion);
228+
Assert.Equal(expectedKnownString, knownString);
247229
}
248230
}
249231
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"profiles": {}
3+
}

0 commit comments

Comments
 (0)