Skip to content

Instantly share code, notes, and snippets.

@joncloud
Last active January 20, 2021 22:29
Show Gist options
  • Save joncloud/b6b8ae1c4f5fc63c3c7821006d695e62 to your computer and use it in GitHub Desktop.
Save joncloud/b6b8ae1c4f5fc63c3c7821006d695e62 to your computer and use it in GitHub Desktop.
ASP.NET Core SignalR Client Allocations Testing
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
</ItemGroup>
</Project>
using System.Globalization;
public class ConnectionState {
private int _nextInvocationId;
public string GetNextId() => (++_nextInvocationId).ToString(CultureInfo.InvariantCulture);
}
using System;
using Microsoft.Extensions.Logging;
static class Log
{
private static readonly Action<ILogger, string, Exception> _startingStream =
LoggerMessage.Define<string>(LogLevel.Trace, new EventId(63, "StartingStream"), "Initiating stream '{StreamId}'.");
public static void StartingStream(ILogger logger, string streamId)
{
_startingStream(logger, streamId, null);
}
}
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
public static class Original {
static readonly ILogger _logger = LoggerFactory.Create(x => { }).CreateLogger("Original");
public static Dictionary<string, object> PackageStreamingParams(ConnectionState connectionState, ref object[] args, out List<string> streamIds)
{
Dictionary<string, object> readers = null;
streamIds = null;
var newArgs = new List<object>(args.Length);
for (var i = 0; i < args.Length; i++)
{
if (args[i] != null && ReflectionHelper.IsStreamingType(args[i].GetType()))
{
if (readers == null)
{
readers = new Dictionary<string, object>();
}
var id = connectionState.GetNextId();
readers[id] = args[i];
if (streamIds == null)
{
streamIds = new List<string>();
}
streamIds.Add(id);
Log.StartingStream(_logger, id);
}
else
{
newArgs.Add(args[i]);
}
}
args = newArgs.ToArray();
return readers;
}
}
using BenchmarkDotNet.Running;
class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<Tests>();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Channels;
internal static class ReflectionHelper
{
// mustBeDirectType - Hub methods must use the base 'stream' type and not be a derived class that just implements the 'stream' type
// and 'stream' types from the client are allowed to inherit from accepted 'stream' types
public static bool IsStreamingType(Type type, bool mustBeDirectType = false)
{
// TODO #2594 - add Streams here, to make sending files easy
if (IsIAsyncEnumerable(type))
{
return true;
}
do
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ChannelReader<>))
{
return true;
}
type = type.BaseType;
} while (mustBeDirectType == false && type != null);
return false;
}
public static bool IsIAsyncEnumerable(Type type)
{
if (type.IsGenericType)
{
if (type.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>))
{
return true;
}
}
return type.GetInterfaces().Any(t =>
{
if (t.IsGenericType)
{
return t.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>);
}
else
{
return false;
}
});
}
}
BenchmarkDotNet=v0.12.1, OS=ubuntu 20.04
Intel Core i5-2400 CPU 3.10GHz (Sandy Bridge), 1 CPU, 4 logical and 4 physical cores
.NET Core SDK=5.0.101
  [Host]     : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
  DefaultJob : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT

