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