Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions Tests/Editor/Allocations/BenchmarkHarnessRobustnessTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,12 @@ private static IEnumerable<TestCaseData> DispatchBaselineSetupCases()
1,
1
).SetName("DispatchBaselineSetup_TargetedNoMatchingTarget");
yield return new TestCaseData(
DispatchBenchmarkScenario.UntargetedFloodOneDirectHandler,
1,
1,
1
).SetName("DispatchBaselineSetup_UntargetedOneDirectHandler");
yield return new TestCaseData(
DispatchBenchmarkScenario.UntargetedFloodTwoHandlersOnePriority,
2,
Expand Down Expand Up @@ -486,6 +492,38 @@ int expectedRegistrationBuckets
);
}

[Test]
public void DirectAndTokenOneHandlerScenariosKeepDistinctRegistrationOwnership()
{
DispatchThroughputBenchmarks.DispatchScenarioContractObservation token =
DispatchThroughputBenchmarks.ConfigureAndEmitOnceForContract(
DispatchBenchmarkScenario.UntargetedFloodOneHandler
);
DispatchThroughputBenchmarks.DispatchScenarioContractObservation direct =
DispatchThroughputBenchmarks.ConfigureAndEmitOnceForContract(
DispatchBenchmarkScenario.UntargetedFloodOneDirectHandler
);

Assert.AreEqual(
1,
token.TokenRegistrations,
"The public one-handler row must retain one token-owned registration."
);
Assert.IsFalse(
token.HasDirectUntargetedRegistration,
"The public one-handler row must not bypass the token wrapper."
);
Assert.AreEqual(
0,
direct.TokenRegistrations,
"The direct row must not stage token metadata."
);
Assert.IsTrue(
direct.HasDirectUntargetedRegistration,
"The direct row must retain the handler-owned deregistration state."
);
}