Method Categories Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated
Original_NoArgs NoArgs 43.62 ns 0.147 ns 0.137 ns 1.00 0.0306 - - 96 B
Update_NoArgs NoArgs 16.78 ns 0.090 ns 0.075 ns 0.38 0.0204 - - 64 B
With256_NoArgs NoArgs 27.72 ns 0.150 ns 0.133 ns 0.64 0.0204 - - 64 B
WithSkipLocalsInit_NoArgs NoArgs 17.71 ns 0.079 ns 0.066 ns 0.41 0.0204 - - 64 B
WithAllocOnly_NoArgs NoArgs 22.90 ns 0.103 ns 0.086 ns 0.53 0.0280 - - 88 B
Original_OneArg_NoStreaming OneArg_NoStreaming 138.24 ns 0.593 ns 0.525 ns 1.00 0.0508 - - 160 B
Update_OneArg_NoStreaming OneArg_NoStreaming 93.41 ns 0.443 ns 0.393 ns 0.68 0.0204 - - 64 B
With256_OneArg_NoStreaming OneArg_NoStreaming 104.47 ns 0.303 ns 0.269 ns 0.76 0.0204 - - 64 B
WithSkipLocalsInit_OneArg_NoStreaming OneArg_NoStreaming 92.68 ns 0.274 ns 0.256 ns 0.67 0.0204 - - 64 B
WithAllocOnly_OneArg_NoStreaming OneArg_NoStreaming 99.73 ns 0.569 ns 0.532 ns 0.72 0.0305 - - 96 B
Original_OneArg_OneStreaming OneArg_OneStreaming 407.45 ns 1.739 ns 1.542 ns 1.00 0.1707 - - 536 B
Update_OneArg_OneStreaming OneArg_OneStreaming 380.67 ns 1.437 ns 1.274 ns 0.93 0.1502 - - 472 B
With256_OneArg_OneStreaming OneArg_OneStreaming 394.59 ns 1.498 ns 1.328 ns 0.97 0.1502 - - 472 B
WithSkipLocalsInit_OneArg_OneStreaming OneArg_OneStreaming 408.57 ns 2.088 ns 1.851 ns 1.00 0.1502 - - 472 B
WithAllocOnly_OneArg_OneStreaming OneArg_OneStreaming 590.41 ns 3.139 ns 2.782 ns 1.45 0.1602 - - 504 B
Original_TwoArgs_NoStreaming TwoArgs_NoStreaming 229.14 ns 0.550 ns 0.459 ns 1.00 0.0560 - - 176 B
Update_TwoArgs_NoStreaming TwoArgs_NoStreaming 176.20 ns 0.801 ns 0.710 ns 0.77 0.0203 - - 64 B
With256_TwoArgs_NoStreaming TwoArgs_NoStreaming 193.43 ns 0.616 ns 0.514 ns 0.84 0.0203 - - 64 B
WithSkipLocalsInit_TwoArgs_NoStreaming TwoArgs_NoStreaming 176.69 ns 0.504 ns 0.472 ns 0.77 0.0203 - - 64 B
WithAllocOnly_TwoArgs_NoStreaming TwoArgs_NoStreaming 182.34 ns 0.718 ns 0.600 ns 0.80 0.0305 - - 96 B
Original_TwoArgs_OneStreaming TwoArgs_OneStreaming 527.35 ns 3.693 ns 3.084 ns 1.00 0.1831 - - 576 B
Update_TwoArgs_OneStreaming TwoArgs_OneStreaming 486.79 ns 2.379 ns 2.225 ns 0.92 0.1602 - - 504 B
With256_TwoArgs_OneStreaming TwoArgs_OneStreaming 535.54 ns 3.771 ns 3.527 ns 1.02 0.1602 - - 504 B
WithSkipLocalsInit_TwoArgs_OneStreaming TwoArgs_OneStreaming 492.93 ns 1.297 ns 1.013 ns 0.94 0.1602 - - 504 B
WithAllocOnly_TwoArgs_OneStreaming TwoArgs_OneStreaming 493.36 ns 2.685 ns 2.512 ns 0.94 0.1707 - - 536 B
Original_TenArgs_NoStreaming TenArgs_NoStreaming 853.48 ns 3.232 ns 2.865 ns 1.00 0.0963 - - 304 B
Update_TenArgs_NoStreaming TenArgs_NoStreaming 778.00 ns 1.614 ns 1.348 ns 0.91 0.0200 - - 64 B
With256_TenArgs_NoStreaming TenArgs_NoStreaming 783.89 ns 1.083 ns 1.013 ns 0.92 0.0200 - - 64 B
WithSkipLocalsInit_TenArgs_NoStreaming TenArgs_NoStreaming 781.19 ns 9.887 ns 8.764 ns 0.92 0.0200 - - 64 B
WithAllocOnly_TenArgs_NoStreaming TenArgs_NoStreaming 775.22 ns 2.555 ns 2.265 ns 0.91 0.0324 - - 104 B
Original_TenArgs_OneStreaming TenArgs_OneStreaming 1,182.97 ns 4.833 ns 4.521 ns 1.00 0.2232 - - 704 B
Update_TenArgs_OneStreaming TenArgs_OneStreaming 1,147.01 ns 4.629 ns 4.330 ns 0.97 0.1793 - - 568 B
With256_TenArgs_OneStreaming TenArgs_OneStreaming 1,161.82 ns 6.469 ns 6.051 ns 0.98 0.1793 - - 568 B
WithSkipLocalsInit_TenArgs_OneStreaming TenArgs_OneStreaming 1,156.79 ns 5.643 ns 5.279 ns 0.98 0.1793 - - 568 B
WithAllocOnly_TenArgs_OneStreaming TenArgs_OneStreaming 1,163.49 ns 4.376 ns 4.093 ns 0.98 0.1926 - - 608 B
Original_ManyArgs_NoStreaming ManyArgs_NoStreaming 79,259.12 ns 185.627 ns 173.635 ns 1.00 5.2490 - - 16529 B
Update_ManyArgs_NoStreaming ManyArgs_NoStreaming 74,866.73 ns 179.037 ns 167.472 ns 0.94 0.2441 - - 1112 B
With256_ManyArgs_NoStreaming ManyArgs_NoStreaming 75,925.16 ns 110.921 ns 98.329 ns 0.96 0.2441 - - 1112 B
WithSkipLocalsInit_ManyArgs_NoStreaming ManyArgs_NoStreaming 78,314.99 ns 204.432 ns 181.223 ns 0.99 0.2441 - - 1112 B
WithAllocOnly_ManyArgs_NoStreaming ManyArgs_NoStreaming 75,549.21 ns 153.690 ns 136.243 ns 0.95 0.2441 - - 1112 B
Original_ManyArgs_OneStreaming ManyArgs_OneStreaming 82,189.85 ns 435.265 ns 363.466 ns 1.00 5.3711 - - 16931 B
Update_ManyArgs_OneStreaming ManyArgs_OneStreaming 83,423.41 ns 150.554 ns 117.543 ns 1.01 3.0518 - - 9730 B
With256_ManyArgs_OneStreaming ManyArgs_OneStreaming 80,118.42 ns 225.688 ns 188.460 ns 0.97 3.0518 - - 9730 B
WithSkipLocalsInit_ManyArgs_OneStreaming ManyArgs_OneStreaming 82,723.71 ns 442.321 ns 413.747 ns 1.01 3.0518 - - 9730 B
WithAllocOnly_ManyArgs_OneStreaming ManyArgs_OneStreaming 80,872.05 ns 350.917 ns 311.078 ns 0.98 3.0518 - - 9730 B
Original_ManyArgs_AllStreaming ManyArgs_AllStreaming 327,351.73 ns 3,659.984 ns 3,423.551 ns 1.00 64.4531 19.5313 - 233642 B
Update_ManyArgs_AllStreaming ManyArgs_AllStreaming 330,556.34 ns 1,087.227 ns 1,016.992 ns 1.01 63.4766 15.6250 - 226444 B
With256_ManyArgs_AllStreaming ManyArgs_AllStreaming 324,711.18 ns 1,065.321 ns 996.502 ns 0.99 62.9883 17.0898 - 226443 B
WithSkipLocalsInit_ManyArgs_AllStreaming ManyArgs_AllStreaming 327,473.35 ns 1,245.702 ns 1,165.231 ns 1.00 62.9883 16.1133 - 226442 B
WithAllocOnly_ManyArgs_AllStreaming ManyArgs_AllStreaming 324,771.75 ns 1,695.398 ns 1,585.877 ns 0.99 62.9883 16.1133 - 226443 B
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[CategoriesColumn]
[MemoryDiagnoser]
public class Tests {
// No Args
static readonly object[] NoArgs = Array.Empty<object>();
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(NoArgs))]
public (Dictionary<string, object>, List<string>) Original_NoArgs() {
return Test(NoArgs, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(NoArgs))]
public (Dictionary<string, object>, List<string>) Update_NoArgs() {
return Test(NoArgs, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(NoArgs))]
public (Dictionary<string, object>, List<string>) With256_NoArgs() {
return Test(NoArgs, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(NoArgs))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_NoArgs() {
return Test(NoArgs, WithSkipLocalsInit.PackageStreamingParams);
}
// One Arg No Streaming
static readonly object[] OneArg_NoStreaming = new [] { new object() };
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(OneArg_NoStreaming))]
public (Dictionary<string, object>, List<string>) Original_OneArg_NoStreaming() {
return Test(OneArg_NoStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(OneArg_NoStreaming))]
public (Dictionary<string, object>, List<string>) Update_OneArg_NoStreaming() {
return Test(OneArg_NoStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(OneArg_NoStreaming))]
public (Dictionary<string, object>, List<string>) With256_OneArg_NoStreaming() {
return Test(OneArg_NoStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(OneArg_NoStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_OneArg_NoStreaming() {
return Test(OneArg_NoStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// One Arg One Streaming
static readonly object[] OneArg_OneStreaming = new [] { new MockAsyncEnumerable<int>() };
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(OneArg_OneStreaming))]
public (Dictionary<string, object>, List<string>) Original_OneArg_OneStreaming() {
return Test(OneArg_OneStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(OneArg_OneStreaming))]
public (Dictionary<string, object>, List<string>) Update_OneArg_OneStreaming() {
return Test(OneArg_OneStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(OneArg_OneStreaming))]
public (Dictionary<string, object>, List<string>) With256_OneArg_OneStreaming() {
return Test(OneArg_OneStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(OneArg_OneStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_OneArg_OneStreaming() {
return Test(OneArg_OneStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// Two Args No Streaming
static readonly object[] TwoArgs_NoStreaming = new [] { new object(), new object() };
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(TwoArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) Original_TwoArgs_NoStreaming() {
return Test(TwoArgs_NoStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TwoArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) Update_TwoArgs_NoStreaming() {
return Test(TwoArgs_NoStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TwoArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) With256_TwoArgs_NoStreaming() {
return Test(TwoArgs_NoStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TwoArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_TwoArgs_NoStreaming() {
return Test(TwoArgs_NoStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// Two Args One Streaming
static readonly object[] TwoArgs_OneStreaming = new [] { new object(), new MockAsyncEnumerable<int>() };
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(TwoArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) Original_TwoArgs_OneStreaming() {
return Test(TwoArgs_OneStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TwoArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) Update_TwoArgs_OneStreaming() {
return Test(TwoArgs_OneStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TwoArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) With256_TwoArgs_OneStreaming() {
return Test(TwoArgs_OneStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TwoArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_TwoArgs_OneStreaming() {
return Test(TwoArgs_OneStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// Ten Args No Streaming
static readonly object[] TenArgs_NoStreaming = Enumerable.Repeat(new object(), 10).ToArray();
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(TenArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) Original_TenArgs_NoStreaming() {
return Test(TenArgs_NoStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TenArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) Update_TenArgs_NoStreaming() {
return Test(TenArgs_NoStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TenArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) With256_TenArgs_NoStreaming() {
return Test(TenArgs_NoStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TenArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_TenArgs_NoStreaming() {
return Test(TenArgs_NoStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// Ten Args One Streaming
static readonly object[] TenArgs_OneStreaming = Enumerable.Repeat(new object(), 5)
.Append(new MockAsyncEnumerable<int>())
.Concat(Enumerable.Repeat(new object(), 4))
.ToArray();
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(TenArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) Original_TenArgs_OneStreaming() {
return Test(TenArgs_OneStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TenArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) Update_TenArgs_OneStreaming() {
return Test(TenArgs_OneStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TenArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) With256_TenArgs_OneStreaming() {
return Test(TenArgs_OneStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(TenArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_TenArgs_OneStreaming() {
return Test(TenArgs_OneStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// Many Args No Streaming
static readonly object[] ManyArgs_NoStreaming = Enumerable.Repeat(new object(), 1024)
.ToArray();
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(ManyArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) Original_ManyArgs_NoStreaming() {
return Test(ManyArgs_NoStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) Update_ManyArgs_NoStreaming() {
return Test(ManyArgs_NoStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) With256_ManyArgs_NoStreaming() {
return Test(ManyArgs_NoStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_NoStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_ManyArgs_NoStreaming() {
return Test(ManyArgs_NoStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// Many Args One Streaming
static readonly object[] ManyArgs_OneStreaming = Enumerable.Repeat(new object(), 512)
.Append(new MockAsyncEnumerable<int>())
.Concat(Enumerable.Repeat(new object(), 511))
.ToArray();
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(ManyArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) Original_ManyArgs_OneStreaming() {
return Test(ManyArgs_OneStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) Update_ManyArgs_OneStreaming() {
return Test(ManyArgs_OneStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) With256_ManyArgs_OneStreaming() {
return Test(ManyArgs_OneStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_OneStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_ManyArgs_OneStreaming() {
return Test(ManyArgs_OneStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// Many Args All Streaming
static readonly object[] ManyArgs_AllStreaming = Enumerable.Repeat(
new MockAsyncEnumerable<int>(), 1024
).ToArray();
[Benchmark(Baseline = true)]
[BenchmarkCategory(nameof(ManyArgs_AllStreaming))]
public (Dictionary<string, object>, List<string>) Original_ManyArgs_AllStreaming() {
return Test(ManyArgs_AllStreaming, Original.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_AllStreaming))]
public (Dictionary<string, object>, List<string>) Update_ManyArgs_AllStreaming() {
return Test(ManyArgs_AllStreaming, Update.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_AllStreaming))]
public (Dictionary<string, object>, List<string>) With256_ManyArgs_AllStreaming() {
return Test(ManyArgs_AllStreaming, With256.PackageStreamingParams);
}
[Benchmark]
[BenchmarkCategory(nameof(ManyArgs_AllStreaming))]
public (Dictionary<string, object>, List<string>) WithSkipLocalsInit_ManyArgs_AllStreaming() {
return Test(ManyArgs_AllStreaming, WithSkipLocalsInit.PackageStreamingParams);
}
// Helpers
ConnectionState _connectionState;
[GlobalSetup]
public void GlobalSetup() {
_connectionState = new ConnectionState();
}
delegate Dictionary<string, object> Packager(ConnectionState connectionState, ref object[] args, out List<string> streamIds);
(Dictionary<string, object>, List<string>) Test(object[] args, Packager packager) {
var readers = packager(
_connectionState, ref args, out var streamIds
);
return (readers, streamIds);
}
class MockAsyncEnumerable<T> : IAsyncEnumerable<T>
{
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
public static class Update {
static readonly ILogger _logger = LoggerFactory.Create(x => { }).CreateLogger("Update");
public static Dictionary<string, object> PackageStreamingParams(ConnectionState connectionState, ref object[] args, out List<string> streamIds)
{
Dictionary<string, object> readers = null;
streamIds = null;
int newArgsCount = args.Length;
Span<bool> isStreaming = args.Length < 1024
? stackalloc bool[args.Length]
: new bool[args.Length];
for (var i = 0; i < args.Length; i++)
{
object arg = args[i];
if (arg is not null && ReflectionHelper.IsStreamingType(arg.GetType()))
{
isStreaming[i] = true;
newArgsCount--;
if (readers is null) {
readers = new Dictionary<string, object>();
}
if (streamIds is null) {
streamIds = new List<string>();
}
var id = connectionState.GetNextId();
readers[id] = args[i];
streamIds.Add(id);
Log.StartingStream(_logger, id);
}
}
if (newArgsCount == args.Length)
{
return null;
}
object[] newArgs = newArgsCount > 0
? new object[newArgsCount]
: Array.Empty<object>();
int newArgsIndex = 0;
for (var i = 0; i < args.Length; i++)
{
if (!isStreaming[i])
{
newArgs[newArgsIndex++] = args[i];
}
}
args = newArgs;
return readers;
}
}
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
public static class With256 {
static readonly ILogger _logger = LoggerFactory.Create(x => { }).CreateLogger("Update");
public static Dictionary<string, object> PackageStreamingParams(ConnectionState connectionState, ref object[] args, out List<string> streamIds)
{
Dictionary<string, object> readers = null;
streamIds = null;
int newArgsCount = args.Length;
const int MaxStackSize = 256;
Span<bool> isStreaming = args.Length <= MaxStackSize
? stackalloc bool[MaxStackSize].Slice(0, args.Length)
: new bool[args.Length];
for (var i = 0; i < args.Length; i++)
{
object arg = args[i];
if (arg is not null && ReflectionHelper.IsStreamingType(arg.GetType()))
{
isStreaming[i] = true;
newArgsCount--;
if (readers is null) {
readers = new Dictionary<string, object>();
}
if (streamIds is null) {
streamIds = new List<string>();
}
var id = connectionState.GetNextId();
readers[id] = args[i];
streamIds.Add(id);
Log.StartingStream(_logger, id);
}
}
if (newArgsCount == args.Length)
{
return null;
}
object[] newArgs = newArgsCount > 0
? new object[newArgsCount]
: Array.Empty<object>();
int newArgsIndex = 0;
for (var i = 0; i < args.Length; i++)
{
if (!isStreaming[i])
{
newArgs[newArgsIndex++] = args[i];
}
}
args = newArgs;
return readers;
}
}
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
public static class WithAllocOnly {
static readonly ILogger _logger = LoggerFactory.Create(x => { }).CreateLogger("Update");
public static Dictionary<string, object> PackageStreamingParams(ConnectionState connectionState, ref object[] args, out List<string> streamIds)
{
Dictionary<string, object> readers = null;
streamIds = null;
int newArgsCount = args.Length;
Span<bool> isStreaming = new bool[args.Length];
for (var i = 0; i < args.Length; i++)
{
object arg = args[i];
if (arg is not null && ReflectionHelper.IsStreamingType(arg.GetType()))
{
isStreaming[i] = true;
newArgsCount--;
if (readers is null) {
readers = new Dictionary<string, object>();
}
if (streamIds is null) {
streamIds = new List<string>();
}
var id = connectionState.GetNextId();
readers[id] = args[i];
streamIds.Add(id);
Log.StartingStream(_logger, id);
}
}
if (newArgsCount == args.Length)
{
return null;
}
object[] newArgs = newArgsCount > 0
? new object[newArgsCount]
: Array.Empty<object>();
int newArgsIndex = 0;
for (var i = 0; i < args.Length; i++)
{
if (!isStreaming[i])
{
newArgs[newArgsIndex++] = args[i];
}
}
args = newArgs;
return readers;
}
}
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Logging;
public static class WithSkipLocalsInit {
static readonly ILogger _logger = LoggerFactory.Create(x => { }).CreateLogger("Update");
[SkipLocalsInit]
public static Dictionary<string, object> PackageStreamingParams(ConnectionState connectionState, ref object[] args, out List<string> streamIds)
{
Dictionary<string, object> readers = null;
streamIds = null;
int newArgsCount = args.Length;
const int MaxStackSize = 256;
Span<bool> isStreaming = args.Length <= MaxStackSize
? stackalloc bool[MaxStackSize].Slice(0, args.Length)
: new bool[args.Length];
for (var i = 0; i < args.Length; i++)
{
object arg = args[i];
if (arg is not null && ReflectionHelper.IsStreamingType(arg.GetType()))
{
isStreaming[i] = true;
newArgsCount--;
if (readers is null) {
readers = new Dictionary<string, object>();
}
if (streamIds is null) {
streamIds = new List<string>();
}
var id = connectionState.GetNextId();
readers[id] = args[i];
streamIds.Add(id);
Log.StartingStream(_logger, id);
}
else
{
isStreaming[i] = false;
}
}
if (newArgsCount == args.Length)
{
return null;
}
object[] newArgs = newArgsCount > 0
? new object[newArgsCount]
: Array.Empty<object>();
int newArgsIndex = 0;
for (var i = 0; i < args.Length; i++)
{
if (!isStreaming[i])
{
newArgs[newArgsIndex++] = args[i];
}
}
args = newArgs;
return readers;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment