188 lines
10 KiB
C#
188 lines
10 KiB
C#
using AyCode.Core.Extensions;
|
|
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.StandardOptions);
|
|
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.StandardOptions);
|
|
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.StandardOptions);
|
|
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
|
|
} |