From 90c7be1fc0c6566a37f487dc165193d3aa2cb961 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 15 Nov 2016 00:42:04 +0000 Subject: [PATCH] Add Request Parsing benchmark --- .gitignore | 2 + KestrelHttpServer.sln | 9 + global.json | 2 +- ...spNetCore.Server.Kestrel.Performance.xproj | 22 ++ .../Program.cs | 48 +++++ .../Readme.md | 11 + .../RequestParsing.cs | 200 ++++++++++++++++++ .../columns/RpsColumn.cs | 31 +++ .../configs/CoreConfig.cs | 31 +++ .../configs/DefaultConfig.cs | 42 ++++ .../helpers/MockApplicationErrorLogger.cs | 51 +++++ .../helpers/MockConnectionControl.cs | 34 +++ .../helpers/MockKestrelTrace.cs | 36 ++++ .../helpers/SocketInputExtensions.cs | 37 ++++ .../project.json | 29 +++ 15 files changed, 584 insertions(+), 1 deletion(-) create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.xproj create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/columns/RpsColumn.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/configs/CoreConfig.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/configs/DefaultConfig.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockApplicationErrorLogger.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockConnectionControl.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockKestrelTrace.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/SocketInputExtensions.cs create mode 100644 perf/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json diff --git a/.gitignore b/.gitignore index 6acc284439..af0898e29a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ runtimes/ .build/ .testPublish/ launchSettings.json +BenchmarkDotNet.Artifacts/ +BDN.Generated/ diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln index 1a5cda8a81..e6dca6eac9 100644 --- a/KestrelHttpServer.sln +++ b/KestrelHttpServer.sln @@ -45,6 +45,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC test\shared\TestServiceContext.cs = test\shared\TestServiceContext.cs EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{21B17FBB-5A58-42A8-8473-43160509A9FF}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Server.Kestrel.Performance", "perf\Microsoft.AspNetCore.Server.Kestrel.Performance\Microsoft.AspNetCore.Server.Kestrel.Performance.xproj", "{70567566-524C-4B67-9B59-E5C206D6C2EB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +83,10 @@ Global {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|Any CPU.Build.0 = Debug|Any CPU {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|Any CPU.ActiveCfg = Release|Any CPU {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|Any CPU.Build.0 = Release|Any CPU + {70567566-524C-4B67-9B59-E5C206D6C2EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70567566-524C-4B67-9B59-E5C206D6C2EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70567566-524C-4B67-9B59-E5C206D6C2EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70567566-524C-4B67-9B59-E5C206D6C2EB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -92,5 +100,6 @@ Global {5F64B3C3-0C2E-431A-B820-A81BBFC863DA} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} {9559A5F1-080C-4909-B6CF-7E4B3DC55748} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} {0EF2ACDF-012F-4472-A13A-4272419E2903} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} + {70567566-524C-4B67-9B59-E5C206D6C2EB} = {21B17FBB-5A58-42A8-8473-43160509A9FF} EndGlobalSection EndGlobal diff --git a/global.json b/global.json index 983ba0401e..c0415c19af 100644 --- a/global.json +++ b/global.json @@ -1,3 +1,3 @@ { - "projects": ["src"] + "projects": ["src", "perf"] } diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.xproj b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.xproj new file mode 100644 index 0000000000..3bbc5601e0 --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.xproj @@ -0,0 +1,22 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 70567566-524c-4b67-9b59-e5c206d6c2eb + Microsoft.AspNetCore.Server.Kestrel.Performance + .\obj + .\bin\ + v4.6.2 + + + 2.0 + + + + + + \ No newline at end of file diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs new file mode 100644 index 0000000000..db29b584db --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs @@ -0,0 +1,48 @@ +// 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 BenchmarkDotNet.Running; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class Program + { + public static void Main(string[] args) + { + var options = (uint[])Enum.GetValues(typeof(BenchmarkType)); + BenchmarkType type; + if (args.Length != 1 || !Enum.TryParse(args[0], out type)) + { + Console.WriteLine($"Please add benchmark to run as parameter:"); + for (var i = 0; i < options.Length; i++) + { + Console.WriteLine($" {((BenchmarkType)options[i]).ToString()}"); + } + + return; + } + + RunSelectedBenchmarks(type); + } + + private static void RunSelectedBenchmarks(BenchmarkType type) + { + if (type.HasFlag(BenchmarkType.RequestParsing)) + { + BenchmarkRunner.Run(); + } + } + } + + [Flags] + public enum BenchmarkType : uint + { + RequestParsing = 1, + // add new ones in powers of two - e.g. 2,4,8,16... + + All = uint.MaxValue + } + + +} diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md new file mode 100644 index 0000000000..4088c38007 --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md @@ -0,0 +1,11 @@ +Compile the solution in Release mode (so Kestrel is available in release) + +To run a specific benchmark add it as parameter +``` +dotnet run -c Release RequestParsing +``` +To run all use `All` as parameter +``` +dotnet run -c Release All +``` +Using no parameter will list all available benchmarks \ No newline at end of file diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs new file mode 100644 index 0000000000..b1bdbe16f9 --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs @@ -0,0 +1,200 @@ +// 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 BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Server.Kestrel.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; +using RequestLineStatus = Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame.RequestLineStatus; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + [Config(typeof(CoreConfig))] + 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 Frame Frame; + + [Benchmark(Baseline = true, OperationsPerInvoke = InnerLoopCount)] + public void ParsePlaintext() + { + for (var i = 0; i < InnerLoopCount; i++) + { + InsertData(_plaintextRequest); + + ParseData(); + } + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + public void ParsePipelinedPlaintext() + { + for (var i = 0; i < InnerLoopCount; i++) + { + InsertData(_plaintextPipelinedRequests); + + ParseData(); + } + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount)] + public void ParseLiveAspNet() + { + for (var i = 0; i < InnerLoopCount; i++) + { + InsertData(_liveaspnentRequest); + + ParseData(); + } + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + public void ParsePipelinedLiveAspNet() + { + for (var i = 0; i < InnerLoopCount; i++) + { + InsertData(_liveaspnentPipelinedRequests); + + ParseData(); + } + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount)] + public void ParseUnicode() + { + for (var i = 0; i < InnerLoopCount; i++) + { + InsertData(_unicodeRequest); + + ParseData(); + } + } + + [Benchmark(OperationsPerInvoke = InnerLoopCount * Pipelining)] + public void ParseUnicodePipelined() + { + for (var i = 0; i < InnerLoopCount; i++) + { + InsertData(_unicodePipelinedRequests); + + ParseData(); + } + } + + private void InsertData(byte[] dataBytes) + { + SocketInput.IncomingData(dataBytes, 0, dataBytes.Length); + } + + private void ParseData() + { + while (SocketInput.GetAwaiter().IsCompleted) + { + Frame.Reset(); + + if (Frame.TakeStartLine(SocketInput) != RequestLineStatus.Done) + { + ThrowInvalidStartLine(); + } + + Frame.InitializeHeaders(); + + if (!Frame.TakeMessageHeaders(SocketInput, (FrameRequestHeaders) Frame.RequestHeaders)) + { + ThrowInvalidMessageHeaders(); + } + } + } + + private void ThrowInvalidStartLine() + { + throw new InvalidOperationException("Invalid StartLine"); + } + + private void ThrowInvalidMessageHeaders() + { + throw new InvalidOperationException("Invalid MessageHeaders"); + } + + [Setup] + public void Setup() + { + Trace = new KestrelTrace(new MockKestrelTrace()); + ThreadPool = new LoggingThreadPool(Trace); + MemoryPool = new MemoryPool(); + SocketInput = new SocketInput(MemoryPool, ThreadPool); + + var serviceContext = new ServiceContext + { + DateHeaderValueManager = new DateHeaderValueManager(), + ServerOptions = new KestrelServerOptions(), + Log = Trace + }; + var listenerContext = new ListenerContext(serviceContext) + { + ServerAddress = ServerAddress.FromUrl("http://localhost:5000") + }; + var connectionContext = new ConnectionContext(listenerContext) + { + ConnectionControl = new MockConnectionControl(), + SocketInput = SocketInput + }; + + Frame = new Frame(application: null, context: connectionContext); + } + + [Cleanup] + public void Cleanup() + { + SocketInput.IncomingFin(); + SocketInput.Dispose(); + MemoryPool.Dispose(); + } + } +} diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/columns/RpsColumn.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/columns/RpsColumn.cs new file mode 100644 index 0000000000..12e9969ed2 --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/columns/RpsColumn.cs @@ -0,0 +1,31 @@ +// 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 BenchmarkDotNet.Columns; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class RpsColumn : IColumn + { + private static int NanosPerSecond = 1000 * 1000 * 1000; + + public string GetValue(Summary summary, Benchmark benchmark) + { + var totalNanos = summary.Reports.First(r => r.Benchmark == benchmark).ResultStatistics.Mean; + // Make sure we don't divide by zero!! + return Math.Abs(totalNanos) > 0.0 ? (NanosPerSecond / totalNanos).ToString("N2") : "N/A"; + } + + public bool IsDefault(Summary summary, Benchmark benchmark) => false; + public bool IsAvailable(Summary summary) => true; + public string Id => "RPS-Column"; + public string ColumnName => "RPS"; + public bool AlwaysShow => true; + public ColumnCategory Category => ColumnCategory.Custom; + public int PriorityInCategory => 1; + } +} diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/configs/CoreConfig.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/configs/CoreConfig.cs new file mode 100644 index 0000000000..eff6663fec --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/configs/CoreConfig.cs @@ -0,0 +1,31 @@ +// 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 BenchmarkDotNet.Configs; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Validators; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class CoreConfig : ManualConfig + { + public CoreConfig() + { + Add(JitOptimizationsValidator.FailOnError); + Add(new RpsColumn()); + + Add(Job.Default. + With(Platform.X64). + With(Jit.RyuJit). + With(BenchmarkDotNet.Environments.Runtime.Core). + WithRemoveOutliers(true). + With(new GcMode() { Server = true }). + With(RunStrategy.Throughput). + WithLaunchCount(3). + WithWarmupCount(5). + WithTargetCount(10)); + } + } +} diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/configs/DefaultConfig.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/configs/DefaultConfig.cs new file mode 100644 index 0000000000..01f30373c4 --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/configs/DefaultConfig.cs @@ -0,0 +1,42 @@ +// 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 BenchmarkDotNet.Configs; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Validators; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class DefaultConfig : ManualConfig + { + public DefaultConfig() + { + Add(JitOptimizationsValidator.FailOnError); + Add(new RpsColumn()); + + Add(Job.Default. + With(Platform.X64). + With(Jit.RyuJit). + With(BenchmarkDotNet.Environments.Runtime.Clr). + WithRemoveOutliers(true). + With(new GcMode() { Server = true }). + With(RunStrategy.Throughput). + WithLaunchCount(3). + WithWarmupCount(5). + WithTargetCount(10)); + + Add(Job.Default. + With(Platform.X64). + With(Jit.RyuJit). + With(BenchmarkDotNet.Environments.Runtime.Core). + WithRemoveOutliers(true). + With(new GcMode() { Server = true }). + With(RunStrategy.Throughput). + WithLaunchCount(3). + WithWarmupCount(5). + WithTargetCount(10)); + } + } +} diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockApplicationErrorLogger.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockApplicationErrorLogger.cs new file mode 100644 index 0000000000..c126eba79c --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockApplicationErrorLogger.cs @@ -0,0 +1,51 @@ +// 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.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Server.Kestrel.Internal; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class MockApplicationErrorLogger : ILogger + { + // Application errors are logged using 13 as the eventId. + private const int ApplicationErrorEventId = 13; + + public List Messages { get; } = new List(); + + public int TotalErrorsLogged => Messages.Count(message => message.LogLevel == LogLevel.Error); + + public int CriticalErrorsLogged => Messages.Count(message => message.LogLevel == LogLevel.Critical); + + public int ApplicationErrorsLogged => Messages.Count(message => message.EventId.Id == ApplicationErrorEventId); + + public IDisposable BeginScope(TState state) + { + return new Disposable(() => { }); + } + + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { +#if false + Console.WriteLine($"Log {logLevel}[{eventId}]: {formatter(state, exception)} {exception?.Message}"); +#endif + + Messages.Add(new LogMessage { LogLevel = logLevel, EventId = eventId, Exception = exception }); + } + + public class LogMessage + { + public LogLevel LogLevel { get; set; } + public EventId EventId { get; set; } + public Exception Exception { get; set; } + } + } +} diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockConnectionControl.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockConnectionControl.cs new file mode 100644 index 0000000000..b3587d81db --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockConnectionControl.cs @@ -0,0 +1,34 @@ +// 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 Microsoft.AspNetCore.Server.Kestrel.Internal.Http; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class MockConnectionControl : IConnectionControl + { + public void CancelTimeout() + { + } + + public void End(ProduceEndType endType) + { + } + + public void Pause() + { + } + + public void ResetTimeout(long milliseconds, TimeoutAction timeoutAction) + { + } + + public void Resume() + { + } + + public void SetTimeout(long milliseconds, TimeoutAction timeoutAction) + { + } + } +} diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockKestrelTrace.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockKestrelTrace.cs new file mode 100644 index 0000000000..9e035010d3 --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/MockKestrelTrace.cs @@ -0,0 +1,36 @@ +// 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 Microsoft.AspNetCore.Server.Kestrel.Internal; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class MockKestrelTrace : KestrelTrace + { + public MockKestrelTrace() : this(new MockApplicationErrorLogger()) + { + } + + public MockKestrelTrace(MockApplicationErrorLogger testLogger) : base(testLogger) + { + Logger = testLogger; + } + + public MockApplicationErrorLogger Logger { get; private set; } + + public override void ConnectionRead(string connectionId, int count) + { + //_logger.LogDebug(1, @"Connection id ""{ConnectionId}"" recv {count} bytes.", connectionId, count); + } + + public override void ConnectionWrite(string connectionId, int count) + { + //_logger.LogDebug(1, @"Connection id ""{ConnectionId}"" send {count} bytes.", connectionId, count); + } + + public override void ConnectionWriteCallback(string connectionId, int status) + { + //_logger.LogDebug(1, @"Connection id ""{ConnectionId}"" send finished with status {status}.", connectionId, status); + } + } +} \ No newline at end of file diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/SocketInputExtensions.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/SocketInputExtensions.cs new file mode 100644 index 0000000000..af26c2994c --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/helpers/SocketInputExtensions.cs @@ -0,0 +1,37 @@ +// 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 Microsoft.AspNetCore.Server.Kestrel.Internal.Http; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public static class SocketInputExtensions + { + public static void IncomingData(this SocketInput input, byte[] buffer, int offset, int count) + { + var bufferIndex = offset; + var remaining = count; + + while (remaining > 0) + { + var block = input.IncomingStart(); + + var bytesLeftInBlock = block.Data.Offset + block.Data.Count - block.End; + var bytesToCopy = remaining < bytesLeftInBlock ? remaining : bytesLeftInBlock; + + Buffer.BlockCopy(buffer, bufferIndex, block.Array, block.End, bytesToCopy); + + bufferIndex += bytesToCopy; + remaining -= bytesToCopy; + + input.IncomingComplete(bytesToCopy, null); + } + } + + public static void IncomingFin(this SocketInput input) + { + input.IncomingComplete(0, null); + } + } +} diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json new file mode 100644 index 0000000000..38bb58ec76 --- /dev/null +++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/project.json @@ -0,0 +1,29 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "emitEntryPoint": true + }, + + "dependencies": { + "BenchmarkDotNet": "0.10.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.2.0-*" + }, + + "frameworks": { + "net46": {}, + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.1-*", + "type": "platform" + } + } + } + }, + + "runtimeOptions": { + "configProperties": { + "System.GC.Server": true + } + } +} \ No newline at end of file