From a42108530333dcc085b91ccb5efe82d021f59e8f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 22 Nov 2016 02:21:09 +0000 Subject: [PATCH 1/4] Add Multithread request parsing perf test --- .../MultiThreadedRequestParsing.cs | 247 ++++++++++++++++++ .../Program.cs | 9 +- .../Readme.md | 1 + .../project.json | 2 +- 4 files changed, 254 insertions(+), 5 deletions(-) create mode 100644 test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs new file mode 100644 index 000000000..9fc7980c3 --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs @@ -0,0 +1,247 @@ +// 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.Linq; +using System.Text; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Server.Kestrel.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; +using Microsoft.AspNetCore.Testing; +using RequestLineStatus = Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame.RequestLineStatus; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + [Config(typeof(CoreConfig))] + public class MultiThreadedRequestParsing + { + // Is divided by processor count so will be slightly off as the + // LCM of Intel's core counts (22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2) is 55440 + // which would be far too many iterations + private const int InnerLoopCount = 4096; + private const int Pipelining = 16; + + private const string plaintextRequest = "GET /plaintext HTTP/1.1\r\nHost: www.example.com\r\n\r\n"; + + private const string liveaspnetRequest = "GET https://live.asp.net/ HTTP/1.1\r\n" + + "Host: live.asp.net\r\n" + + "Connection: keep-alive\r\n" + + "Upgrade-Insecure-Requests: 1\r\n" + + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\r\n" + + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + + "DNT: 1\r\n" + + "Accept-Encoding: gzip, deflate, sdch, br\r\n" + + "Accept-Language: en-US,en;q=0.8\r\n" + + "Cookie: __unam=7a67379-1s65dc575c4-6d778abe-1; omniID=9519gfde_3347_4762_8762_df51458c8ec2\r\n\r\n"; + + private const string unicodeRequest = + "GET http://stackoverflow.com/questions/40148683/why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric HTTP/1.1\r\n" + + "Accept: text/html, application/xhtml+xml, image/jxr, */*\r\n" + + "Accept-Language: en-US,en-GB;q=0.7,en;q=0.3\r\n" + + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.14965\r\n" + + "Accept-Encoding: gzip, deflate\r\n" + + "Host: stackoverflow.com\r\n" + + "Connection: Keep-Alive\r\n" + + "Cache-Control: max-age=0\r\n" + + "Upgrade-Insecure-Requests: 1\r\n" + + "DNT: 1\r\n" + + "Referer: http://stackoverflow.com/?tab=month\r\n" + + "Pragma: no-cache\r\n" + + "Cookie: prov=20629ccd-8b0f-e8ef-2935-cd26609fc0bc; __qca=P0-1591065732-1479167353442; _ga=GA1.2.1298898376.1479167354; _gat=1; sgt=id=9519gfde_3347_4762_8762_df51458c8ec2; acct=t=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric&s=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric\r\n\r\n"; + + private static readonly byte[] _plaintextPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(plaintextRequest, Pipelining))); + private static readonly byte[] _plaintextRequest = Encoding.ASCII.GetBytes(plaintextRequest); + + private static readonly byte[] _liveaspnentPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(liveaspnetRequest, Pipelining))); + private static readonly byte[] _liveaspnentRequest = Encoding.ASCII.GetBytes(liveaspnetRequest); + + private static readonly byte[] _unicodePipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(unicodeRequest, Pipelining))); + private static readonly byte[] _unicodeRequest = Encoding.ASCII.GetBytes(unicodeRequest); + + private static readonly int ThreadCount = Environment.ProcessorCount; + private static readonly int LoopCount = InnerLoopCount / ThreadCount; + + private static KestrelTrace[] Trace; + private static LoggingThreadPool[] ThreadPool; + private static MemoryPool[] MemoryPool; + private static SocketInput[] SocketInput; + private static Frame[] Frame; + + [Benchmark(Baseline = true, OperationsPerInvoke = InnerLoopCount)] + public void ParsePlaintext() + { + Parallel.For(0, ThreadCount, new ParallelOptions() {MaxDegreeOfParallelism = ThreadCount}, (index) => + { + var socketInput = SocketInput[index]; + var frame = Frame[index]; + + for (var i = 0; i < LoopCount; i++) + { + InsertData(socketInput, _plaintextRequest); + + ParseData(socketInput, frame); + } + }); + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + public void ParsePipelinedPlaintext() + { + Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => + { + var socketInput = SocketInput[index]; + var frame = Frame[index]; + + for (var i = 0; i < LoopCount; i++) + { + InsertData(socketInput, _plaintextPipelinedRequests); + + ParseData(socketInput, frame); + } + }); + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount)] + public void ParseLiveAspNet() + { + Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => + { + var socketInput = SocketInput[index]; + var frame = Frame[index]; + + for (var i = 0; i < LoopCount; i++) + { + InsertData(socketInput, _liveaspnentRequest); + + ParseData(socketInput, frame); + } + }); + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + public void ParsePipelinedLiveAspNet() + { + Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => + { + var socketInput = SocketInput[index]; + var frame = Frame[index]; + + for (var i = 0; i < LoopCount; i++) + { + InsertData(socketInput, _liveaspnentPipelinedRequests); + + ParseData(socketInput, frame); + } + }); + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount)] + public void ParseUnicode() + { + Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => + { + var socketInput = SocketInput[index]; + var frame = Frame[index]; + + for (var i = 0; i < LoopCount; i++) + { + InsertData(socketInput, _unicodeRequest); + + ParseData(socketInput, frame); + } + }); + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + public void ParseUnicodePipelined() + { + Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => + { + var socketInput = SocketInput[index]; + var frame = Frame[index]; + + for (var i = 0; i < LoopCount; i++) + { + InsertData(socketInput, _unicodePipelinedRequests); + + ParseData(socketInput, frame); + } + }); + } + + private static void InsertData(SocketInput socketInput, byte[] dataBytes) + { + socketInput.IncomingData(dataBytes, 0, dataBytes.Length); + } + + private static void ParseData(SocketInput socketInput, Frame frame) + { + while (socketInput.GetAwaiter().IsCompleted) + { + frame.Reset(); + + if (frame.TakeStartLine(socketInput) != RequestLineStatus.Done) + { + ThrowInvalidStartLine(); + } + + frame.InitializeHeaders(); + + if (!frame.TakeMessageHeaders(socketInput, (FrameRequestHeaders)frame.RequestHeaders)) + { + ThrowInvalidMessageHeaders(); + } + } + } + + private static void ThrowInvalidStartLine() + { + throw new InvalidOperationException("Invalid StartLine"); + } + + private static void ThrowInvalidMessageHeaders() + { + throw new InvalidOperationException("Invalid MessageHeaders"); + } + + [Setup] + public void Setup() + { + var threadCount = ThreadCount; + + Trace = new KestrelTrace[threadCount]; + ThreadPool = new LoggingThreadPool[threadCount]; + MemoryPool = new MemoryPool[threadCount]; + SocketInput = new SocketInput[threadCount]; + Frame = new Frame[threadCount]; + + for (var i = 0; i < threadCount; i++) + { + Trace[i] = new KestrelTrace(new TestKestrelTrace()); + ThreadPool[i] = new LoggingThreadPool(Trace[i]); + MemoryPool[i] = new MemoryPool(); + SocketInput[i] = new SocketInput(MemoryPool[i], ThreadPool[i]); + + var connectionContext = new MockConnection(new KestrelServerOptions()); + connectionContext.SocketInput = SocketInput[i]; + + Frame[i] = new Frame(application: null, context: connectionContext); + } + + } + + [Cleanup] + public void Cleanup() + { + var threadCount = ThreadCount; + for (var i = 0; i < threadCount; i++) + { + SocketInput[i].IncomingFin(); + SocketInput[i].Dispose(); + MemoryPool[i].Dispose(); + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs index b627755d7..00ed0d580 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs @@ -2,11 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using BenchmarkDotNet.Environments; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Properties; using BenchmarkDotNet.Running; -using BenchmarkDotNet.Toolchains; namespace Microsoft.AspNetCore.Server.Kestrel.Performance { @@ -36,6 +32,10 @@ private static void RunSelectedBenchmarks(BenchmarkType type) { BenchmarkRunner.Run(); } + if (type.HasFlag(BenchmarkType.MultiThreadedRequestParsing)) + { + BenchmarkRunner.Run(); + } } } @@ -43,6 +43,7 @@ private static void RunSelectedBenchmarks(BenchmarkType type) public enum BenchmarkType : uint { RequestParsing = 1, + MultiThreadedRequestParsing = 2, // add new ones in powers of two - e.g. 2,4,8,16... All = uint.MaxValue diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md index b98f36ff5..1ea3365cd 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md @@ -3,6 +3,7 @@ To run a specific benchmark add it as parameter ``` dotnet run RequestParsing +dotnet run MultiThreadedRequestParsing ``` To run all use `All` as parameter ``` diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json index a74be5fef..16457927b 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json @@ -8,7 +8,7 @@ "netcoreapp1.0": { "dependencies": { "Microsoft.NETCore.App": { - "version": "1.0.1-*", + "version": "1.1.0-*", "type": "platform" } } From 59534368e59ab8986b7dcbf884abc784dd91942e Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 24 Nov 2016 12:22:03 +0000 Subject: [PATCH 2/4] Share setup --- .../MultiThreadedRequestParsing.cs | 93 ++++--------------- .../Program.cs | 1 + .../RequestParsing.cs | 87 ++++------------- .../Requests.cs | 75 +++++++++++++++ 4 files changed, 112 insertions(+), 144 deletions(-) create mode 100644 test/Microsoft.AspNetCore.Server.Kestrel.Performance/Requests.cs diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs index 9fc7980c3..92db445ce 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs @@ -2,11 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Linq; -using System.Text; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; -using Microsoft.AspNetCore.Server.Kestrel.Internal; using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; using Microsoft.AspNetCore.Testing; @@ -21,52 +18,12 @@ public class MultiThreadedRequestParsing // LCM of Intel's core counts (22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2) is 55440 // which would be far too many iterations private const int InnerLoopCount = 4096; - private const int Pipelining = 16; - - private const string plaintextRequest = "GET /plaintext HTTP/1.1\r\nHost: www.example.com\r\n\r\n"; - - private const string liveaspnetRequest = "GET https://live.asp.net/ HTTP/1.1\r\n" + - "Host: live.asp.net\r\n" + - "Connection: keep-alive\r\n" + - "Upgrade-Insecure-Requests: 1\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\r\n" + - "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + - "DNT: 1\r\n" + - "Accept-Encoding: gzip, deflate, sdch, br\r\n" + - "Accept-Language: en-US,en;q=0.8\r\n" + - "Cookie: __unam=7a67379-1s65dc575c4-6d778abe-1; omniID=9519gfde_3347_4762_8762_df51458c8ec2\r\n\r\n"; - - private const string unicodeRequest = - "GET http://stackoverflow.com/questions/40148683/why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric HTTP/1.1\r\n" + - "Accept: text/html, application/xhtml+xml, image/jxr, */*\r\n" + - "Accept-Language: en-US,en-GB;q=0.7,en;q=0.3\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.14965\r\n" + - "Accept-Encoding: gzip, deflate\r\n" + - "Host: stackoverflow.com\r\n" + - "Connection: Keep-Alive\r\n" + - "Cache-Control: max-age=0\r\n" + - "Upgrade-Insecure-Requests: 1\r\n" + - "DNT: 1\r\n" + - "Referer: http://stackoverflow.com/?tab=month\r\n" + - "Pragma: no-cache\r\n" + - "Cookie: prov=20629ccd-8b0f-e8ef-2935-cd26609fc0bc; __qca=P0-1591065732-1479167353442; _ga=GA1.2.1298898376.1479167354; _gat=1; sgt=id=9519gfde_3347_4762_8762_df51458c8ec2; acct=t=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric&s=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric\r\n\r\n"; - - private static readonly byte[] _plaintextPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(plaintextRequest, Pipelining))); - private static readonly byte[] _plaintextRequest = Encoding.ASCII.GetBytes(plaintextRequest); - - private static readonly byte[] _liveaspnentPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(liveaspnetRequest, Pipelining))); - private static readonly byte[] _liveaspnentRequest = Encoding.ASCII.GetBytes(liveaspnetRequest); - - private static readonly byte[] _unicodePipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(unicodeRequest, Pipelining))); - private static readonly byte[] _unicodeRequest = Encoding.ASCII.GetBytes(unicodeRequest); private static readonly int ThreadCount = Environment.ProcessorCount; private static readonly int LoopCount = InnerLoopCount / ThreadCount; - private static KestrelTrace[] Trace; - private static LoggingThreadPool[] ThreadPool; private static MemoryPool[] MemoryPool; - private static SocketInput[] SocketInput; + private static SocketInput[] Input; private static Frame[] Frame; [Benchmark(Baseline = true, OperationsPerInvoke = InnerLoopCount)] @@ -74,29 +31,29 @@ public void ParsePlaintext() { Parallel.For(0, ThreadCount, new ParallelOptions() {MaxDegreeOfParallelism = ThreadCount}, (index) => { - var socketInput = SocketInput[index]; + var socketInput = Input[index]; var frame = Frame[index]; for (var i = 0; i < LoopCount; i++) { - InsertData(socketInput, _plaintextRequest); + InsertData(socketInput, Requests.PlaintextRequest); ParseData(socketInput, frame); } }); } - [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + [Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)] public void ParsePipelinedPlaintext() { Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => { - var socketInput = SocketInput[index]; + var socketInput = Input[index]; var frame = Frame[index]; for (var i = 0; i < LoopCount; i++) { - InsertData(socketInput, _plaintextPipelinedRequests); + InsertData(socketInput, Requests.PlaintextPipelinedRequests); ParseData(socketInput, frame); } @@ -108,29 +65,29 @@ public void ParseLiveAspNet() { Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => { - var socketInput = SocketInput[index]; + var socketInput = Input[index]; var frame = Frame[index]; for (var i = 0; i < LoopCount; i++) { - InsertData(socketInput, _liveaspnentRequest); + InsertData(socketInput, Requests.LiveaspnentRequest); ParseData(socketInput, frame); } }); } - [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + [Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)] public void ParsePipelinedLiveAspNet() { Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => { - var socketInput = SocketInput[index]; + var socketInput = Input[index]; var frame = Frame[index]; for (var i = 0; i < LoopCount; i++) { - InsertData(socketInput, _liveaspnentPipelinedRequests); + InsertData(socketInput, Requests.LiveaspnentPipelinedRequests); ParseData(socketInput, frame); } @@ -142,29 +99,29 @@ public void ParseUnicode() { Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => { - var socketInput = SocketInput[index]; + var socketInput = Input[index]; var frame = Frame[index]; for (var i = 0; i < LoopCount; i++) { - InsertData(socketInput, _unicodeRequest); + InsertData(socketInput, Requests.UnicodeRequest); ParseData(socketInput, frame); } }); } - [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + [Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)] public void ParseUnicodePipelined() { Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) => { - var socketInput = SocketInput[index]; + var socketInput = Input[index]; var frame = Frame[index]; for (var i = 0; i < LoopCount; i++) { - InsertData(socketInput, _unicodePipelinedRequests); + InsertData(socketInput, Requests.UnicodePipelinedRequests); ParseData(socketInput, frame); } @@ -211,23 +168,13 @@ public void Setup() { var threadCount = ThreadCount; - Trace = new KestrelTrace[threadCount]; - ThreadPool = new LoggingThreadPool[threadCount]; MemoryPool = new MemoryPool[threadCount]; - SocketInput = new SocketInput[threadCount]; + Input = new SocketInput[threadCount]; Frame = new Frame[threadCount]; for (var i = 0; i < threadCount; i++) { - Trace[i] = new KestrelTrace(new TestKestrelTrace()); - ThreadPool[i] = new LoggingThreadPool(Trace[i]); - MemoryPool[i] = new MemoryPool(); - SocketInput[i] = new SocketInput(MemoryPool[i], ThreadPool[i]); - - var connectionContext = new MockConnection(new KestrelServerOptions()); - connectionContext.SocketInput = SocketInput[i]; - - Frame[i] = new Frame(application: null, context: connectionContext); + Requests.SetupFrameObjects(out MemoryPool[i], out Input[i], out Frame[i]); } } @@ -238,9 +185,7 @@ public void Cleanup() var threadCount = ThreadCount; for (var i = 0; i < threadCount; i++) { - SocketInput[i].IncomingFin(); - SocketInput[i].Dispose(); - MemoryPool[i].Dispose(); + Requests.CleanUpFrameObjects(ref MemoryPool[i], ref Input[i], ref Frame[i]); } } } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs index 00ed0d580..df74f6e91 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs @@ -10,6 +10,7 @@ public class Program { public static void Main(string[] args) { + Console.WriteLine($"Kestrel.Performance Tests - ProcessorCount: {Environment.ProcessorCount.ToString()}"); var options = (uint[])Enum.GetValues(typeof(BenchmarkType)); BenchmarkType type; if (args.Length != 1 || !Enum.TryParse(args[0], out type)) diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs index facb7bcef..50e1bbcac 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs @@ -2,10 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Linq; -using System.Text; using BenchmarkDotNet.Attributes; -using Microsoft.AspNetCore.Server.Kestrel.Internal; using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; using Microsoft.AspNetCore.Testing; @@ -17,49 +14,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance public class RequestParsing { private const int InnerLoopCount = 512; - private const int Pipelining = 16; - - private const string plaintextRequest = "GET /plaintext HTTP/1.1\r\nHost: www.example.com\r\n\r\n"; - - private const string liveaspnetRequest = "GET https://live.asp.net/ HTTP/1.1\r\n" + - "Host: live.asp.net\r\n" + - "Connection: keep-alive\r\n" + - "Upgrade-Insecure-Requests: 1\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\r\n" + - "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + - "DNT: 1\r\n" + - "Accept-Encoding: gzip, deflate, sdch, br\r\n" + - "Accept-Language: en-US,en;q=0.8\r\n" + - "Cookie: __unam=7a67379-1s65dc575c4-6d778abe-1; omniID=9519gfde_3347_4762_8762_df51458c8ec2\r\n\r\n"; - - private const string unicodeRequest = - "GET http://stackoverflow.com/questions/40148683/why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric HTTP/1.1\r\n" + - "Accept: text/html, application/xhtml+xml, image/jxr, */*\r\n" + - "Accept-Language: en-US,en-GB;q=0.7,en;q=0.3\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.14965\r\n" + - "Accept-Encoding: gzip, deflate\r\n" + - "Host: stackoverflow.com\r\n" + - "Connection: Keep-Alive\r\n" + - "Cache-Control: max-age=0\r\n" + - "Upgrade-Insecure-Requests: 1\r\n" + - "DNT: 1\r\n" + - "Referer: http://stackoverflow.com/?tab=month\r\n" + - "Pragma: no-cache\r\n" + - "Cookie: prov=20629ccd-8b0f-e8ef-2935-cd26609fc0bc; __qca=P0-1591065732-1479167353442; _ga=GA1.2.1298898376.1479167354; _gat=1; sgt=id=9519gfde_3347_4762_8762_df51458c8ec2; acct=t=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric&s=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric\r\n\r\n"; - - private static readonly byte[] _plaintextPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(plaintextRequest, Pipelining))); - private static readonly byte[] _plaintextRequest = Encoding.ASCII.GetBytes(plaintextRequest); - - private static readonly byte[] _liveaspnentPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(liveaspnetRequest, Pipelining))); - private static readonly byte[] _liveaspnentRequest = Encoding.ASCII.GetBytes(liveaspnetRequest); - - private static readonly byte[] _unicodePipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(unicodeRequest, Pipelining))); - private static readonly byte[] _unicodeRequest = Encoding.ASCII.GetBytes(unicodeRequest); - - private KestrelTrace Trace; - private LoggingThreadPool ThreadPool; + private MemoryPool MemoryPool; - private SocketInput SocketInput; + private SocketInput Input; private Frame Frame; [Benchmark(Baseline = true, OperationsPerInvoke = InnerLoopCount)] @@ -67,18 +24,18 @@ public void ParsePlaintext() { for (var i = 0; i < InnerLoopCount; i++) { - InsertData(_plaintextRequest); + InsertData(Requests.PlaintextRequest); ParseData(); } } - [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + [Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)] public void ParsePipelinedPlaintext() { for (var i = 0; i < InnerLoopCount; i++) { - InsertData(_plaintextPipelinedRequests); + InsertData(Requests.PlaintextPipelinedRequests); ParseData(); } @@ -89,18 +46,18 @@ public void ParseLiveAspNet() { for (var i = 0; i < InnerLoopCount; i++) { - InsertData(_liveaspnentRequest); + InsertData(Requests.LiveaspnentRequest); ParseData(); } } - [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + [Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)] public void ParsePipelinedLiveAspNet() { for (var i = 0; i < InnerLoopCount; i++) { - InsertData(_liveaspnentPipelinedRequests); + InsertData(Requests.LiveaspnentPipelinedRequests); ParseData(); } @@ -111,18 +68,18 @@ public void ParseUnicode() { for (var i = 0; i < InnerLoopCount; i++) { - InsertData(_unicodeRequest); + InsertData(Requests.UnicodeRequest); ParseData(); } } - [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + [Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)] public void ParseUnicodePipelined() { for (var i = 0; i < InnerLoopCount; i++) { - InsertData(_unicodePipelinedRequests); + InsertData(Requests.UnicodePipelinedRequests); ParseData(); } @@ -130,23 +87,23 @@ public void ParseUnicodePipelined() private void InsertData(byte[] dataBytes) { - SocketInput.IncomingData(dataBytes, 0, dataBytes.Length); + Input.IncomingData(dataBytes, 0, dataBytes.Length); } private void ParseData() { - while (SocketInput.GetAwaiter().IsCompleted) + while (Input.GetAwaiter().IsCompleted) { Frame.Reset(); - if (Frame.TakeStartLine(SocketInput) != RequestLineStatus.Done) + if (Frame.TakeStartLine(Input) != RequestLineStatus.Done) { ThrowInvalidStartLine(); } Frame.InitializeHeaders(); - if (!Frame.TakeMessageHeaders(SocketInput, (FrameRequestHeaders) Frame.RequestHeaders)) + if (!Frame.TakeMessageHeaders(Input, (FrameRequestHeaders) Frame.RequestHeaders)) { ThrowInvalidMessageHeaders(); } @@ -166,23 +123,13 @@ private void ThrowInvalidMessageHeaders() [Setup] public void Setup() { - Trace = new KestrelTrace(new TestKestrelTrace()); - ThreadPool = new LoggingThreadPool(Trace); - MemoryPool = new MemoryPool(); - SocketInput = new SocketInput(MemoryPool, ThreadPool); - - var connectionContext = new MockConnection(new KestrelServerOptions()); - connectionContext.Input = SocketInput; - - Frame = new Frame(application: null, context: connectionContext); + Requests.SetupFrameObjects(out MemoryPool, out Input, out Frame); } [Cleanup] public void Cleanup() { - SocketInput.IncomingFin(); - SocketInput.Dispose(); - MemoryPool.Dispose(); + Requests.CleanUpFrameObjects(ref MemoryPool, ref Input, ref Frame); } } } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Requests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Requests.cs new file mode 100644 index 000000000..5434e46c6 --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Requests.cs @@ -0,0 +1,75 @@ +// 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.Linq; +using System.Text; +using Microsoft.AspNetCore.Server.Kestrel.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; +using Microsoft.AspNetCore.Testing; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class Requests + { + public const int Pipelining = 16; + + private const string plaintextRequest = "GET /plaintext HTTP/1.1\r\nHost: www.example.com\r\n\r\n"; + + private const string liveaspnetRequest = "GET https://live.asp.net/ HTTP/1.1\r\n" + + "Host: live.asp.net\r\n" + + "Connection: keep-alive\r\n" + + "Upgrade-Insecure-Requests: 1\r\n" + + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\r\n" + + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + + "DNT: 1\r\n" + + "Accept-Encoding: gzip, deflate, sdch, br\r\n" + + "Accept-Language: en-US,en;q=0.8\r\n" + + "Cookie: __unam=7a67379-1s65dc575c4-6d778abe-1; omniID=9519gfde_3347_4762_8762_df51458c8ec2\r\n\r\n"; + + private const string unicodeRequest = + "GET http://stackoverflow.com/questions/40148683/why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric HTTP/1.1\r\n" + + "Accept: text/html, application/xhtml+xml, image/jxr, */*\r\n" + + "Accept-Language: en-US,en-GB;q=0.7,en;q=0.3\r\n" + + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.14965\r\n" + + "Accept-Encoding: gzip, deflate\r\n" + + "Host: stackoverflow.com\r\n" + + "Connection: Keep-Alive\r\n" + + "Cache-Control: max-age=0\r\n" + + "Upgrade-Insecure-Requests: 1\r\n" + + "DNT: 1\r\n" + + "Referer: http://stackoverflow.com/?tab=month\r\n" + + "Pragma: no-cache\r\n" + + "Cookie: prov=20629ccd-8b0f-e8ef-2935-cd26609fc0bc; __qca=P0-1591065732-1479167353442; _ga=GA1.2.1298898376.1479167354; _gat=1; sgt=id=9519gfde_3347_4762_8762_df51458c8ec2; acct=t=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric&s=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric\r\n\r\n"; + + public static readonly byte[] PlaintextPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(plaintextRequest, Pipelining))); + public static readonly byte[] PlaintextRequest = Encoding.ASCII.GetBytes(plaintextRequest); + + public static readonly byte[] LiveaspnentPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(liveaspnetRequest, Pipelining))); + public static readonly byte[] LiveaspnentRequest = Encoding.ASCII.GetBytes(liveaspnetRequest); + + public static readonly byte[] UnicodePipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(unicodeRequest, Pipelining))); + public static readonly byte[] UnicodeRequest = Encoding.ASCII.GetBytes(unicodeRequest); + + public static void SetupFrameObjects(out MemoryPool memoryPool, out SocketInput input, out Frame frame) + { + memoryPool = new MemoryPool(); + + var trace = new KestrelTrace(new TestKestrelTrace()); + var threadPool = new LoggingThreadPool(trace); + input = new SocketInput(memoryPool, threadPool); + + var connectionContext = new MockConnection(new KestrelServerOptions()); + connectionContext.Input = input; + + frame = new Frame(application: null, context: connectionContext); + } + + public static void CleanUpFrameObjects(ref MemoryPool memoryPool, ref SocketInput input, ref Frame frame) + { + input.IncomingFin(); + input.Dispose(); + memoryPool.Dispose(); + } + } +} \ No newline at end of file From 8301721b80d57828fb106699685502c1d0ff5f58 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 4 Dec 2016 10:59:18 +0000 Subject: [PATCH 3/4] Update to BenchmarkDotNet to 0.10.1 --- .../project.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json index 16457927b..ca1cf9230 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json @@ -1,11 +1,11 @@ { "version": "1.0.0-*", "dependencies": { - "BenchmarkDotNet": "0.10.0", + "BenchmarkDotNet": "0.10.1", "Microsoft.AspNetCore.Server.Kestrel": "1.2.0-*" }, "frameworks": { - "netcoreapp1.0": { + "netcoreapp1.1": { "dependencies": { "Microsoft.NETCore.App": { "version": "1.1.0-*", From 40bb88075f6cc866eae0da40218bc7bf688542bc Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 5 Dec 2016 23:06:41 +0000 Subject: [PATCH 4/4] Rename request variable --- .../MultiThreadedRequestParsing.cs | 4 ++-- .../RequestParsing.cs | 4 ++-- .../Requests.cs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs index 92db445ce..53b8ea02d 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/MultiThreadedRequestParsing.cs @@ -70,7 +70,7 @@ public void ParseLiveAspNet() for (var i = 0; i < LoopCount; i++) { - InsertData(socketInput, Requests.LiveaspnentRequest); + InsertData(socketInput, Requests.LiveAspNetRequest); ParseData(socketInput, frame); } @@ -87,7 +87,7 @@ public void ParsePipelinedLiveAspNet() for (var i = 0; i < LoopCount; i++) { - InsertData(socketInput, Requests.LiveaspnentPipelinedRequests); + InsertData(socketInput, Requests.LiveAspNetPipelinedRequests); ParseData(socketInput, frame); } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs index 50e1bbcac..36af5f782 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs @@ -46,7 +46,7 @@ public void ParseLiveAspNet() { for (var i = 0; i < InnerLoopCount; i++) { - InsertData(Requests.LiveaspnentRequest); + InsertData(Requests.LiveAspNetRequest); ParseData(); } @@ -57,7 +57,7 @@ public void ParsePipelinedLiveAspNet() { for (var i = 0; i < InnerLoopCount; i++) { - InsertData(Requests.LiveaspnentPipelinedRequests); + InsertData(Requests.LiveAspNetPipelinedRequests); ParseData(); } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Requests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Requests.cs index 5434e46c6..7f9fe93c9 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Requests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Requests.cs @@ -16,7 +16,7 @@ public class Requests private const string plaintextRequest = "GET /plaintext HTTP/1.1\r\nHost: www.example.com\r\n\r\n"; - private const string liveaspnetRequest = "GET https://live.asp.net/ HTTP/1.1\r\n" + + private const string liveAspNetRequest = "GET https://live.asp.net/ HTTP/1.1\r\n" + "Host: live.asp.net\r\n" + "Connection: keep-alive\r\n" + "Upgrade-Insecure-Requests: 1\r\n" + @@ -45,8 +45,8 @@ public class Requests public static readonly byte[] PlaintextPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(plaintextRequest, Pipelining))); public static readonly byte[] PlaintextRequest = Encoding.ASCII.GetBytes(plaintextRequest); - public static readonly byte[] LiveaspnentPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(liveaspnetRequest, Pipelining))); - public static readonly byte[] LiveaspnentRequest = Encoding.ASCII.GetBytes(liveaspnetRequest); + public static readonly byte[] LiveAspNetPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(liveAspNetRequest, Pipelining))); + public static readonly byte[] LiveAspNetRequest = Encoding.ASCII.GetBytes(liveAspNetRequest); public static readonly byte[] UnicodePipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(unicodeRequest, Pipelining))); public static readonly byte[] UnicodeRequest = Encoding.ASCII.GetBytes(unicodeRequest);