From c52a3bf534109047641b1e28f3b16e4c705fdbbb Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 14 May 2018 03:26:30 +0100 Subject: [PATCH] [Platform] Speedups (#2569) - Move ProcessRequest into ParseHttpRequest (out of state machine) - Use struct adapter for parsing - Non-virtual calls - RequestType to enum - Readonly statics and early init statics - Early init path statics - Faster Json fall through - CountingBufferWriter -> Smaller BufferWriter - Pre-init date static - Smaller WriteNumeric --- .../PlatformBenchmarks/AsciiString.cs | 2 +- .../BenchmarkApplication.HttpConnection.cs | 171 ++++++++++++++++++ .../BenchmarkApplication.cs | 89 ++++----- .../PlatformBenchmarks/BufferExtensions.cs | 104 +++++++++++ .../PlatformBenchmarks/BufferWriter.cs | 94 ++++++++++ .../PlatformBenchmarks/DateHeader.cs | 3 + .../PlatformBenchmarks/HttpApplication.cs | 136 +------------- .../PlatformBenchmarks/IHttpConnection.cs | 17 ++ benchmarkapps/PlatformBenchmarks/Program.cs | 10 +- benchmarkapps/PlatformBenchmarks/Startup.cs | 3 - 10 files changed, 443 insertions(+), 186 deletions(-) create mode 100644 benchmarkapps/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs create mode 100644 benchmarkapps/PlatformBenchmarks/BufferExtensions.cs create mode 100644 benchmarkapps/PlatformBenchmarks/BufferWriter.cs create mode 100644 benchmarkapps/PlatformBenchmarks/IHttpConnection.cs diff --git a/benchmarkapps/PlatformBenchmarks/AsciiString.cs b/benchmarkapps/PlatformBenchmarks/AsciiString.cs index f84d42c978..ef2a32d615 100644 --- a/benchmarkapps/PlatformBenchmarks/AsciiString.cs +++ b/benchmarkapps/PlatformBenchmarks/AsciiString.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; namespace PlatformBenchmarks { - public struct AsciiString : IEquatable + public readonly struct AsciiString : IEquatable { private readonly byte[] _data; diff --git a/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs b/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs new file mode 100644 index 0000000000..d7aaa83c87 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs @@ -0,0 +1,171 @@ +// 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 Parser { get; } = new HttpParser(); + + 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; + if (!ParseHttpRequest(ref result)) + { + break; + } + + if (_state == State.Body) + { + await ProcessRequestAsync(); + + _state = State.StartLine; + } + } + } + + // Should be `in` but ReadResult isn't readonly struct + private bool ParseHttpRequest(ref ReadResult result) + { + var buffer = result.Buffer; + var consumed = buffer.Start; + var examined = buffer.End; + var state = _state; + + if (!buffer.IsEmpty) + { + var parsingStartLine = state == State.StartLine; + if (parsingStartLine) + { + if (Parser.ParseRequestLine(new ParsingAdapter(this), buffer, out consumed, out examined)) + { + state = State.Headers; + } + } + + if (state == State.Headers) + { + if (parsingStartLine) + { + buffer = buffer.Slice(consumed); + } + + if (Parser.ParseHeaders(new ParsingAdapter(this), buffer, out consumed, out examined, out int consumedBytes)) + { + state = State.Body; + } + } + + if (state != State.Body && result.IsCompleted) + { + ThrowUnexpectedEndOfData(); + } + } + else if (result.IsCompleted) + { + return false; + } + + _state = state; + Reader.AdvanceTo(consumed, examined); + return true; + } + + public void OnHeader(Span name, Span value) + { + } + + 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 GetWriter(PipeWriter pipeWriter) + => new BufferWriter(new WriterAdapter(pipeWriter)); + + private struct WriterAdapter : IBufferWriter + { + public PipeWriter Writer; + + public WriterAdapter(PipeWriter writer) + => Writer = writer; + + public void Advance(int count) + => Writer.Advance(count); + + public Memory GetMemory(int sizeHint = 0) + => Writer.GetMemory(sizeHint); + + public Span 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 name, Span value) + => RequestHandler.OnHeader(name, value); + + public void OnStartLine(HttpMethod method, HttpVersion version, Span target, Span path, Span query, Span customMethod, bool pathEncoded) + => RequestHandler.OnStartLine(method, version, target, path, query, customMethod, pathEncoded); + } + } + +} diff --git a/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.cs b/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.cs index d65452c13c..f494902681 100644 --- a/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.cs +++ b/benchmarkapps/PlatformBenchmarks/BenchmarkApplication.cs @@ -2,7 +2,6 @@ // 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.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; @@ -10,57 +9,55 @@ using Utf8Json; namespace PlatformBenchmarks { - public class BenchmarkApplication : HttpConnection + public partial class BenchmarkApplication { - private static AsciiString _crlf = "\r\n"; - private static AsciiString _eoh = "\r\n\r\n"; // End Of Headers - private static AsciiString _http11OK = "HTTP/1.1 200 OK\r\n"; - private static AsciiString _headerServer = "Server: Custom"; - private static AsciiString _headerContentLength = "Content-Length: "; - private static AsciiString _headerContentLengthZero = "Content-Length: 0\r\n"; - private static AsciiString _headerContentTypeText = "Content-Type: text/plain\r\n"; - private static AsciiString _headerContentTypeJson = "Content-Type: application/json\r\n"; + 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 static AsciiString _plainTextBody = "Hello, World!"; + private readonly static AsciiString _plainTextBody = "Hello, World!"; - private static class Paths + public static class Paths { - public static AsciiString Plaintext = "/plaintext"; - public static AsciiString Json = "/json"; + public readonly static AsciiString Plaintext = "/plaintext"; + public readonly static AsciiString Json = "/json"; } - private bool _isPlainText; - private bool _isJson; + private RequestType _requestType; - public override void OnStartLine(HttpMethod method, HttpVersion version, Span target, Span path, Span query, Span customMethod, bool pathEncoded) + public void OnStartLine(HttpMethod method, HttpVersion version, Span target, Span path, Span query, Span customMethod, bool pathEncoded) { - if (path.StartsWith(Paths.Plaintext) && method == HttpMethod.Get) + var requestType = RequestType.NotRecognized; + if (method == HttpMethod.Get) { - _isPlainText = true; - } - else if (path.StartsWith(Paths.Json) && method == HttpMethod.Get) - { - _isJson = true; - } - else - { - _isPlainText = false; - _isJson = false; + 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 override void OnHeader(Span name, Span value) + public ValueTask ProcessRequestAsync() { - } - - public override ValueTask ProcessRequestAsync() - { - if (_isPlainText) + if (_requestType == RequestType.PlainText) { PlainText(Writer); } - else if (_isJson) + else if (_requestType == RequestType.Json) { Json(Writer); } @@ -72,13 +69,10 @@ namespace PlatformBenchmarks return default; } - public override async ValueTask OnReadCompletedAsync() - { - await Writer.FlushAsync(); - } private static void PlainText(PipeWriter pipeWriter) { - var writer = new CountingBufferWriter(pipeWriter); + var writer = GetWriter(pipeWriter); + // HTTP 1.1 OK writer.Write(_http11OK); @@ -93,7 +87,7 @@ namespace PlatformBenchmarks // Content-Length header writer.Write(_headerContentLength); - writer.WriteNumeric((ulong)_plainTextBody.Length); + writer.WriteNumeric((uint)_plainTextBody.Length); // End of headers writer.Write(_eoh); @@ -105,7 +99,7 @@ namespace PlatformBenchmarks private static void Json(PipeWriter pipeWriter) { - var writer = new CountingBufferWriter(pipeWriter); + var writer = GetWriter(pipeWriter); // HTTP 1.1 OK writer.Write(_http11OK); @@ -122,7 +116,7 @@ namespace PlatformBenchmarks // Content-Length header writer.Write(_headerContentLength); var jsonPayload = JsonSerializer.SerializeUnsafe(new { message = "Hello, World!" }); - writer.WriteNumeric((ulong)jsonPayload.Count); + writer.WriteNumeric((uint)jsonPayload.Count); // End of headers writer.Write(_eoh); @@ -134,7 +128,7 @@ namespace PlatformBenchmarks private static void Default(PipeWriter pipeWriter) { - var writer = new CountingBufferWriter(pipeWriter); + var writer = GetWriter(pipeWriter); // HTTP 1.1 OK writer.Write(_http11OK); @@ -152,5 +146,12 @@ namespace PlatformBenchmarks writer.Write(_crlf); writer.Commit(); } + + private enum RequestType + { + NotRecognized, + PlainText, + Json + } } } diff --git a/benchmarkapps/PlatformBenchmarks/BufferExtensions.cs b/benchmarkapps/PlatformBenchmarks/BufferExtensions.cs new file mode 100644 index 0000000000..a6c59a862e --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/BufferExtensions.cs @@ -0,0 +1,104 @@ +// 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; +using System.Runtime.InteropServices; + +namespace PlatformBenchmarks +{ + // Same as KestrelHttpServer\src\Kestrel.Core\Internal\Http\PipelineExtensions.cs + // However methods accept T : struct, IBufferWriter 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(ref this BufferWriter buffer, uint number) + where T : struct, IBufferWriter + { + 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 = &MemoryMarshal.GetReference(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(ref this BufferWriter buffer, uint number) + where T : struct, IBufferWriter + { + 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(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; + } + } +} diff --git a/benchmarkapps/PlatformBenchmarks/BufferWriter.cs b/benchmarkapps/PlatformBenchmarks/BufferWriter.cs new file mode 100644 index 0000000000..9381fd9d76 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/BufferWriter.cs @@ -0,0 +1,94 @@ +// 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 where T : IBufferWriter + { + private T _output; + private Span _span; + private int _buffered; + + public BufferWriter(T output) + { + _buffered = 0; + _output = output; + _span = output.GetSpan(); + } + + public Span 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 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(); + } + + _output.GetMemory(count); + _span = _output.GetSpan(); + } + + private void WriteMultiBuffer(ReadOnlySpan 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); + } + } + } +} diff --git a/benchmarkapps/PlatformBenchmarks/DateHeader.cs b/benchmarkapps/PlatformBenchmarks/DateHeader.cs index 5cfbc5c3b2..18897dfcb3 100644 --- a/benchmarkapps/PlatformBenchmarks/DateHeader.cs +++ b/benchmarkapps/PlatformBenchmarks/DateHeader.cs @@ -37,8 +37,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http 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 HeaderBytes => s_headerBytesMaster; private static void SetDateValues(DateTimeOffset value) diff --git a/benchmarkapps/PlatformBenchmarks/HttpApplication.cs b/benchmarkapps/PlatformBenchmarks/HttpApplication.cs index e9b20f5aaa..48c736ab27 100644 --- a/benchmarkapps/PlatformBenchmarks/HttpApplication.cs +++ b/benchmarkapps/PlatformBenchmarks/HttpApplication.cs @@ -1,161 +1,29 @@ // 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.Threading.Tasks; using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; namespace PlatformBenchmarks { public static class HttpApplicationConnectionBuilderExtensions { - public static IConnectionBuilder UseHttpApplication(this IConnectionBuilder builder) where TConnection : HttpConnection, new() + public static IConnectionBuilder UseHttpApplication(this IConnectionBuilder builder) where TConnection : IHttpConnection, new() { return builder.Use(next => new HttpApplication().ExecuteAsync); } } - public class HttpApplication where TConnection : HttpConnection, new() + public class HttpApplication where TConnection : IHttpConnection, new() { public Task ExecuteAsync(ConnectionContext connection) { - var parser = new HttpParser(); - var httpConnection = new TConnection { - Parser = parser, Reader = connection.Transport.Input, Writer = connection.Transport.Output }; return httpConnection.ExecuteAsync(); } } - - public class HttpConnection : IHttpHeadersHandler, IHttpRequestLineHandler - { - private State _state; - - public PipeReader Reader { get; set; } - public PipeWriter Writer { get; set; } - - internal HttpParser Parser { get; set; } - - public virtual void OnHeader(Span name, Span value) - { - - } - - public virtual void OnStartLine(HttpMethod method, HttpVersion version, Span target, Span path, Span query, Span customMethod, bool pathEncoded) - { - - } - - public virtual ValueTask ProcessRequestAsync() - { - return default; - } - - public virtual ValueTask OnReadCompletedAsync() - { - return default; - } - - 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; - var consumed = buffer.Start; - var examined = buffer.End; - - if (!buffer.IsEmpty) - { - ParseHttpRequest(buffer, out consumed, out examined); - - if (_state != State.Body && result.IsCompleted) - { - ThrowUnexpectedEndOfData(); - } - } - else if (result.IsCompleted) - { - break; - } - - Reader.AdvanceTo(consumed, examined); - - if (_state == State.Body) - { - await ProcessRequestAsync(); - - _state = State.StartLine; - } - } - } - - private void ParseHttpRequest(in ReadOnlySequence buffer, out SequencePosition consumed, out SequencePosition examined) - { - consumed = buffer.Start; - examined = buffer.End; - - var parsingStartLine = _state == State.StartLine; - if (parsingStartLine) - { - if (Parser.ParseRequestLine(this, buffer, out consumed, out examined)) - { - _state = State.Headers; - } - } - - if (_state == State.Headers) - { - if (Parser.ParseHeaders(this, parsingStartLine ? buffer.Slice(consumed) : buffer, out consumed, out examined, out int consumedBytes)) - { - _state = State.Body; - } - } - } - - private static void ThrowUnexpectedEndOfData() - { - throw new InvalidOperationException("Unexpected end of data!"); - } - - private enum State - { - StartLine, - Headers, - Body - } - } } diff --git a/benchmarkapps/PlatformBenchmarks/IHttpConnection.cs b/benchmarkapps/PlatformBenchmarks/IHttpConnection.cs new file mode 100644 index 0000000000..2d58819ae2 --- /dev/null +++ b/benchmarkapps/PlatformBenchmarks/IHttpConnection.cs @@ -0,0 +1,17 @@ +// 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(); + } +} diff --git a/benchmarkapps/PlatformBenchmarks/Program.cs b/benchmarkapps/PlatformBenchmarks/Program.cs index 7776cbd1ce..6b801e15f3 100644 --- a/benchmarkapps/PlatformBenchmarks/Program.cs +++ b/benchmarkapps/PlatformBenchmarks/Program.cs @@ -3,12 +3,9 @@ using System; using System.Net; -using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; namespace PlatformBenchmarks { @@ -16,6 +13,11 @@ namespace PlatformBenchmarks { 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(); } diff --git a/benchmarkapps/PlatformBenchmarks/Startup.cs b/benchmarkapps/PlatformBenchmarks/Startup.cs index bfc1f70375..a2bcbe88fc 100644 --- a/benchmarkapps/PlatformBenchmarks/Startup.cs +++ b/benchmarkapps/PlatformBenchmarks/Startup.cs @@ -2,10 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Net.WebSockets; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; namespace PlatformBenchmarks {