mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 23:01:44 +01:00
improved test design
added perf. profiling for StrictlyOrderedAwaitTask
This commit is contained in:
parent
debe76be89
commit
65e87e5aa9
@ -12,8 +12,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Capnp.Net.Runtime" Version="1.3.88-g218ad92525" />
|
||||
<PackageReference Include="CapnpC.CSharp.MsBuild.Generation" Version="1.3.88-g218ad92525" />
|
||||
<PackageReference Include="Capnp.Net.Runtime" Version="1.3.89-gdebe76be89" />
|
||||
<PackageReference Include="CapnpC.CSharp.MsBuild.Generation" Version="1.3.89-gdebe76be89" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -18,6 +18,10 @@
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.1|AnyCPU'">
|
||||
<DefineConstants>TRACE;SOTASK_PERF</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
|
||||
|
@ -53,7 +53,6 @@ namespace Capnp.Net.Runtime.Tests
|
||||
public void Cancel()
|
||||
{
|
||||
var t = new TcpRpcPorted();
|
||||
t.InitConsoleLogging();
|
||||
Repeat(1000, t.Cancel);
|
||||
}
|
||||
|
||||
@ -61,7 +60,6 @@ namespace Capnp.Net.Runtime.Tests
|
||||
public void Embargo()
|
||||
{
|
||||
var t = new TcpRpcPorted();
|
||||
t.InitConsoleLogging();
|
||||
Repeat(100,
|
||||
() =>
|
||||
NewLocalhostTcpTestbed(TcpRpcTestOptions.ClientTracer | TcpRpcTestOptions.ClientFluctStream)
|
||||
@ -72,7 +70,6 @@ namespace Capnp.Net.Runtime.Tests
|
||||
public void EmbargoServer()
|
||||
{
|
||||
var t2 = new TcpRpcInterop();
|
||||
t2.InitConsoleLogging();
|
||||
Repeat(20, t2.EmbargoServer);
|
||||
}
|
||||
|
||||
@ -82,11 +79,9 @@ namespace Capnp.Net.Runtime.Tests
|
||||
// Some code paths are really rare during this test, therefore increased repetition count.
|
||||
|
||||
var t = new TcpRpcPorted();
|
||||
t.InitConsoleLogging();
|
||||
Repeat(1000, t.EmbargoNull);
|
||||
|
||||
var t2 = new TcpRpcInterop();
|
||||
t2.InitConsoleLogging();
|
||||
Repeat(100, t2.EmbargoNullServer);
|
||||
}
|
||||
|
||||
@ -94,7 +89,6 @@ namespace Capnp.Net.Runtime.Tests
|
||||
public void RetainAndRelease()
|
||||
{
|
||||
var t = new TcpRpcPorted();
|
||||
t.InitConsoleLogging();
|
||||
Repeat(100, t.RetainAndRelease);
|
||||
}
|
||||
|
||||
@ -102,7 +96,6 @@ namespace Capnp.Net.Runtime.Tests
|
||||
public void PipelineAfterReturn()
|
||||
{
|
||||
var t = new TcpRpc();
|
||||
t.InitConsoleLogging();
|
||||
Repeat(100, t.PipelineAfterReturn);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Capnp.Net.Runtime.Tests.Util;
|
||||
using Capnp.Rpc;
|
||||
using Capnp.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
@ -351,6 +352,32 @@ namespace Capnp.Net.Runtime.Tests
|
||||
|
||||
protected ILogger Logger { get; set; }
|
||||
|
||||
protected TestBase()
|
||||
{
|
||||
Logging.LoggerFactory?.Dispose();
|
||||
#pragma warning disable CS0618
|
||||
Logging.LoggerFactory = new LoggerFactory().AddConsole((msg, level) => true);
|
||||
#pragma warning restore CS0618
|
||||
Logger = Logging.CreateLogger<TcpRpcStress>();
|
||||
if (Thread.CurrentThread.Name == null)
|
||||
Thread.CurrentThread.Name = $"Test Thread {Thread.CurrentThread.ManagedThreadId}";
|
||||
|
||||
#if SOTASK_PERF
|
||||
StrictlyOrderedTaskExtensions.Stats.Reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void TestCleanup()
|
||||
{
|
||||
#if SOTASK_PERF
|
||||
Console.WriteLine($"StrictlyOrderedTask performance statistics:");
|
||||
Console.WriteLine($"AwaitInternal: max. {StrictlyOrderedTaskExtensions.Stats.AwaitInternalMaxOuterIterations} outer iterations");
|
||||
Console.WriteLine($"AwaitInternal: max. {StrictlyOrderedTaskExtensions.Stats.AwaitInternalMaxInnerIterations} inner iterations");
|
||||
Console.WriteLine($"OnCompleted: max. {StrictlyOrderedTaskExtensions.Stats.OnCompletedMaxSpins} iterations");
|
||||
#endif
|
||||
}
|
||||
|
||||
protected static TcpRpcClient SetupClient(IPAddress addr, int port, TcpRpcTestOptions options = TcpRpcTestOptions.None)
|
||||
{
|
||||
var client = new TcpRpcClient();
|
||||
@ -404,18 +431,6 @@ namespace Capnp.Net.Runtime.Tests
|
||||
|
||||
protected static LocalTestbed NewLocalTestbed() => new LocalTestbed();
|
||||
|
||||
[TestInitialize]
|
||||
public void InitConsoleLogging()
|
||||
{
|
||||
Logging.LoggerFactory?.Dispose();
|
||||
#pragma warning disable CS0618
|
||||
Logging.LoggerFactory = new LoggerFactory().AddConsole((msg, level) => true);
|
||||
#pragma warning restore CS0618
|
||||
Logger = Logging.CreateLogger<TcpRpcStress>();
|
||||
if (Thread.CurrentThread.Name == null)
|
||||
Thread.CurrentThread.Name = $"Test Thread {Thread.CurrentThread.ManagedThreadId}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Somewhat ugly helper method which ensures that both Tcp client and server
|
||||
/// are waiting for data reception from each other. This is a "balanced" state, meaning
|
||||
|
@ -33,7 +33,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DefineConstants></DefineConstants>
|
||||
<DefineConstants>SOTASK_PERF</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.0|AnyCPU'">
|
||||
|
@ -53,18 +53,37 @@ namespace Capnp.Util
|
||||
}
|
||||
finally
|
||||
{
|
||||
#if SOTASK_PERF
|
||||
long outerCount = 0, innerCount = 0;
|
||||
#endif
|
||||
|
||||
SpinWait.SpinUntil(() =>
|
||||
{
|
||||
Action? continuations;
|
||||
do
|
||||
|
||||
while (true)
|
||||
{
|
||||
#if SOTASK_PERF
|
||||
++innerCount;
|
||||
#endif
|
||||
|
||||
continuations = (Action?)Interlocked.Exchange(ref _state, null);
|
||||
continuations?.Invoke();
|
||||
|
||||
} while (continuations != null);
|
||||
if (continuations != null)
|
||||
continuations();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
#if SOTASK_PERF
|
||||
++outerCount;
|
||||
#endif
|
||||
return Interlocked.CompareExchange(ref _state, s_seal, null) == null;
|
||||
});
|
||||
|
||||
#if SOTASK_PERF
|
||||
StrictlyOrderedTaskExtensions.Stats.UpdateAwaitInternal(outerCount, innerCount);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +94,16 @@ namespace Capnp.Util
|
||||
{
|
||||
bool first = false;
|
||||
|
||||
#if SOTASK_PERF
|
||||
long spinCount = 0;
|
||||
#endif
|
||||
|
||||
SpinWait.SpinUntil(() => {
|
||||
|
||||
#if SOTASK_PERF
|
||||
++spinCount;
|
||||
#endif
|
||||
|
||||
object? cur, next;
|
||||
cur = Volatile.Read(ref _state);
|
||||
first = false;
|
||||
@ -102,6 +130,10 @@ namespace Capnp.Util
|
||||
return Interlocked.CompareExchange(ref _state, next, cur) == cur;
|
||||
});
|
||||
|
||||
#if SOTASK_PERF
|
||||
StrictlyOrderedTaskExtensions.Stats.UpdateOnCompleted(spinCount);
|
||||
#endif
|
||||
|
||||
if (first)
|
||||
{
|
||||
AwaitInternal();
|
||||
@ -168,6 +200,48 @@ namespace Capnp.Util
|
||||
/// </summary>
|
||||
public static class StrictlyOrderedTaskExtensions
|
||||
{
|
||||
#if SOTASK_PERF
|
||||
public class Statistics
|
||||
{
|
||||
internal long _awaitInternalMaxOuterIterations;
|
||||
internal long _awaitInternalMaxInnerIterations;
|
||||
internal long _onCompletedMaxSpins;
|
||||
|
||||
public long AwaitInternalMaxOuterIterations => Volatile.Read(ref _awaitInternalMaxOuterIterations);
|
||||
public long AwaitInternalMaxInnerIterations => Volatile.Read(ref _awaitInternalMaxInnerIterations);
|
||||
public long OnCompletedMaxSpins => Volatile.Read(ref _onCompletedMaxSpins);
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Volatile.Write(ref _awaitInternalMaxOuterIterations, 0);
|
||||
Volatile.Write(ref _awaitInternalMaxInnerIterations, 0);
|
||||
Volatile.Write(ref _onCompletedMaxSpins, 0);
|
||||
}
|
||||
|
||||
internal static void InterlockedMax(ref long current, long value)
|
||||
{
|
||||
long existing;
|
||||
do
|
||||
{
|
||||
existing = Volatile.Read(ref current);
|
||||
if (value <= existing) return;
|
||||
} while (Interlocked.CompareExchange(ref current, value, existing) != existing);
|
||||
}
|
||||
|
||||
internal void UpdateAwaitInternal(long outerCount, long innerCount)
|
||||
{
|
||||
InterlockedMax(ref _awaitInternalMaxOuterIterations, outerCount);
|
||||
InterlockedMax(ref _awaitInternalMaxInnerIterations, innerCount);
|
||||
}
|
||||
|
||||
internal void UpdateOnCompleted(long spinCount)
|
||||
{
|
||||
InterlockedMax(ref _onCompletedMaxSpins, spinCount);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly Statistics Stats = new Statistics();
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Converts the task to a task-like object which enforces that all await operations from the same thread leave in the exact order they were issued.
|
||||
/// </summary>
|
||||
|
Loading…
x
Reference in New Issue
Block a user