More micro benchmarks (#1158)

- Hub Protocol benchmark
- Broadcast benchmark
- Run benchmarks validation during builds
This commit is contained in:
BrennanConroy 2017-11-28 15:39:58 -08:00 committed by GitHub
parent 887e22ec07
commit 531c7cfba1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 232 additions and 27 deletions

View File

@ -0,0 +1,47 @@
using System;
using System.Threading.Channels;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
using Microsoft.AspNetCore.Sockets;
using Microsoft.AspNetCore.Sockets.Internal;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{
[ParameterizedJobConfig(typeof(CoreConfig))]
public class BroadcastBenchmark
{
private DefaultHubLifetimeManager<Hub> _hubLifetimeManager;
private HubContext<Hub> _hubContext;
[Params(1, 10, 1000)]
public int Connections;
[GlobalSetup]
public void GlobalSetup()
{
_hubLifetimeManager = new DefaultHubLifetimeManager<Hub>();
var options = new UnboundedChannelOptions { AllowSynchronousContinuations = true };
for (var i = 0; i < Connections; ++i)
{
var transportToApplication = Channel.CreateUnbounded<byte[]>(options);
var applicationToTransport = Channel.CreateUnbounded<byte[]>(options);
var application = ChannelConnection.Create<byte[]>(input: applicationToTransport, output: transportToApplication);
var transport = ChannelConnection.Create<byte[]>(input: transportToApplication, output: applicationToTransport);
var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), transport, application);
_hubLifetimeManager.OnConnectedAsync(new HubConnectionContext(Channel.CreateUnbounded<HubMessage>(), connection)).Wait();
}
_hubContext = new HubContext<Hub>(_hubLifetimeManager);
}
[Benchmark]
public Task InvokeAsyncAll()
{
return _hubContext.All.InvokeAsync("Method");
}
}
}

View File

@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Validators;
@ -13,17 +9,23 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{
public class CoreConfig : ManualConfig
{
public CoreConfig()
public CoreConfig() : this(Job.Core)
{
// Here because build.cmd calls the other constructor
// and this setting will complain about non-release builds
Add(JitOptimizationsValidator.FailOnError);
}
public CoreConfig(Job job)
{
Add(DefaultConfig.Instance);
Add(MemoryDiagnoser.Default);
Add(StatisticColumn.OperationsPerSecond);
Add(Job.Default
.With(BenchmarkDotNet.Environments.Runtime.Core)
Add(job
.With(RunStrategy.Throughput)
.WithRemoveOutliers(false)
.With(new GcMode() { Server = true })
.With(RunStrategy.Throughput)
.WithLaunchCount(3)
.WithWarmupCount(5)
.WithTargetCount(10));

View File

@ -0,0 +1,93 @@
using System;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Encoders;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{
[ParameterizedJobConfig(typeof(CoreConfig))]
public class HubProtocolBenchmark
{
private HubProtocolReaderWriter _hubProtocolReaderWriter;
private byte[] _binaryInput;
private TestBinder _binder;
private HubMessage _hubMessage;
[Params(Message.NoArguments, Message.FewArguments, Message.ManyArguments, Message.LargeArguments)]
public Message Input { get; set; }
[Params(Protocol.MsgPack, Protocol.Json)]
public Protocol HubProtocol { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
switch (HubProtocol)
{
case Protocol.MsgPack:
_hubProtocolReaderWriter = new HubProtocolReaderWriter(new MessagePackHubProtocol(), new PassThroughEncoder());
break;
case Protocol.Json:
_hubProtocolReaderWriter = new HubProtocolReaderWriter(new JsonHubProtocol(), new PassThroughEncoder());
break;
}
switch (Input)
{
case Message.NoArguments:
_hubMessage = new InvocationMessage("123", true, "Target", null);
break;
case Message.FewArguments:
_hubMessage = new InvocationMessage("123", true, "Target", null, 1, "Foo", 2.0f);
break;
case Message.ManyArguments:
_hubMessage = new InvocationMessage("123", true, "Target", null, 1, "string", 2.0f, true, (byte)9, new byte[] { 5, 4, 3, 2, 1 }, 'c', 123456789101112L);
break;
case Message.LargeArguments:
_hubMessage = new InvocationMessage("123", true, "Target", null, new string('F', 10240), new byte[10240]);
break;
}
_binaryInput = GetBytes(_hubMessage);
_binder = new TestBinder(_hubMessage);
}
[Benchmark]
public void ReadSingleMessage()
{
if (!_hubProtocolReaderWriter.ReadMessages(_binaryInput, _binder, out var _))
{
throw new InvalidOperationException("Failed to read message");
}
}
[Benchmark]
public void WriteSingleMessage()
{
if (_hubProtocolReaderWriter.WriteMessage(_hubMessage).Length != _binaryInput.Length)
{
throw new InvalidOperationException("Failed to write message");
}
}
public enum Protocol
{
MsgPack = 0,
Json = 1
}
public enum Message
{
NoArguments = 0,
FewArguments = 1,
ManyArguments = 2,
LargeArguments = 3
}
private byte[] GetBytes(HubMessage hubMessage)
{
return _hubProtocolReaderWriter.WriteMessage(_hubMessage);
}
}
}

View File

@ -5,7 +5,7 @@ using Microsoft.AspNetCore.SignalR.Internal.Formatters;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{
[Config(typeof(CoreConfig))]
[ParameterizedJobConfig(typeof(CoreConfig))]
public class MessageParserBenchmark
{
private static readonly Random Random = new Random();

View File

@ -2,12 +2,16 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Core\Microsoft.AspNetCore.SignalR.Core.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Sockets\Microsoft.AspNetCore.Sockets.csproj" />
<PackageReference Include="BenchmarkDotNet" Version="$(BenchmarkDotNetPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
<PackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -1,13 +0,0 @@
using System.Reflection;
using BenchmarkDotNet.Running;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{
class Program
{
static void Main(string[] args)
{
BenchmarkSwitcher.FromAssembly(typeof(Program).GetTypeInfo().Assembly).Run(args);
}
}
}

View File

@ -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.Linq;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{
public class TestBinder : IInvocationBinder
{
private readonly Type[] _paramTypes;
private readonly Type _returnType;
public TestBinder(HubMessage expectedMessage)
{
switch (expectedMessage)
{
case StreamInvocationMessage i:
_paramTypes = i.Arguments?.Select(a => a?.GetType() ?? typeof(object))?.ToArray();
break;
case InvocationMessage i:
_paramTypes = i.Arguments?.Select(a => a?.GetType() ?? typeof(object))?.ToArray();
break;
case StreamItemMessage s:
_returnType = s.Item?.GetType() ?? typeof(object);
break;
case CompletionMessage c:
_returnType = c.Result?.GetType() ?? typeof(object);
break;
}
}
public TestBinder() : this(null, null) { }
public TestBinder(Type[] paramTypes) : this(paramTypes, null) { }
public TestBinder(Type returnType) : this(null, returnType) { }
public TestBinder(Type[] paramTypes, Type returnType)
{
_paramTypes = paramTypes;
_returnType = returnType;
}
public Type[] GetParameterTypes(string methodName)
{
if (_paramTypes != null)
{
return _paramTypes;
}
throw new InvalidOperationException("Unexpected binder call");
}
public Type GetReturnType(string invocationId)
{
if (_returnType != null)
{
return _returnType;
}
throw new InvalidOperationException("Unexpected binder call");
}
}
}

View File

@ -4,6 +4,7 @@
</PropertyGroup>
<PropertyGroup Label="Package Versions">
<BenchmarkDotNetPackageVersion>0.10.9</BenchmarkDotNetPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview1-27579</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<BuildBundlerMinifierPackageVersion>2.4.337</BuildBundlerMinifierPackageVersion>
<GoogleProtobufPackageVersion>3.1.0</GoogleProtobufPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview1-15549</InternalAspNetCoreSdkPackageVersion>

View File

@ -1,4 +1,7 @@
<Project>
<PropertyGroup>
<EnableBenchmarkValidation>true</EnableBenchmarkValidation>
</PropertyGroup>
<ItemGroup>
<ProjectsToPack Include="$(RepositoryRoot)client-ts\Microsoft.AspNetCore.SignalR.Client.TS\*.csproj" />
</ItemGroup>

View File

@ -104,7 +104,7 @@ click('connect', function(event) {
connectButton.disabled = true;
disconnectButton.disabled = false;
console.log('http://' + document.location.host + '/' + hubRoute);
connection = new signalR.HubConnection(hubRoute, logger, { transport: transportType, logger: logger });
connection = new signalR.HubConnection(hubRoute, { transport: transportType, logging: logger });
connection.on('Send', function(msg) {
addLine('message-list', msg);
});

View File

@ -69,7 +69,13 @@ namespace Microsoft.AspNetCore.SignalR
private Task InvokeAllWhere(string methodName, object[] args, Func<HubConnectionContext, bool> include)
{
var tasks = new List<Task>(_connections.Count);
var count = _connections.Count;
if (count == 0)
{
return Task.CompletedTask;
}
var tasks = new List<Task>(count);
var message = CreateInvocationMessage(methodName, args);
// TODO: serialize once per format by providing a different stream?

View File

@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
new object[] { new[] { new StreamInvocationMessage("xyz", "method", null, new[] { new CustomObject(), new CustomObject() }) } },
new object[] { new[] { new CancelInvocationMessage("xyz") } },
new object[] { new[] { PingMessage.Instance } },
new object[]