Moving PlatformBenchmarks from aspnet/benchmarks (#2457)

This commit is contained in:
Sébastien Ros 2018-04-11 14:27:34 -07:00 committed by GitHub
parent 7e5104573e
commit e411cbf7e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 697 additions and 0 deletions

View File

@ -126,6 +126,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemdTestApp", "samples\S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlaintextApp", "samples\PlaintextApp\PlaintextApp.csproj", "{CE5523AE-6E38-4E20-998F-C64E02C5CC51}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformBenchmarks", "benchmarkapps\PlatformBenchmarks\PlatformBenchmarks.csproj", "{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -364,6 +366,18 @@ Global
{CE5523AE-6E38-4E20-998F-C64E02C5CC51}.Release|x64.Build.0 = Release|Any CPU
{CE5523AE-6E38-4E20-998F-C64E02C5CC51}.Release|x86.ActiveCfg = Release|Any CPU
{CE5523AE-6E38-4E20-998F-C64E02C5CC51}.Release|x86.Build.0 = Release|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|x64.ActiveCfg = Debug|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|x64.Build.0 = Debug|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|x86.ActiveCfg = Debug|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Debug|x86.Build.0 = Debug|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|Any CPU.Build.0 = Release|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|x64.ActiveCfg = Release|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|x64.Build.0 = Release|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|x86.ActiveCfg = Release|Any CPU
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -390,6 +404,7 @@ Global
{7BC22A4A-15D2-44C2-AB45-049F0FB562FA} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE}
{A7994A41-CAF8-47A7-8975-F101F75B5BC1} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE}
{CE5523AE-6E38-4E20-998F-C64E02C5CC51} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE}
{7C24EAB8-57A9-4613-A8A6-4C21BB7D260D} = {A95C3BE1-B850-4265-97A0-777ADCCD437F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2D10D020-6770-47CA-BB8D-2C23FE3AE071}

View File

@ -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 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);
}
}
}

View File

@ -0,0 +1,156 @@
// 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.Server.Kestrel.Core.Internal.Http;
using Utf8Json;
namespace PlatformBenchmarks
{
public class BenchmarkApplication : HttpConnection
{
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 static AsciiString _plainTextBody = "Hello, World!";
private static class Paths
{
public static AsciiString Plaintext = "/plaintext";
public static AsciiString Json = "/json";
}
private bool _isPlainText;
private bool _isJson;
public override void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
{
if (path.StartsWith(Paths.Plaintext) && method == HttpMethod.Get)
{
_isPlainText = true;
}
else if (path.StartsWith(Paths.Json) && method == HttpMethod.Get)
{
_isJson = true;
}
else
{
_isPlainText = false;
_isJson = false;
}
}
public override void OnHeader(Span<byte> name, Span<byte> value)
{
}
public override ValueTask ProcessRequestAsync()
{
if (_isPlainText)
{
PlainText(Writer);
}
else if (_isJson)
{
Json(Writer);
}
else
{
Default(Writer);
}
return default;
}
public override async ValueTask OnReadCompletedAsync()
{
await Writer.FlushAsync();
}
private static void PlainText(PipeWriter pipeWriter)
{
var writer = new BufferWriter<PipeWriter>(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((ulong)_plainTextBody.Length);
// End of headers
writer.Write(_eoh);
// Body
writer.Write(_plainTextBody);
writer.Commit();
}
private static void Json(PipeWriter pipeWriter)
{
var writer = new BufferWriter<PipeWriter>(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.SerializeUnsafe(new { message = "Hello, World!" });
writer.WriteNumeric((ulong)jsonPayload.Count);
// End of headers
writer.Write(_eoh);
// Body
writer.Write(jsonPayload);
writer.Commit();
}
private static void Default(PipeWriter pipeWriter)
{
var writer = new BufferWriter<PipeWriter>(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();
}
}
}

View File

@ -0,0 +1,81 @@
// 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;
}
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 = 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);
}
}
}

View File

@ -0,0 +1,38 @@
// 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.Buffers.Text;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
namespace System.Buffers
{
internal static class BufferWriterExtensions
{
private const int MaxULongByteLength = 20;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void WriteNumeric(ref this BufferWriter<PipeWriter> buffer, ulong number)
{
// Try to format directly
if (Utf8Formatter.TryFormat(number, buffer.Span, out int bytesWritten))
{
buffer.Advance(bytesWritten);
}
else
{
// Ask for at least 20 bytes
buffer.Ensure(MaxULongByteLength);
Debug.Assert(buffer.Span.Length >= 20, "Buffer is < 20 bytes");
// Try again
if (Utf8Formatter.TryFormat(number, buffer.Span, out bytesWritten))
{
buffer.Advance(bytesWritten);
}
}
}
}
}