[Test]
public void BenchmarkMethodologyConstantsAreLocked()
{
Expand Down
4 changes: 4 additions & 0 deletions Tests/Runtime/Benchmarks/BenchmarkProtocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,8 @@ public static string Key(DispatchBenchmarkScenario scenario)
{
DispatchBenchmarkScenario.EmptyBusDispatch => "EmptyBus_Dispatch",
DispatchBenchmarkScenario.UntargetedFloodOneHandler => "UntargetedFlood_OneHandler",
DispatchBenchmarkScenario.UntargetedFloodOneDirectHandler =>
"UntargetedFlood_OneDirectHandler",
DispatchBenchmarkScenario.UntargetedFloodTwoHandlersOnePriority =>
"UntargetedFlood_TwoHandlers_OnePriority",
DispatchBenchmarkScenario.UntargetedFloodThreeHandlersOnePriority =>
Expand Down Expand Up @@ -577,6 +579,8 @@ public static string DisplayName(DispatchBenchmarkScenario scenario)
DispatchBenchmarkScenario.EmptyBusDispatch => "Empty Bus Dispatch",
DispatchBenchmarkScenario.UntargetedFloodOneHandler =>
"Untargeted Flood (One Handler)",
DispatchBenchmarkScenario.UntargetedFloodOneDirectHandler =>
"Untargeted Flood (One Direct Handler)",
DispatchBenchmarkScenario.UntargetedFloodTwoHandlersOnePriority =>
"Untargeted Flood (Two Handlers, One Priority)",
DispatchBenchmarkScenario.UntargetedFloodThreeHandlersOnePriority =>
Expand Down
97 changes: 91 additions & 6 deletions Tests/Runtime/Benchmarks/DispatchThroughputBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum DispatchBenchmarkScenario
{
EmptyBusDispatch,
UntargetedFloodOneHandler,
UntargetedFloodOneDirectHandler,
UntargetedFloodTwoHandlersOnePriority,
UntargetedFloodThreeHandlersOnePriority,
UntargetedFloodFourHandlersOnePriority,
Expand Down Expand Up @@ -57,7 +58,8 @@ public enum DispatchBenchmarkScenario
public sealed class DispatchThroughputBenchmarks
{
internal const int PublishedDispatchOrder = 0;
internal const int DeregistrationAttributionOrder = 1;
internal const int RegistrationAttributionOrder = 1;
internal const int DeregistrationAttributionOrder = 2;

[Test, Performance, Category("PerfBench")]
public void MessageRegistrationHandlePhysicalSize()
Expand Down Expand Up @@ -132,6 +134,17 @@ public void DispatchBenchmark(DispatchBenchmarkScenario scenario)
_ = RunScenario(scenario);
}

[Test, Performance, Category("PerfBench"), Order(RegistrationAttributionOrder)]
[TestCaseSource(nameof(RegistrationAttributionBenchmarkCases))]
public void RegistrationAttributionBenchmark(RegistrationAttributionOperation operation)
{
DispatchBenchmarkResult result = RegistrationAttributionBenchmarks.RunScenario(
operation
);
Debug.Log(result.ToStructuredLog());
TestContext.Out.WriteLine(result.ToCsvRow());
}

[Test, Performance, Category("PerfBench"), Order(DeregistrationAttributionOrder)]
[TestCaseSource(nameof(DeregistrationAttributionBenchmarkCases))]
public void DeregistrationAttributionBenchmark(DeregistrationAttributionOperation operation)
Expand Down Expand Up @@ -238,6 +251,20 @@ DeregistrationAttributionOperation operation in Enum.GetValues(
}
}

private static IEnumerable<TestCaseData> RegistrationAttributionBenchmarkCases()
{
foreach (
RegistrationAttributionOperation operation in Enum.GetValues(
typeof(RegistrationAttributionOperation)
)
)
{
yield return new TestCaseData(operation).SetName(
RegistrationAttributionBenchmarks.ScenarioKey(operation)
);
}
}

private static DispatchBenchmarkResult MeasureEmitScenario(
DispatchBenchmarkScenario scenario
)
Expand Down Expand Up @@ -317,6 +344,7 @@ internal static int ExpectedHandlerInvocationsPerEmit(DispatchBenchmarkScenario
case DispatchBenchmarkScenario.TargetedFloodNoMatchingTarget:
return 0;
case DispatchBenchmarkScenario.UntargetedFloodOneHandler:
case DispatchBenchmarkScenario.UntargetedFloodOneDirectHandler:
case DispatchBenchmarkScenario.TargetedFloodOneListener:
case DispatchBenchmarkScenario.BroadcastFloodOneHandler:
case DispatchBenchmarkScenario.InterceptorHeavyFourInterceptors:
Expand Down Expand Up @@ -1153,6 +1181,9 @@ InvocationCounter handlerInvocations
case DispatchBenchmarkScenario.UntargetedFloodOneHandler:
RegisterUntargeted(scope, handlerInvocations, 0);
return;
case DispatchBenchmarkScenario.UntargetedFloodOneDirectHandler:
scope.RegisterUntargetedDirect(handlerInvocations);
return;
case DispatchBenchmarkScenario.UntargetedFloodTwoHandlersOnePriority:
for (int index = 0; index < 2; index++)
{
Expand Down Expand Up @@ -1281,6 +1312,7 @@ private static void EmitMany(MessageBus bus, DispatchBenchmarkScenario scenario,
{
case DispatchBenchmarkScenario.EmptyBusDispatch:
case DispatchBenchmarkScenario.UntargetedFloodOneHandler:
case DispatchBenchmarkScenario.UntargetedFloodOneDirectHandler:
case DispatchBenchmarkScenario.UntargetedFloodTwoHandlersOnePriority:
case DispatchBenchmarkScenario.UntargetedFloodThreeHandlersOnePriority:
case DispatchBenchmarkScenario.UntargetedFloodFourHandlersOnePriority:
Expand Down Expand Up @@ -1362,7 +1394,9 @@ DispatchBenchmarkScenario scenario
return new DispatchScenarioContractObservation(
scenarioFanOut,
handlerInvocations.Count,
registrationBuckets
registrationBuckets,
scope.TokenRegistrations,
scope.HasDirectUntargetedRegistration
);
}

Expand All @@ -1371,19 +1405,27 @@ internal readonly struct DispatchScenarioContractObservation
internal DispatchScenarioContractObservation(
long scenarioFanOut,
long controlFanOut,
int registrationBuckets
int registrationBuckets,
int tokenRegistrations,
bool hasDirectUntargetedRegistration
)
{
ScenarioFanOut = scenarioFanOut;
ControlFanOut = controlFanOut;
RegistrationBuckets = registrationBuckets;
TokenRegistrations = tokenRegistrations;
HasDirectUntargetedRegistration = hasDirectUntargetedRegistration;
}

internal long ScenarioFanOut { get; }

internal long ControlFanOut { get; }

internal int RegistrationBuckets { get; }

internal int TokenRegistrations { get; }

internal bool HasDirectUntargetedRegistration { get; }
}

private static bool AllowUntargeted(ref SimpleUntargetedMessage message)
Expand Down Expand Up @@ -1933,6 +1975,8 @@ private sealed class BenchmarkRegistrationScope : IDisposable
{
private readonly List<MessageRegistrationToken> _tokens = new();
private readonly List<MessageHandler> _handlers = new();
private MessageHandler.TypedHandler<SimpleUntargetedMessage>.TypedHandlerDeregistrationState _directUntargetedDeregistration;
private bool _hasDirectUntargetedDeregistration;
private int _nextOwner = 32000;

public BenchmarkRegistrationScope()
Expand All @@ -1949,6 +1993,21 @@ public BenchmarkRegistrationScope()

public MessageRegistrationToken PrimaryToken { get; }

public int TokenRegistrations
{
get
{
int count = 0;
for (int index = 0; index < _tokens.Count; index++)
{
count += _tokens[index]._metadata.Count;
}
return count;
}
}

public bool HasDirectUntargetedRegistration => _hasDirectUntargetedDeregistration;

public MessageRegistrationToken CreateToken(bool active = true)
{
MessageHandler handler = new(new InstanceId(_nextOwner++), Bus) { active = active };
Expand All @@ -1960,6 +2019,21 @@ public MessageRegistrationToken CreateToken(bool active = true)
return token;
}

public void RegisterUntargetedDirect(InvocationCounter handlerInvocations)
{
MessageHandler handler = new(new InstanceId(_nextOwner++), Bus) { active = true };
MessageHandler.FastHandler<SimpleUntargetedMessage> callback = (
ref SimpleUntargetedMessage message
) => handlerInvocations.Increment();
_directUntargetedDeregistration = handler.RegisterUntargetedMessageHandler(
callback,
callback,
messageBus: Bus
);
_hasDirectUntargetedDeregistration = true;
_handlers.Add(handler);
}

public void SetAllHandlersActive(bool active)
{
for (int index = 0; index < _handlers.Count; index++)
Expand All @@ -1970,10 +2044,21 @@ public void SetAllHandlersActive(bool active)

public void Dispose()
{
for (int index = _tokens.Count - 1; index >= 0; index--)
try
{
if (_hasDirectUntargetedDeregistration)
{
_directUntargetedDeregistration.Deregister();
_hasDirectUntargetedDeregistration = false;
}
}
finally
{
_tokens[index].UnregisterAll();
_tokens[index].Dispose();
for (int index = _tokens.Count - 1; index >= 0; index--)
{
_tokens[index].UnregisterAll();
_tokens[index].Dispose();
}
}
}
}
Expand Down
Loading
Loading