using AyCode.Core.Extensions; using AyCode.Core.Serializers.Jsons; using AyCode.Core.Tests.TestModels; using BenchmarkDotNet.Attributes; using MessagePack; namespace AyCode.Core.Benchmarks; /// /// SignalR communication benchmarks measuring the full serialization workflow: /// Client ? IdMessage ? MessagePack ? Server ? Deserialize ? Response ? MessagePack ? Client /// [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(_singleIntMessage, SignalRMessageFactory.ContractlessOptions); var idMessage = postMessage.PostDataJson!.JsonTo()!; return AcJsonDeserializer.Deserialize(idMessage.Ids[0]); } [Benchmark(Description = "Server: Deserialize two ints")] [BenchmarkCategory("Server", "Deserialize")] public (int, int) Server_DeserializeTwoInts() { var postMessage = MessagePackSerializer.Deserialize(_twoIntMessage, SignalRMessageFactory.ContractlessOptions); var idMessage = postMessage.PostDataJson!.JsonTo()!; var a = AcJsonDeserializer.Deserialize(idMessage.Ids[0]); var b = AcJsonDeserializer.Deserialize(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(_fiveParamsMessage, SignalRMessageFactory.ContractlessOptions); var idMessage = postMessage.PostDataJson!.JsonTo()!; var a = AcJsonDeserializer.Deserialize(idMessage.Ids[0]); var b = AcJsonDeserializer.Deserialize(idMessage.Ids[1])!; var c = AcJsonDeserializer.Deserialize(idMessage.Ids[2]); var d = AcJsonDeserializer.Deserialize(idMessage.Ids[3]); var e = AcJsonDeserializer.Deserialize(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(_complexOrderItemMessage, SignalRMessageFactory.ContractlessOptions); return postMessage.PostDataJson!.JsonTo()!; } [Benchmark(Description = "Server: Deserialize complex Order")] [BenchmarkCategory("Server", "Deserialize")] public TestOrder Server_DeserializeComplexOrder() { var postMessage = MessagePackSerializer.Deserialize(_complexOrderMessage, SignalRMessageFactory.ContractlessOptions); return postMessage.PostDataJson!.JsonTo()!; } #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(); } #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(requestBytes, SignalRMessageFactory.ContractlessOptions); var idMessage = postMessage.PostDataJson!.JsonTo()!; var value = AcJsonDeserializer.Deserialize(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(requestBytes, SignalRMessageFactory.ContractlessOptions); var order = postMessage.PostDataJson!.JsonTo()!; // 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(); } #endregion }