View File

@ -0,0 +1,59 @@
// 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);
}
public static ReadOnlySpan<byte> HeaderBytes => s_headerBytesMaster;
private static void SetDateValues(DateTimeOffset value)
{
lock (s_headerBytesScratch)
{
if (!Utf8Formatter.TryFormat(value, s_headerBytesScratch.AsSpan().Slice(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;
}
}
}
}

View File

@ -0,0 +1,161 @@
// 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<TConnection>(this IConnectionBuilder builder) where TConnection : HttpConnection, new()
{
return builder.Use(next => new HttpApplication<TConnection>().ExecuteAsync);
}
}
public class HttpApplication<TConnection> where TConnection : HttpConnection, new()
{
public Task ExecuteAsync(ConnectionContext connection)
{
var parser = new HttpParser<HttpConnection>();
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<HttpConnection> Parser { get; set; }
public virtual void OnHeader(Span<byte> name, Span<byte> value)
{
}
public virtual void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> 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<byte> 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
}
}
}

View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<OutputType>Exe</OutputType>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Utf8Json" Version="$(Utf8JsonPackageVersion)" />
</ItemGroup>
<!-- These references are used when running locally -->
<ItemGroup Condition="'$(BenchmarksTargetFramework)' == ''">
<ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" />
<ProjectReference Include="..\..\src\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" />
<PackageReference Include="Microsoft.Extensions.Buffers.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
</ItemGroup>
<!-- These references are used when running on the Benchmarks Server -->
<ItemGroup Condition="'$(BenchmarksTargetFramework)' != ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(MicrosoftAspNetCoreAllPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Buffers.Sources" Version="$(MicrosoftAspNetCoreAllPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,46 @@
// 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.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace PlatformBenchmarks
{
public class Program
{
public static void Main(string[] args)
{
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;
}
}
}

View File

@ -0,0 +1,19 @@
// 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.WebSockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace PlatformBenchmarks
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
}
}
}

View File

@ -0,0 +1,15 @@
{
"Default": {
"Client": "Wrk",
"PresetHeaders": "Json",
"Source": {
"Repository": "https://github.com/aspnet/KestrelHttpServer.git",
"BranchOrCommit": "dev",
"Project": "benchmarkapps/PlatformBenchmarks/PlatformBenchmarks.csproj"
}
},
"JsonPlatform": {
"Path": "/json"
}
}

View File

@ -0,0 +1,26 @@
{
"Default": {
"Client": "Wrk",
"PresetHeaders": "Plaintext",
"ClientProperties": {
"ScriptName": "pipeline",
"PipelineDepth": 16
},
"Source": {
"Repository": "https://github.com/aspnet/KestrelHttpServer.git",
"BranchOrCommit": "dev",
"Project": "benchmarkapps/PlatformBenchmarks/PlatformBenchmarks.csproj"
},
"Port": 8080
},
"PlaintextPlatform": {
"Path": "/plaintext"
},
"PlaintextNonPipelinedPlatform": {
"Path": "/plaintext",
"ClientProperties": {
"ScriptName": "",
"PipelineDepth": 0
}
}
}

View File

@ -7,6 +7,7 @@
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview3-17002</InternalAspNetCoreSdkPackageVersion>
<LibuvPackageVersion>1.10.0</LibuvPackageVersion>
<MicrosoftAspNetCoreAllPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreAllPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
@ -20,6 +21,7 @@
<MicrosoftExtensionsBuffersSourcesPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsBuffersSourcesPackageVersion>
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.1.0-preview3-32037</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
@ -40,6 +42,7 @@
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.5.0-preview3-26331-02</SystemRuntimeCompilerServicesUnsafePackageVersion>
<SystemSecurityCryptographyCngPackageVersion>4.5.0-preview3-26331-02</SystemSecurityCryptographyCngPackageVersion>
<SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview3-26331-02</SystemThreadingTasksExtensionsPackageVersion>
<Utf8JsonPackageVersion>1.3.7</Utf8JsonPackageVersion>
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
<XunitPackageVersion>2.3.1</XunitPackageVersion>
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>