AyCode.Core/AyCode.Benchmark/SignalRCommunicationBenchma...

189 lines
10 KiB
C#

using AyCode.Core.Extensions;
using AyCode.Core.Serializers.Jsons;
using AyCode.Core.Tests.TestModels;
using BenchmarkDotNet.Attributes;
using MessagePack;
namespace AyCode.Core.Benchmarks;
/// <summary>
/// SignalR communication benchmarks measuring the full serialization workflow:
/// Client ? IdMessage ? MessagePack ? Server ? Deserialize ? Response ? MessagePack ? Client
/// </summary>
[MemoryDiagnoser]
public class SignalRCommunicationBenchmarks
{
// Shared test data
private SignalRBenchmarkData _data = null !;
// Pre-serialized messages for deserialization benchmarks
private byte[] _singleIntMessage = null !;
private byte[] _twoIntMessage = null !;
private byte[] _fiveParamsMessage = null !;
private byte[] _complexOrderItemMessage = null !;
private byte[] _complexOrderMessage = null !;
private byte[] _intArrayMessage = null !;
private byte[] _mixedParamsMessage = null !;
// Pre-serialized response for client-side deserialization
private byte[] _successResponseMessage = null !;
private byte[] _complexResponseMessage = null !;
[GlobalSetup]
public void Setup()
{
_data = new SignalRBenchmarkData();
// Copy pre-serialized messages
_singleIntMessage = _data.SingleIntMessage;
_twoIntMessage = _data.TwoIntMessage;
_fiveParamsMessage = _data.FiveParamsMessage;
_complexOrderItemMessage = _data.ComplexOrderItemMessage;
_complexOrderMessage = _data.ComplexOrderMessage;
_intArrayMessage = _data.IntArrayMessage;
_mixedParamsMessage = _data.MixedParamsMessage;
// Pre-serialize response messages
_successResponseMessage = SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.SingleIntParam, "Received: 42");
_complexResponseMessage = SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.TestOrderParam, _data.TestOrder);
Console.WriteLine("=== SignalR Message Size Comparison ===");
Console.WriteLine($"Single int message: {_singleIntMessage.Length} bytes");
Console.WriteLine($"Two int message: {_twoIntMessage.Length} bytes");
Console.WriteLine($"Five params message: {_fiveParamsMessage.Length} bytes");
Console.WriteLine($"Complex OrderItem message: {_complexOrderItemMessage.Length} bytes");
Console.WriteLine($"Complex Order message: {_complexOrderMessage.Length} bytes");
Console.WriteLine($"Int array message: {_intArrayMessage.Length} bytes");
Console.WriteLine($"Mixed params message: {_mixedParamsMessage.Length} bytes");
Console.WriteLine($"Success response: {_successResponseMessage.Length} bytes");
Console.WriteLine($"Complex response: {_complexResponseMessage.Length} bytes");
}
#region Client-Side: Message Creation (IdMessage + MessagePack Serialization)
[Benchmark(Description = "Client: Create single int message")]
[BenchmarkCategory("Client", "Create")]
public byte[] Client_CreateSingleIntMessage() => SignalRMessageFactory.CreateSingleParamMessage(42);
[Benchmark(Description = "Client: Create two int message")]
[BenchmarkCategory("Client", "Create")]
public byte[] Client_CreateTwoIntMessage() => SignalRMessageFactory.CreateIdMessage(10, 20);
[Benchmark(Description = "Client: Create five params message")]
[BenchmarkCategory("Client", "Create")]
public byte[] Client_CreateFiveParamsMessage() => SignalRMessageFactory.CreateIdMessage(42, "hello", true, _data.TestGuid, 99.99m);
[Benchmark(Description = "Client: Create complex OrderItem message")]
[BenchmarkCategory("Client", "Create")]
public byte[] Client_CreateComplexOrderItemMessage() => SignalRMessageFactory.CreateComplexObjectMessage(_data.TestOrderItem);
[Benchmark(Description = "Client: Create complex Order message")]
[BenchmarkCategory("Client", "Create")]
public byte[] Client_CreateComplexOrderMessage() => SignalRMessageFactory.CreateComplexObjectMessage(_data.TestOrder);
#endregion
#region Server-Side: Message Deserialization (MessagePack + JSON)
[Benchmark(Description = "Server: Deserialize single int")]
[BenchmarkCategory("Server", "Deserialize")]
public int Server_DeserializeSingleInt()
{
var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_singleIntMessage, SignalRMessageFactory.ContractlessOptions);
var idMessage = postMessage.PostDataJson!.JsonTo<SignalRIdMessageDto>()!;
return AcJsonDeserializer.Deserialize<int>(idMessage.Ids[0]);
}
[Benchmark(Description = "Server: Deserialize two ints")]
[BenchmarkCategory("Server", "Deserialize")]
public (int, int) Server_DeserializeTwoInts()
{
var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_twoIntMessage, SignalRMessageFactory.ContractlessOptions);
var idMessage = postMessage.PostDataJson!.JsonTo<SignalRIdMessageDto>()!;
var a = AcJsonDeserializer.Deserialize<int>(idMessage.Ids[0]);
var b = AcJsonDeserializer.Deserialize<int>(idMessage.Ids[1]);
return (a, b);
}
[Benchmark(Description = "Server: Deserialize five params")]
[BenchmarkCategory("Server", "Deserialize")]
public (int, string, bool, Guid, decimal) Server_DeserializeFiveParams()
{
var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_fiveParamsMessage, SignalRMessageFactory.ContractlessOptions);
var idMessage = postMessage.PostDataJson!.JsonTo<SignalRIdMessageDto>()!;
var a = AcJsonDeserializer.Deserialize<int>(idMessage.Ids[0]);
var b = AcJsonDeserializer.Deserialize<string>(idMessage.Ids[1])!;
var c = AcJsonDeserializer.Deserialize<bool>(idMessage.Ids[2]);
var d = AcJsonDeserializer.Deserialize<Guid>(idMessage.Ids[3]);
var e = AcJsonDeserializer.Deserialize<decimal>(idMessage.Ids[4]);
return (a, b, c, d, e);
}
[Benchmark(Description = "Server: Deserialize complex OrderItem")]
[BenchmarkCategory("Server", "Deserialize")]
public TestOrderItem Server_DeserializeComplexOrderItem()
{
var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_complexOrderItemMessage, SignalRMessageFactory.ContractlessOptions);
return postMessage.PostDataJson!.JsonTo<TestOrderItem>()!;
}
[Benchmark(Description = "Server: Deserialize complex Order")]
[BenchmarkCategory("Server", "Deserialize")]
public TestOrder Server_DeserializeComplexOrder()
{
var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_complexOrderMessage, SignalRMessageFactory.ContractlessOptions);
return postMessage.PostDataJson!.JsonTo<TestOrder>()!;
}
#endregion
#region Server-Side: Response Creation (JSON + MessagePack Serialization)
[Benchmark(Description = "Server: Create success response (string)")]
[BenchmarkCategory("Server", "Response")]
public byte[] Server_CreateSuccessStringResponse() => SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.SingleIntParam, "Received: 42");
[Benchmark(Description = "Server: Create success response (OrderItem)")]
[BenchmarkCategory("Server", "Response")]
public byte[] Server_CreateSuccessOrderItemResponse() => SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.TestOrderItemParam, _data.TestOrderItem);
[Benchmark(Description = "Server: Create success response (Order)")]
[BenchmarkCategory("Server", "Response")]
public byte[] Server_CreateSuccessOrderResponse() => SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.TestOrderParam, _data.TestOrder);
#endregion
#region Client-Side: Response Deserialization
[Benchmark(Description = "Client: Deserialize string response")]
[BenchmarkCategory("Client", "Response")]
public string? Client_DeserializeStringResponse()
{
var response = SignalRMessageFactory.DeserializeResponse(_successResponseMessage);
return response?.ResponseData;
}
[Benchmark(Description = "Client: Deserialize complex Order response")]
[BenchmarkCategory("Client", "Response")]
public TestOrder? Client_DeserializeOrderResponse()
{
var response = SignalRMessageFactory.DeserializeResponse(_complexResponseMessage);
return response?.ResponseData?.JsonTo<TestOrder>();
}
#endregion
#region Full Round-Trip Benchmarks
[Benchmark(Description = "Full: Single int round-trip")]
[BenchmarkCategory("Full")]
public string? Full_SingleIntRoundTrip()
{
// Client creates message
var requestBytes = SignalRMessageFactory.CreateSingleParamMessage(42);
// Server deserializes
var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(requestBytes, SignalRMessageFactory.ContractlessOptions);
var idMessage = postMessage.PostDataJson!.JsonTo<SignalRIdMessageDto>()!;
var value = AcJsonDeserializer.Deserialize<int>(idMessage.Ids[0]);
// Server creates response
var responseBytes = SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.SingleIntParam, $"Received: {value}");
// Client deserializes response
var response = SignalRMessageFactory.DeserializeResponse(responseBytes);
return response?.ResponseData;
}
[Benchmark(Description = "Full: Complex Order round-trip")]
[BenchmarkCategory("Full")]
public TestOrder? Full_ComplexOrderRoundTrip()
{
// Client creates message
var requestBytes = SignalRMessageFactory.CreateComplexObjectMessage(_data.TestOrder);
// Server deserializes
var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(requestBytes, SignalRMessageFactory.ContractlessOptions);
var order = postMessage.PostDataJson!.JsonTo<TestOrder>()!;
// Server modifies and creates response
order.OrderNumber = "PROCESSED-" + order.OrderNumber;
var responseBytes = SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.TestOrderParam, order);
// Client deserializes response
var response = SignalRMessageFactory.DeserializeResponse(responseBytes);
return response?.ResponseData?.JsonTo<TestOrder>();
}
#endregion
}