Browse Source

Add .NET CoreRT (AOT) (#3906)

Ben Adams 7 years ago
parent
commit
559eb38c25
20 changed files with 1051 additions and 0 deletions
  1. 38 0
      frameworks/CSharp/aspnetcore-corert/.gitignore
  2. 50 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/AsciiString.cs
  3. 192 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs
  4. 39 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Plaintext.cs
  5. 141 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.cs
  6. 60 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs
  7. 29 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/HttpApplication.cs
  8. 17 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/IHttpConnection.cs
  9. 8 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/NuGet.Config
  10. 22 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/PlatformBenchmarks.csproj
  11. 52 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Program.cs
  12. 16 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Startup.cs
  13. 115 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utilities/BufferExtensions.cs
  14. 95 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utilities/BufferWriter.cs
  15. 62 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utilities/DateHeader.cs
  16. 3 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.json
  17. 48 0
      frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/rd.xml
  18. 27 0
      frameworks/CSharp/aspnetcore-corert/README.md
  19. 14 0
      frameworks/CSharp/aspnetcore-corert/aspcore-corert.dockerfile
  20. 23 0
      frameworks/CSharp/aspnetcore-corert/benchmark_config.json

+ 38 - 0
frameworks/CSharp/aspnetcore-corert/.gitignore

@@ -0,0 +1,38 @@
+[Oo]bj/
+[Bb]in/
+[Oo]ut/
+TestResults/
+.nuget/
+*.sln.ide/
+_ReSharper.*/
+.idea/
+packages/
+artifacts/
+PublishProfiles/
+.vs/
+*.user
+*.suo
+*.cache
+*.docstates
+_ReSharper.*
+nuget.exe
+*net45.csproj
+*net451.csproj
+*k10.csproj
+*.psess
+*.vsp
+*.pidb
+*.userprefs
+*DS_Store
+*.ncrunchsolution
+*.*sdf
+*.ipch
+*.swp
+*~
+.build/
+.testPublish/
+launchSettings.json
+BenchmarkDotNet.Artifacts/
+BDN.Generated/
+binaries/
+global.json

+ 50 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/AsciiString.cs

@@ -0,0 +1,50 @@
+// 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;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
+
+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() => HttpUtilities.GetAsciiStringNonNullCharacters(_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);
+        }
+
+    }
+}

+ 192 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs

@@ -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.Buffers;
+using System.IO.Pipelines;
+using System.Runtime.CompilerServices;
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
+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; }
+
+        protected HtmlEncoder HtmlEncoder { get; } = CreateHtmlEncoder();
+
+        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)
+                {
+                    if (Parser.ParseHeaders(new ParsingAdapter(this), buffer, out consumed, out examined, out int consumedBytes))
+                    {
+                        state = State.Body;
+                    }
+
+                    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 async ValueTask OnReadCompletedAsync()
+        {
+            await Writer.FlushAsync();
+        }
+
+        private static HtmlEncoder CreateHtmlEncoder()
+        {
+            var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
+            settings.AllowCharacter('\u2014');  // allow EM DASH through
+            return HtmlEncoder.Create(settings);
+        }
+
+        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);
+        }
+    }
+
+}

+ 39 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.Plaintext.cs

@@ -0,0 +1,39 @@
+// 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 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+
+namespace PlatformBenchmarks
+{
+    public partial class BenchmarkApplication
+    {
+        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();
+        }
+    }
+}

+ 141 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkApplication.cs

@@ -0,0 +1,141 @@
+// 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.IO.Pipelines;
+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 _headerContentTypeHtml = "Content-Type: text/html; charset=UTF-8\r\n";
+
+        private readonly static AsciiString _plainTextBody = "Hello, World!";
+
+        private readonly static AsciiString _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
+        private readonly static AsciiString _fortunesRowStart = "<tr><td>";
+        private readonly static AsciiString _fortunesColumn = "</td><td>";
+        private readonly static AsciiString _fortunesRowEnd = "</td></tr>";
+        private readonly static AsciiString _fortunesTableEnd = "</table></body></html>";
+        private readonly static AsciiString _contentLengthGap = new string(' ', 4);
+
+        public static class Paths
+        {
+            public readonly static AsciiString SingleQuery = "/db";
+            public readonly static AsciiString Json = "/json";
+            public readonly static AsciiString Fortunes = "/fortunes";
+            public readonly static AsciiString Plaintext = "/plaintext";
+            public readonly static AsciiString Updates = "/updates/queries=";
+        }
+
+        private RequestType _requestType;
+        private int _queries;
+
+        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)
+            {
+                var pathLength = path.Length;
+                if (Paths.SingleQuery.Length <= pathLength && path.StartsWith(Paths.SingleQuery))
+                {
+                    requestType = RequestType.SingleQuery;
+                }
+                else if (Paths.Json.Length <= pathLength && path.StartsWith(Paths.Json))
+                {
+                    requestType = RequestType.Json;
+                }
+                else if (Paths.Fortunes.Length <= pathLength && path.StartsWith(Paths.Fortunes))
+                {
+                    requestType = RequestType.Fortunes;
+                }
+                else if (Paths.Plaintext.Length <= pathLength && path.StartsWith(Paths.Plaintext))
+                {
+                    requestType = RequestType.PlainText;
+                }
+                else if (Paths.Updates.Length <= pathLength && path.StartsWith(Paths.Updates))
+                {
+                    _queries = ParseQueries(path);
+                    requestType = RequestType.Updates;
+                }
+            }
+
+            _requestType = requestType;
+        }
+
+        private static int ParseQueries(Span<byte> path)
+        {
+            if (!Utf8Parser.TryParse(path.Slice(Paths.Updates.Length), out int queries, out _) || queries < 1)
+            {
+                queries = 1;
+            }
+            else if (queries > 500)
+            {
+                queries = 500;
+            }
+
+            return queries;
+        }
+
+        public Task ProcessRequestAsync()
+        {
+            var requestType = _requestType;
+            var task = Task.CompletedTask;
+            if (requestType == RequestType.PlainText)
+            {
+                PlainText(Writer);
+            }
+            else
+            {
+                Default(Writer);
+            }
+
+            return task;
+        }
+
+        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,
+            Fortunes,
+            SingleQuery,
+            Updates
+        }
+    }
+}

+ 60 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/BenchmarkConfigurationHelpers.cs

@@ -0,0 +1,60 @@
+// 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;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+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;
+            }
+
+            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 = ServerAddress.FromUrl(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);
+        }
+    }
+}

+ 29 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/HttpApplication.cs

@@ -0,0 +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.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();
+        }
+    }
+}

+ 17 - 0
frameworks/CSharp/aspnetcore-corert/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();
+    }
+}

+ 8 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/NuGet.Config

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <packageSources>
+    <clear />
+    <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
+    <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
+  </packageSources>
+</configuration>

+ 22 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/PlatformBenchmarks.csproj

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <LangVersion>latest</LangVersion>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  
+  <ItemGroup>
+    <RdXmlFile Include="rd.xml" />
+  </ItemGroup>
+  
+  <ItemGroup>
+      <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.AspNetCore" Version="2.1.1" />
+    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="1.0.0-alpha-*" />
+  </ItemGroup>
+</Project>

+ 52 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Program.cs

@@ -0,0 +1,52 @@
+// 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.Server.Kestrel.Core.Internal.Http;
+using Microsoft.Extensions.Configuration;
+
+namespace PlatformBenchmarks
+{
+    public class Program
+    {
+        public static string[] Args;
+
+        public static void Main(string[] args)
+        {
+            Args = args;
+
+            Console.WriteLine(BenchmarkApplication.ApplicationName);
+            Console.WriteLine(BenchmarkApplication.Paths.Plaintext);
+            DateHeader.SyncDateTimer();
+
+            BuildWebHost(args).Run();
+        }
+
+        public static IWebHost BuildWebHost(string[] args)
+        {
+            var config = new ConfigurationBuilder()
+                .AddJsonFile("appsettings.json")
+                .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;
+        }
+    }
+}

+ 16 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Startup.cs

@@ -0,0 +1,16 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Builder;
+
+namespace PlatformBenchmarks
+{
+    public class Startup
+    {
+        public void Configure(IApplicationBuilder app)
+        {
+            
+        }
+    }
+}

+ 115 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utilities/BufferExtensions.cs

@@ -0,0 +1,115 @@
+// 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.Buffers.Text;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+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;
+
+        internal static void WriteUtf8String<T>(ref this BufferWriter<T> buffer, string text)
+             where T : struct, IBufferWriter<byte>
+        {
+            var byteCount = Encoding.UTF8.GetByteCount(text);
+            buffer.Ensure(byteCount);
+            byteCount = Encoding.UTF8.GetBytes(text.AsSpan(), buffer.Span);
+            buffer.Advance(byteCount);
+        }
+
+        [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 = &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<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;
+        }
+    }
+}

+ 95 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utilities/BufferWriter.cs

@@ -0,0 +1,95 @@
+// 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;
+        public int Buffered => _buffered;
+
+        [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();
+            }
+
+            _output.GetMemory(count);
+            _span = _output.GetSpan();
+        }
+
+        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);
+            }
+        }
+    }
+}

+ 62 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/Utilities/DateHeader.cs

@@ -0,0 +1,62 @@
+// 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;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
+{
+    /// <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;
+            }
+        }
+    }
+}

+ 3 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/appsettings.json

@@ -0,0 +1,3 @@
+{
+  "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnetcore-Benchmarks;Trusted_Connection=True;MultipleActiveResultSets=true"
+}

+ 48 - 0
frameworks/CSharp/aspnetcore-corert/PlatformBenchmarks/rd.xml

