Remove old benchmark application (#18889)
This commit is contained in:
parent
65494819d3
commit
4911c08338
|
|
@ -1,49 +0,0 @@
|
||||||
// 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.Text;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public readonly struct AsciiString : IEquatable<AsciiString>
|
|
||||||
{
|
|
||||||
private readonly byte[] _data;
|
|
||||||
|
|
||||||
public AsciiString(string s) => _data = Encoding.ASCII.GetBytes(s);
|
|
||||||
|
|
||||||
public int Length => _data.Length;
|
|
||||||
|
|
||||||
public ReadOnlySpan<byte> AsSpan() => _data;
|
|
||||||
|
|
||||||
public static implicit operator ReadOnlySpan<byte>(AsciiString str) => str._data;
|
|
||||||
public static implicit operator byte[] (AsciiString str) => str._data;
|
|
||||||
|
|
||||||
public static implicit operator AsciiString(string str) => new AsciiString(str);
|
|
||||||
|
|
||||||
public override string ToString() => Encoding.ASCII.GetString(_data);
|
|
||||||
public static explicit operator string(AsciiString str) => str.ToString();
|
|
||||||
|
|
||||||
public bool Equals(AsciiString other) => ReferenceEquals(_data, other._data) || SequenceEqual(_data, other._data);
|
|
||||||
private bool SequenceEqual(byte[] data1, byte[] data2) => new Span<byte>(data1).SequenceEqual(data2);
|
|
||||||
|
|
||||||
public static bool operator ==(AsciiString a, AsciiString b) => a.Equals(b);
|
|
||||||
public static bool operator !=(AsciiString a, AsciiString b) => !a.Equals(b);
|
|
||||||
public override bool Equals(object other) => (other is AsciiString) && Equals((AsciiString)other);
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
// Copied from x64 version of string.GetLegacyNonRandomizedHashCode()
|
|
||||||
// https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/String.Comparison.cs
|
|
||||||
var data = _data;
|
|
||||||
int hash1 = 5381;
|
|
||||||
int hash2 = hash1;
|
|
||||||
foreach (int b in data)
|
|
||||||
{
|
|
||||||
hash1 = ((hash1 << 5) + hash1) ^ b;
|
|
||||||
}
|
|
||||||
return hash1 + (hash2 * 1566083941);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,200 +0,0 @@
|
||||||
// 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.Buffers;
|
|
||||||
using System.IO.Pipelines;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public partial class BenchmarkApplication : IHttpConnection
|
|
||||||
{
|
|
||||||
private State _state;
|
|
||||||
|
|
||||||
public PipeReader Reader { get; set; }
|
|
||||||
public PipeWriter Writer { get; set; }
|
|
||||||
|
|
||||||
private HttpParser<ParsingAdapter> Parser { get; } = new HttpParser<ParsingAdapter>();
|
|
||||||
|
|
||||||
public async Task ExecuteAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ProcessRequestsAsync();
|
|
||||||
|
|
||||||
Reader.Complete();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Reader.Complete(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Writer.Complete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ProcessRequestsAsync()
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var task = Reader.ReadAsync();
|
|
||||||
|
|
||||||
if (!task.IsCompleted)
|
|
||||||
{
|
|
||||||
// No more data in the input
|
|
||||||
await OnReadCompletedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await task;
|
|
||||||
var buffer = result.Buffer;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (!ParseHttpRequest(ref buffer, result.IsCompleted, out var examined))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_state == State.Body)
|
|
||||||
{
|
|
||||||
await ProcessRequestAsync();
|
|
||||||
|
|
||||||
_state = State.StartLine;
|
|
||||||
|
|
||||||
if (!buffer.IsEmpty)
|
|
||||||
{
|
|
||||||
// More input data to parse
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No more input or incomplete data, Advance the Reader
|
|
||||||
Reader.AdvanceTo(buffer.Start, examined);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ParseHttpRequest(ref ReadOnlySequence<byte> buffer, bool isCompleted, out SequencePosition examined)
|
|
||||||
{
|
|
||||||
examined = buffer.End;
|
|
||||||
|
|
||||||
var consumed = buffer.Start;
|
|
||||||
var state = _state;
|
|
||||||
|
|
||||||
if (!buffer.IsEmpty)
|
|
||||||
{
|
|
||||||
if (state == State.StartLine)
|
|
||||||
{
|
|
||||||
if (Parser.ParseRequestLine(new ParsingAdapter(this), buffer, out consumed, out examined))
|
|
||||||
{
|
|
||||||
state = State.Headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = buffer.Slice(consumed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == State.Headers)
|
|
||||||
{
|
|
||||||
var reader = new SequenceReader<byte>(buffer);
|
|
||||||
var success = Parser.ParseHeaders(new ParsingAdapter(this), ref reader);
|
|
||||||
|
|
||||||
consumed = reader.Position;
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
examined = consumed;
|
|
||||||
state = State.Body;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
examined = buffer.End;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = buffer.Slice(consumed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state != State.Body && isCompleted)
|
|
||||||
{
|
|
||||||
ThrowUnexpectedEndOfData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isCompleted)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_state = state;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnHeader(Span<byte> name, Span<byte> value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnHeadersComplete()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask OnReadCompletedAsync()
|
|
||||||
{
|
|
||||||
await Writer.FlushAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ThrowUnexpectedEndOfData()
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Unexpected end of data!");
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum State
|
|
||||||
{
|
|
||||||
StartLine,
|
|
||||||
Headers,
|
|
||||||
Body
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static BufferWriter<WriterAdapter> GetWriter(PipeWriter pipeWriter)
|
|
||||||
=> new BufferWriter<WriterAdapter>(new WriterAdapter(pipeWriter));
|
|
||||||
|
|
||||||
private struct WriterAdapter : IBufferWriter<byte>
|
|
||||||
{
|
|
||||||
public PipeWriter Writer;
|
|
||||||
|
|
||||||
public WriterAdapter(PipeWriter writer)
|
|
||||||
=> Writer = writer;
|
|
||||||
|
|
||||||
public void Advance(int count)
|
|
||||||
=> Writer.Advance(count);
|
|
||||||
|
|
||||||
public Memory<byte> GetMemory(int sizeHint = 0)
|
|
||||||
=> Writer.GetMemory(sizeHint);
|
|
||||||
|
|
||||||
public Span<byte> GetSpan(int sizeHint = 0)
|
|
||||||
=> Writer.GetSpan(sizeHint);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct ParsingAdapter : IHttpRequestLineHandler, IHttpHeadersHandler
|
|
||||||
{
|
|
||||||
public BenchmarkApplication RequestHandler;
|
|
||||||
|
|
||||||
public ParsingAdapter(BenchmarkApplication requestHandler)
|
|
||||||
=> RequestHandler = requestHandler;
|
|
||||||
|
|
||||||
public void OnHeader(Span<byte> name, Span<byte> value)
|
|
||||||
=> RequestHandler.OnHeader(name, value);
|
|
||||||
|
|
||||||
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
|
|
||||||
=> RequestHandler.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
|
|
||||||
|
|
||||||
public void OnHeadersComplete()
|
|
||||||
=> RequestHandler.OnHeadersComplete();
|
|
||||||
#if !NETCOREAPP
|
|
||||||
#error This is a .NET Core 3.0 application and needs to be compiled for <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
// 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.IO.Pipelines;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public partial class BenchmarkApplication
|
|
||||||
{
|
|
||||||
private readonly static AsciiString _applicationName = "Kestrel Platform-Level Application";
|
|
||||||
public static AsciiString ApplicationName => _applicationName;
|
|
||||||
|
|
||||||
private readonly static AsciiString _crlf = "\r\n";
|
|
||||||
private readonly static AsciiString _eoh = "\r\n\r\n"; // End Of Headers
|
|
||||||
private readonly static AsciiString _http11OK = "HTTP/1.1 200 OK\r\n";
|
|
||||||
private readonly static AsciiString _headerServer = "Server: Custom";
|
|
||||||
private readonly static AsciiString _headerContentLength = "Content-Length: ";
|
|
||||||
private readonly static AsciiString _headerContentLengthZero = "Content-Length: 0\r\n";
|
|
||||||
private readonly static AsciiString _headerContentTypeText = "Content-Type: text/plain\r\n";
|
|
||||||
private readonly static AsciiString _headerContentTypeJson = "Content-Type: application/json\r\n";
|
|
||||||
|
|
||||||
private readonly static AsciiString _plainTextBody = "Hello, World!";
|
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions();
|
|
||||||
|
|
||||||
public static class Paths
|
|
||||||
{
|
|
||||||
public readonly static AsciiString Plaintext = "/plaintext";
|
|
||||||
public readonly static AsciiString Json = "/json";
|
|
||||||
}
|
|
||||||
|
|
||||||
private RequestType _requestType;
|
|
||||||
|
|
||||||
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
|
|
||||||
{
|
|
||||||
var requestType = RequestType.NotRecognized;
|
|
||||||
if (method == HttpMethod.Get)
|
|
||||||
{
|
|
||||||
if (Paths.Plaintext.Length <= path.Length && path.StartsWith(Paths.Plaintext))
|
|
||||||
{
|
|
||||||
requestType = RequestType.PlainText;
|
|
||||||
}
|
|
||||||
else if (Paths.Json.Length <= path.Length && path.StartsWith(Paths.Json))
|
|
||||||
{
|
|
||||||
requestType = RequestType.Json;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_requestType = requestType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask ProcessRequestAsync()
|
|
||||||
{
|
|
||||||
if (_requestType == RequestType.PlainText)
|
|
||||||
{
|
|
||||||
PlainText(Writer);
|
|
||||||
}
|
|
||||||
else if (_requestType == RequestType.Json)
|
|
||||||
{
|
|
||||||
Json(Writer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Default(Writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PlainText(PipeWriter pipeWriter)
|
|
||||||
{
|
|
||||||
var writer = GetWriter(pipeWriter);
|
|
||||||
|
|
||||||
// HTTP 1.1 OK
|
|
||||||
writer.Write(_http11OK);
|
|
||||||
|
|
||||||
// Server headers
|
|
||||||
writer.Write(_headerServer);
|
|
||||||
|
|
||||||
// Date header
|
|
||||||
writer.Write(DateHeader.HeaderBytes);
|
|
||||||
|
|
||||||
// Content-Type header
|
|
||||||
writer.Write(_headerContentTypeText);
|
|
||||||
|
|
||||||
// Content-Length header
|
|
||||||
writer.Write(_headerContentLength);
|
|
||||||
writer.WriteNumeric((uint)_plainTextBody.Length);
|
|
||||||
|
|
||||||
// End of headers
|
|
||||||
writer.Write(_eoh);
|
|
||||||
|
|
||||||
// Body
|
|
||||||
writer.Write(_plainTextBody);
|
|
||||||
writer.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Json(PipeWriter pipeWriter)
|
|
||||||
{
|
|
||||||
var writer = GetWriter(pipeWriter);
|
|
||||||
|
|
||||||
// HTTP 1.1 OK
|
|
||||||
writer.Write(_http11OK);
|
|
||||||
|
|
||||||
// Server headers
|
|
||||||
writer.Write(_headerServer);
|
|
||||||
|
|
||||||
// Date header
|
|
||||||
writer.Write(DateHeader.HeaderBytes);
|
|
||||||
|
|
||||||
// Content-Type header
|
|
||||||
writer.Write(_headerContentTypeJson);
|
|
||||||
|
|
||||||
// Content-Length header
|
|
||||||
writer.Write(_headerContentLength);
|
|
||||||
var jsonPayload = JsonSerializer.SerializeToUtf8Bytes(new JsonMessage { message = "Hello, World!" }, SerializerOptions);
|
|
||||||
writer.WriteNumeric((uint)jsonPayload.Length);
|
|
||||||
|
|
||||||
// End of headers
|
|
||||||
writer.Write(_eoh);
|
|
||||||
|
|
||||||
// Body
|
|
||||||
writer.Write(jsonPayload);
|
|
||||||
writer.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Default(PipeWriter pipeWriter)
|
|
||||||
{
|
|
||||||
var writer = GetWriter(pipeWriter);
|
|
||||||
|
|
||||||
// HTTP 1.1 OK
|
|
||||||
writer.Write(_http11OK);
|
|
||||||
|
|
||||||
// Server headers
|
|
||||||
writer.Write(_headerServer);
|
|
||||||
|
|
||||||
// Date header
|
|
||||||
writer.Write(DateHeader.HeaderBytes);
|
|
||||||
|
|
||||||
// Content-Length 0
|
|
||||||
writer.Write(_headerContentLengthZero);
|
|
||||||
|
|
||||||
// End of headers
|
|
||||||
writer.Write(_crlf);
|
|
||||||
writer.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum RequestType
|
|
||||||
{
|
|
||||||
NotRecognized,
|
|
||||||
PlainText,
|
|
||||||
Json
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct JsonMessage
|
|
||||||
{
|
|
||||||
public string message { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
// 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.Net;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public static class BenchmarkConfigurationHelpers
|
|
||||||
{
|
|
||||||
public static IWebHostBuilder UseBenchmarksConfiguration(this IWebHostBuilder builder, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
builder.UseConfiguration(configuration);
|
|
||||||
|
|
||||||
// Handle the transport type
|
|
||||||
var webHost = builder.GetSetting("KestrelTransport");
|
|
||||||
|
|
||||||
// Handle the thread count
|
|
||||||
var threadCountRaw = builder.GetSetting("threadCount");
|
|
||||||
int? theadCount = null;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(threadCountRaw) &&
|
|
||||||
Int32.TryParse(threadCountRaw, out var value))
|
|
||||||
{
|
|
||||||
theadCount = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Equals(webHost, "Libuv", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
builder.UseLibuv(options =>
|
|
||||||
{
|
|
||||||
if (theadCount.HasValue)
|
|
||||||
{
|
|
||||||
options.ThreadCount = theadCount.Value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (string.Equals(webHost, "Sockets", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
builder.UseSockets(options =>
|
|
||||||
{
|
|
||||||
if (theadCount.HasValue)
|
|
||||||
{
|
|
||||||
options.IOQueueCount = theadCount.Value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IPEndPoint CreateIPEndPoint(this IConfiguration config)
|
|
||||||
{
|
|
||||||
var url = config["server.urls"] ?? config["urls"];
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(url))
|
|
||||||
{
|
|
||||||
return new IPEndPoint(IPAddress.Loopback, 8080);
|
|
||||||
}
|
|
||||||
|
|
||||||
var address = BindingAddress.Parse(url);
|
|
||||||
|
|
||||||
IPAddress ip;
|
|
||||||
|
|
||||||
if (string.Equals(address.Host, "localhost", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
ip = IPAddress.Loopback;
|
|
||||||
}
|
|
||||||
else if (!IPAddress.TryParse(address.Host, out ip))
|
|
||||||
{
|
|
||||||
ip = IPAddress.IPv6Any;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new IPEndPoint(ip, address.Port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
// 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.Buffers;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
// Same as KestrelHttpServer\src\Kestrel.Core\Internal\Http\PipelineExtensions.cs
|
|
||||||
// However methods accept T : struct, IBufferWriter<byte> rather than PipeWriter.
|
|
||||||
// This allows a struct wrapper to turn CountingBufferWriter into a non-shared generic,
|
|
||||||
// while still offering the WriteNumeric extension.
|
|
||||||
|
|
||||||
public static class BufferExtensions
|
|
||||||
{
|
|
||||||
private const int _maxULongByteLength = 20;
|
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static byte[] _numericBytesScratch;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static unsafe void WriteNumeric<T>(ref this BufferWriter<T> buffer, uint number)
|
|
||||||
where T : struct, IBufferWriter<byte>
|
|
||||||
{
|
|
||||||
const byte AsciiDigitStart = (byte)'0';
|
|
||||||
|
|
||||||
var span = buffer.Span;
|
|
||||||
var bytesLeftInBlock = span.Length;
|
|
||||||
|
|
||||||
// Fast path, try copying to the available memory directly
|
|
||||||
var advanceBy = 0;
|
|
||||||
fixed (byte* output = span)
|
|
||||||
{
|
|
||||||
var start = output;
|
|
||||||
if (number < 10 && bytesLeftInBlock >= 1)
|
|
||||||
{
|
|
||||||
start[0] = (byte)(number + AsciiDigitStart);
|
|
||||||
advanceBy = 1;
|
|
||||||
}
|
|
||||||
else if (number < 100 && bytesLeftInBlock >= 2)
|
|
||||||
{
|
|
||||||
var tens = (byte)((number * 205u) >> 11); // div10, valid to 1028
|
|
||||||
|
|
||||||
start[0] = (byte)(tens + AsciiDigitStart);
|
|
||||||
start[1] = (byte)(number - (tens * 10) + AsciiDigitStart);
|
|
||||||
advanceBy = 2;
|
|
||||||
}
|
|
||||||
else if (number < 1000 && bytesLeftInBlock >= 3)
|
|
||||||
{
|
|
||||||
var digit0 = (byte)((number * 41u) >> 12); // div100, valid to 1098
|
|
||||||
var digits01 = (byte)((number * 205u) >> 11); // div10, valid to 1028
|
|
||||||
|
|
||||||
start[0] = (byte)(digit0 + AsciiDigitStart);
|
|
||||||
start[1] = (byte)(digits01 - (digit0 * 10) + AsciiDigitStart);
|
|
||||||
start[2] = (byte)(number - (digits01 * 10) + AsciiDigitStart);
|
|
||||||
advanceBy = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (advanceBy > 0)
|
|
||||||
{
|
|
||||||
buffer.Advance(advanceBy);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriteNumericMultiWrite(ref buffer, number);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private static void WriteNumericMultiWrite<T>(ref this BufferWriter<T> buffer, uint number)
|
|
||||||
where T : struct, IBufferWriter<byte>
|
|
||||||
{
|
|
||||||
const byte AsciiDigitStart = (byte)'0';
|
|
||||||
|
|
||||||
var value = number;
|
|
||||||
var position = _maxULongByteLength;
|
|
||||||
var byteBuffer = NumericBytesScratch;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// Consider using Math.DivRem() if available
|
|
||||||
var quotient = value / 10;
|
|
||||||
byteBuffer[--position] = (byte)(AsciiDigitStart + (value - quotient * 10)); // 0x30 = '0'
|
|
||||||
value = quotient;
|
|
||||||
}
|
|
||||||
while (value != 0);
|
|
||||||
|
|
||||||
var length = _maxULongByteLength - position;
|
|
||||||
buffer.Write(new ReadOnlySpan<byte>(byteBuffer, position, length));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] NumericBytesScratch => _numericBytesScratch ?? CreateNumericBytesScratch();
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private static byte[] CreateNumericBytesScratch()
|
|
||||||
{
|
|
||||||
var bytes = new byte[_maxULongByteLength];
|
|
||||||
_numericBytesScratch = bytes;
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
// 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.Buffers;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public ref struct BufferWriter<T> where T : IBufferWriter<byte>
|
|
||||||
{
|
|
||||||
private T _output;
|
|
||||||
private Span<byte> _span;
|
|
||||||
private int _buffered;
|
|
||||||
|
|
||||||
public BufferWriter(T output)
|
|
||||||
{
|
|
||||||
_buffered = 0;
|
|
||||||
_output = output;
|
|
||||||
_span = output.GetSpan();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Span<byte> Span => _span;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Commit()
|
|
||||||
{
|
|
||||||
var buffered = _buffered;
|
|
||||||
if (buffered > 0)
|
|
||||||
{
|
|
||||||
_buffered = 0;
|
|
||||||
_output.Advance(buffered);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Advance(int count)
|
|
||||||
{
|
|
||||||
_buffered += count;
|
|
||||||
_span = _span.Slice(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Write(ReadOnlySpan<byte> source)
|
|
||||||
{
|
|
||||||
if (_span.Length >= source.Length)
|
|
||||||
{
|
|
||||||
source.CopyTo(_span);
|
|
||||||
Advance(source.Length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriteMultiBuffer(source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Ensure(int count = 1)
|
|
||||||
{
|
|
||||||
if (_span.Length < count)
|
|
||||||
{
|
|
||||||
EnsureMore(count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private void EnsureMore(int count = 0)
|
|
||||||
{
|
|
||||||
if (_buffered > 0)
|
|
||||||
{
|
|
||||||
Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
_span = _output.GetSpan(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteMultiBuffer(ReadOnlySpan<byte> source)
|
|
||||||
{
|
|
||||||
while (source.Length > 0)
|
|
||||||
{
|
|
||||||
if (_span.Length == 0)
|
|
||||||
{
|
|
||||||
EnsureMore();
|
|
||||||
}
|
|
||||||
|
|
||||||
var writable = Math.Min(source.Length, _span.Length);
|
|
||||||
source.Slice(0, writable).CopyTo(_span);
|
|
||||||
source = source.Slice(writable);
|
|
||||||
Advance(writable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
// 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.Buffers.Text;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Manages the generation of the date header value.
|
|
||||||
/// </summary>
|
|
||||||
internal static class DateHeader
|
|
||||||
{
|
|
||||||
const int prefixLength = 8; // "\r\nDate: ".Length
|
|
||||||
const int dateTimeRLength = 29; // Wed, 14 Mar 2018 14:20:00 GMT
|
|
||||||
const int suffixLength = 2; // crlf
|
|
||||||
const int suffixIndex = dateTimeRLength + prefixLength;
|
|
||||||
|
|
||||||
private static readonly Timer s_timer = new Timer((s) => {
|
|
||||||
SetDateValues(DateTimeOffset.UtcNow);
|
|
||||||
}, null, 1000, 1000);
|
|
||||||
|
|
||||||
private static byte[] s_headerBytesMaster = new byte[prefixLength + dateTimeRLength + suffixLength];
|
|
||||||
private static byte[] s_headerBytesScratch = new byte[prefixLength + dateTimeRLength + suffixLength];
|
|
||||||
|
|
||||||
static DateHeader()
|
|
||||||
{
|
|
||||||
var utf8 = Encoding.ASCII.GetBytes("\r\nDate: ").AsSpan();
|
|
||||||
utf8.CopyTo(s_headerBytesMaster);
|
|
||||||
utf8.CopyTo(s_headerBytesScratch);
|
|
||||||
s_headerBytesMaster[suffixIndex] = (byte)'\r';
|
|
||||||
s_headerBytesMaster[suffixIndex + 1] = (byte)'\n';
|
|
||||||
s_headerBytesScratch[suffixIndex] = (byte)'\r';
|
|
||||||
s_headerBytesScratch[suffixIndex + 1] = (byte)'\n';
|
|
||||||
SetDateValues(DateTimeOffset.UtcNow);
|
|
||||||
SyncDateTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SyncDateTimer() => s_timer.Change(1000, 1000);
|
|
||||||
|
|
||||||
public static ReadOnlySpan<byte> HeaderBytes => s_headerBytesMaster;
|
|
||||||
|
|
||||||
private static void SetDateValues(DateTimeOffset value)
|
|
||||||
{
|
|
||||||
lock (s_headerBytesScratch)
|
|
||||||
{
|
|
||||||
if (!Utf8Formatter.TryFormat(value, s_headerBytesScratch.AsSpan(prefixLength), out int written, 'R'))
|
|
||||||
{
|
|
||||||
throw new Exception("date time format failed");
|
|
||||||
}
|
|
||||||
Debug.Assert(written == dateTimeRLength);
|
|
||||||
var temp = s_headerBytesMaster;
|
|
||||||
s_headerBytesMaster = s_headerBytesScratch;
|
|
||||||
s_headerBytesScratch = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
<Project>
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<!-- This file prevents any other Directory.Build.targets from a parent folder to be loaded -->
|
|
||||||
<Project>
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
// 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.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Connections;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public static class HttpApplicationConnectionBuilderExtensions
|
|
||||||
{
|
|
||||||
public static IConnectionBuilder UseHttpApplication<TConnection>(this IConnectionBuilder builder) where TConnection : IHttpConnection, new()
|
|
||||||
{
|
|
||||||
return builder.Use(next => new HttpApplication<TConnection>().ExecuteAsync);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class HttpApplication<TConnection> where TConnection : IHttpConnection, new()
|
|
||||||
{
|
|
||||||
public Task ExecuteAsync(ConnectionContext connection)
|
|
||||||
{
|
|
||||||
var httpConnection = new TConnection
|
|
||||||
{
|
|
||||||
Reader = connection.Transport.Input,
|
|
||||||
Writer = connection.Transport.Output
|
|
||||||
};
|
|
||||||
return httpConnection.ExecuteAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
// 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.IO.Pipelines;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public interface IHttpConnection : IHttpHeadersHandler, IHttpRequestLineHandler
|
|
||||||
{
|
|
||||||
PipeReader Reader { get; set; }
|
|
||||||
PipeWriter Writer { get; set; }
|
|
||||||
Task ExecuteAsync();
|
|
||||||
ValueTask OnReadCompletedAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<packageSources>
|
|
||||||
<clear />
|
|
||||||
<add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
|
|
||||||
<add key="extensions" value="https://dotnetfeed.blob.core.windows.net/aspnet-extensions/index.json" />
|
|
||||||
<add key="entityframeworkcore" value="https://dotnetfeed.blob.core.windows.net/aspnet-entityframeworkcore/index.json" />
|
|
||||||
<add key="aspnetcore-tooling" value="https://dotnetfeed.blob.core.windows.net/aspnet-aspnetcore-tooling/index.json" />
|
|
||||||
<add key="aspnetcore" value="https://dotnetfeed.blob.core.windows.net/aspnet-aspnetcore/index.json" />
|
|
||||||
<add key="NuGet.org" value="https://api.nuget.org/v3/index.json" />
|
|
||||||
</packageSources>
|
|
||||||
</configuration>
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
|
||||||
<TargetFramework Condition="'$(BenchmarksTargetFramework)' != ''">$(BenchmarksTargetFramework)</TargetFramework>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
<IsTestAssetProject>true</IsTestAssetProject>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<!-- These references are used when running locally -->
|
|
||||||
<ItemGroup Condition="'$(BenchmarksTargetFramework)' == ''">
|
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
|
|
||||||
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" />
|
|
||||||
|
|
||||||
<Reference Include="Microsoft.Extensions.Configuration.CommandLine" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- These references are used when running on the Benchmarks Server -->
|
|
||||||
<ItemGroup Condition="'$(BenchmarksTargetFramework)' != ''">
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="$(MicrosoftAspNetCoreAppPackageVersion)" />
|
|
||||||
|
|
||||||
<FrameworkReference Update="Microsoft.AspNetCore.App" RuntimeFrameworkVersion="$(MicrosoftAspNetCoreAppPackageVersion)" />
|
|
||||||
<FrameworkReference Update="Microsoft.NETCore.App" RuntimeFrameworkVersion="$(MicrosoftNETCoreAppPackageVersion)" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// 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.Net;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Console.WriteLine(BenchmarkApplication.ApplicationName);
|
|
||||||
Console.WriteLine(BenchmarkApplication.Paths.Plaintext);
|
|
||||||
Console.WriteLine(BenchmarkApplication.Paths.Json);
|
|
||||||
DateHeader.SyncDateTimer();
|
|
||||||
|
|
||||||
BuildWebHost(args).Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IWebHost BuildWebHost(string[] args)
|
|
||||||
{
|
|
||||||
var config = new ConfigurationBuilder()
|
|
||||||
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
|
|
||||||
.AddCommandLine(args)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var host = new WebHostBuilder()
|
|
||||||
.UseBenchmarksConfiguration(config)
|
|
||||||
.UseKestrel((context, options) =>
|
|
||||||
{
|
|
||||||
IPEndPoint endPoint = context.Configuration.CreateIPEndPoint();
|
|
||||||
|
|
||||||
options.Listen(endPoint, builder =>
|
|
||||||
{
|
|
||||||
builder.UseHttpApplication<BenchmarkApplication>();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.UseStartup<Startup>()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
// 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.Builder;
|
|
||||||
|
|
||||||
namespace PlatformBenchmarks
|
|
||||||
{
|
|
||||||
public class Startup
|
|
||||||
{
|
|
||||||
public void Configure(IApplicationBuilder app)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"Default": {
|
|
||||||
"Client": "Wrk",
|
|
||||||
"PresetHeaders": "Json",
|
|
||||||
|
|
||||||
"Source": {
|
|
||||||
"Repository": "https://github.com/dotnet/aspnetcore.git",
|
|
||||||
"BranchOrCommit": "master",
|
|
||||||
"Project": "src/Servers/Kestrel/perf/PlatformBenchmarks/PlatformBenchmarks.csproj"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"JsonPlatform": {
|
|
||||||
"Path": "/json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
{
|
|
||||||
"Default": {
|
|
||||||
"Client": "Wrk",
|
|
||||||
"PresetHeaders": "Plaintext",
|
|
||||||
"ClientProperties": {
|
|
||||||
"ScriptName": "pipeline",
|
|
||||||
"PipelineDepth": 16
|
|
||||||
},
|
|
||||||
"Source": {
|
|
||||||
"Repository": "https://github.com/dotnet/aspnetcore.git",
|
|
||||||
"BranchOrCommit": "master",
|
|
||||||
"Project": "src/Servers/Kestrel/perf/PlatformBenchmarks/PlatformBenchmarks.csproj"
|
|
||||||
},
|
|
||||||
"Port": 8080
|
|
||||||
},
|
|
||||||
"PlaintextPlatform": {
|
|
||||||
"Path": "/plaintext"
|
|
||||||
},
|
|
||||||
"PlaintextNonPipelinedPlatform": {
|
|
||||||
"Path": "/plaintext",
|
|
||||||
"ClientProperties": {
|
|
||||||
"ScriptName": "",
|
|
||||||
"PipelineDepth": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue