Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// 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.Threading.Tasks;
using BenchmarkDotNet.Attributes;
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 static readonly int ThreadCount = Environment.ProcessorCount;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For convenience, can we print this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to Program.cs

private static readonly int LoopCount = InnerLoopCount / ThreadCount;

private static MemoryPool[] MemoryPool;
private static SocketInput[] Input;
private static Frame<object>[] Frame;

[Benchmark(Baseline = true, OperationsPerInvoke = InnerLoopCount)]
public void ParsePlaintext()
{
Parallel.For(0, ThreadCount, new ParallelOptions() {MaxDegreeOfParallelism = ThreadCount}, (index) =>
{
var socketInput = Input[index];
var frame = Frame[index];

for (var i = 0; i < LoopCount; i++)
{
InsertData(socketInput, Requests.PlaintextRequest);

ParseData(socketInput, frame);
}
});
}

[Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)]
public void ParsePipelinedPlaintext()
{
Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) =>
{
var socketInput = Input[index];
var frame = Frame[index];

for (var i = 0; i < LoopCount; i++)
{
InsertData(socketInput, Requests.PlaintextPipelinedRequests);

ParseData(socketInput, frame);
}
});
}

[Benchmark(OperationsPerInvoke = InnerLoopCount)]
public void ParseLiveAspNet()
{
Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) =>
{
var socketInput = Input[index];
var frame = Frame[index];

for (var i = 0; i < LoopCount; i++)
{
InsertData(socketInput, Requests.LiveAspNetRequest);

ParseData(socketInput, frame);
}
});
}

[Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)]
public void ParsePipelinedLiveAspNet()
{
Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) =>
{
var socketInput = Input[index];
var frame = Frame[index];

for (var i = 0; i < LoopCount; i++)
{
InsertData(socketInput, Requests.LiveAspNetPipelinedRequests);

ParseData(socketInput, frame);
}
});
}

[Benchmark(OperationsPerInvoke = InnerLoopCount)]
public void ParseUnicode()
{
Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) =>
{
var socketInput = Input[index];
var frame = Frame[index];

for (var i = 0; i < LoopCount; i++)
{
InsertData(socketInput, Requests.UnicodeRequest);

ParseData(socketInput, frame);
}
});
}

[Benchmark(OperationsPerInvoke = InnerLoopCount * Requests.Pipelining)]
public void ParseUnicodePipelined()
{
Parallel.For(0, ThreadCount, new ParallelOptions() { MaxDegreeOfParallelism = ThreadCount }, (index) =>
{
var socketInput = Input[index];
var frame = Frame[index];

for (var i = 0; i < LoopCount; i++)
{
InsertData(socketInput, Requests.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<object> 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;

MemoryPool = new MemoryPool[threadCount];
Input = new SocketInput[threadCount];
Frame = new Frame<object>[threadCount];

for (var i = 0; i < threadCount; i++)
{
Requests.SetupFrameObjects(out MemoryPool[i], out Input[i], out Frame[i]);
}

}

[Cleanup]
public void Cleanup()
{
var threadCount = ThreadCount;
for (var i = 0; i < threadCount; i++)
{
Requests.CleanUpFrameObjects(ref MemoryPool[i], ref Input[i], ref Frame[i]);
}
}
}
}
10 changes: 6 additions & 4 deletions test/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@
// 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
{
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))
Expand All @@ -36,13 +33,18 @@ private static void RunSelectedBenchmarks(BenchmarkType type)
{
BenchmarkRunner.Run<RequestParsing>();
}
if (type.HasFlag(BenchmarkType.MultiThreadedRequestParsing))
{
BenchmarkRunner.Run<MultiThreadedRequestParsing>();
}
}
}

[Flags]
public enum BenchmarkType : uint
{
RequestParsing = 1,
MultiThreadedRequestParsing = 2,
// add new ones in powers of two - e.g. 2,4,8,16...

All = uint.MaxValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
Loading