@@ -0,0 +1,48 @@
+<Directives>
+    <Application>
+        <Assembly Name="PlatformBenchmarks" Dynamic="Required All" />
+        <Assembly Name="Microsoft.AspNetCore.Server.Kestrel.Core">
+            <Type Name="Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer" Dynamic="Required All" />
+            <Type Name="Microsoft.AspNetCore.Server.Kestrel.Core.Internal.KestrelServerOptionsSetup" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.AspNetCore.Server.Kestrel" Dynamic="Required All"/>
+        <Assembly Name="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets">
+            <Type Name="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory" Dynamic="Required All" />
+            <Type Name="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.Extensions.DependencyInjection" Dynamic="Required All">
+            <Type Name="Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory" Dynamic="Required All" />
+            <Type Name="Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver" Dynamic="Required All" />
+            <Type Name="Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.Extensions.Options">
+            <Type Name="Microsoft.Extensions.Options.OptionsManager`1[[Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions,Microsoft.AspNetCore.Server.Kestrel.Core]]" Dynamic="Required All" />
+            <Type Name="Microsoft.Extensions.Options.OptionsFactory`1[[Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions,Microsoft.AspNetCore.Server.Kestrel.Core]]" Dynamic="Required All" />
+            <Type Name="Microsoft.Extensions.Options.OptionsMonitor`1[[Microsoft.Extensions.Logging.Console.ConsoleLoggerOptions,Microsoft.Extensions.Logging.Console]]" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.AspNetCore.Http">
+            <Type Name="Microsoft.AspNetCore.Http.HttpContextFactory" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.AspNetCore.HostFiltering">
+            <Type Name="Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.AspNetCore.Hosting" Dynamic="Required All">
+            <Type Name="Microsoft.AspNetCore.Hosting.Internal.ApplicationLifetime" Dynamic="Required All" />
+            <Type Name="Microsoft.AspNetCore.Hosting.Internal.StartupLoader+ConfigureServicesDelegateBuilder`1[[System.Object,System.Private.CoreLib]]" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.Extensions.Logging.Abstractions">
+            <Type Name="Microsoft.Extensions.Logging.Logger`1[[Microsoft.AspNetCore.Hosting.Internal.WebHost,Microsoft.AspNetCore.Hosting]]" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.Extensions.Logging">
+            <Type Name="Microsoft.Extensions.Logging.LoggerFactory" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="System.ComponentModel.TypeConverter">
+            <Type Name="System.ComponentModel.TypeConverter" Dynamic="Required All" />
+            <Type Name="System.ComponentModel.StringConverter" Dynamic="Required All" />
+            <Type Name="System.ComponentModel.Int32Converter" Dynamic="Required All" />
+        </Assembly>
+        <Assembly Name="Microsoft.Extensions.Configuration.Json">
+            <Type Name="Microsoft.Extensions.Configuration.Json.JsonConfigurationSource" Dynamic="Required All" />
+        </Assembly>
+    </Application>
+</Directives>

+ 27 - 0
frameworks/CSharp/aspnetcore-corert/README.md

@@ -0,0 +1,27 @@
+# ASP.NET Core Tests on Windows and Linux
+
+See [.NET CoreRT](https://github.com/dotnet/corert) and [ASP.NET Core](https://github.com/aspnet) for more information.
+
+This includes tests for plaintext
+
+## Infrastructure Software Versions
+
+**Language**
+
+* C# 7.0
+
+**Platforms**
+
+* .NET [CoreRT](https://github.com/dotnet/corert), a .NET Core runtime optimized for AOT (ahead of time compilation), with the accompanying .NET native compiler toolchain
+
+**Web Servers**
+
+* [Kestrel](https://github.com/aspnet/KestrelHttpServer)
+
+**Web Stack**
+
+* ASP.NET Core
+
+## Paths & Source for Tests
+
+* [Plaintext](Benchmarks/Middleware/PlaintextMiddleware.cs): "/plaintext"

+ 14 - 0
frameworks/CSharp/aspnetcore-corert/aspcore-corert.dockerfile

@@ -0,0 +1,14 @@
+FROM microsoft/dotnet:2.1-sdk-stretch AS build
+RUN echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.9 main" | tee /etc/apt/sources.list.d/llvm.list
+RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | apt-key add -
+RUN apt-get update
+RUN apt-get -yqq install cmake clang-3.9 libicu57 libunwind8 uuid-dev libcurl4-openssl-dev zlib1g-dev libkrb5-dev
+WORKDIR /app
+COPY PlatformBenchmarks .
+RUN dotnet publish -c Release -o out -r linux-x64
+
+FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
+WORKDIR /app
+COPY --from=build /app/out ./
+
+ENTRYPOINT ["./PlatformBenchmarks", "--server.urls=http://+:8080"]

+ 23 - 0
frameworks/CSharp/aspnetcore-corert/benchmark_config.json

@@ -0,0 +1,23 @@
+{
+  "framework": "aspcore-corert",
+  "tests": [{
+    "default": {
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "ASP.NET Core",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": ".NET",
+      "flavor": "CoreRT",
+      "webserver": "Kestrel",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ASP.NET Core",
+      "notes": "",
+      "versus": "aspcore"
+    }
+  }]
+}