AyCode.Core/BenchmarkSuite1/SerializationBenchmarks.cs

339 lines
13 KiB
C#
Raw Blame History

using AyCode.Core.Extensions;
using AyCode.Core.Tests.TestModels;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Reports;
using Newtonsoft.Json;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace AyCode.Core.Benchmarks;
/// <summary>
/// Serialization benchmarks comparing AyCode, Newtonsoft.Json, and System.Text.Json.
/// Tests small, medium, and large data with and without reference handling.
/// </summary>
[MemoryDiagnoser]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[CategoriesColumn]
public class SerializationBenchmarks
{
// Test data - small, medium, large
private TestOrder _smallOrder = null!;
private TestOrder _mediumOrder = null!;
private TestOrder _largeOrder = null!;
// Pre-serialized JSON for deserialization benchmarks
private string _smallAyCodeJson = null!;
private string _smallAyCodeNoRefJson = null!;
private string _smallStjJson = null!;
private string _smallStjNoRefJson = null!;
private string _smallNewtonsoftJson = null!;
private string _mediumAyCodeJson = null!;
private string _mediumAyCodeNoRefJson = null!;
private string _mediumStjJson = null!;
private string _mediumStjNoRefJson = null!;
private string _mediumNewtonsoftJson = null!;
private string _largeAyCodeJson = null!;
private string _largeAyCodeNoRefJson = null!;
private string _largeStjJson = null!;
private string _largeStjNoRefJson = null!;
private string _largeNewtonsoftJson = null!;
// STJ options
private JsonSerializerOptions _stjWithRefs = null!;
private JsonSerializerOptions _stjNoRefs = null!;
// AyCode options
private AcJsonSerializerOptions _ayCodeWithRefs = null!;
private AcJsonSerializerOptions _ayCodeNoRefs = null!;
// Newtonsoft settings
private JsonSerializerSettings _newtonsoftSettings = null!;
[GlobalSetup]
public void Setup()
{
// Small: ~20 objects (1 item <20> 1 pallet <20> 2 measurements <20> 3 points)
_smallOrder = TestDataFactory.CreateBenchmarkOrder(
itemCount: 1,
palletsPerItem: 1,
measurementsPerPallet: 2,
pointsPerMeasurement: 3);
// Medium: ~300 objects (3 items <20> 2 pallets <20> 2 measurements <20> 5 points)
_mediumOrder = TestDataFactory.CreateBenchmarkOrder(
itemCount: 3,
palletsPerItem: 2,
measurementsPerPallet: 2,
pointsPerMeasurement: 5);
// Large: ~1500 objects (5 items <20> 4 pallets <20> 3 measurements <20> 5 points)
_largeOrder = TestDataFactory.CreateBenchmarkOrder(
itemCount: 5,
palletsPerItem: 4,
measurementsPerPallet: 3,
pointsPerMeasurement: 5);
// STJ options with reference handling
_stjWithRefs = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = null,
WriteIndented = false,
ReferenceHandler = ReferenceHandler.Preserve,
MaxDepth = 256
};
// STJ options without reference handling
_stjNoRefs = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = null,
WriteIndented = false,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
MaxDepth = 256
};
// AyCode options
_ayCodeWithRefs = AcJsonSerializerOptions.Default;
_ayCodeNoRefs = AcJsonSerializerOptions.WithoutReferenceHandling();
// Newtonsoft settings
_newtonsoftSettings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.None
};
// Pre-serialize for deserialization benchmarks
_smallAyCodeJson = AcJsonSerializer.Serialize(_smallOrder, _ayCodeWithRefs);
_smallAyCodeNoRefJson = AcJsonSerializer.Serialize(_smallOrder, _ayCodeNoRefs);
_smallStjJson = JsonSerializer.Serialize(_smallOrder, _stjWithRefs);
_smallStjNoRefJson = JsonSerializer.Serialize(_smallOrder, _stjNoRefs);
_smallNewtonsoftJson = JsonConvert.SerializeObject(_smallOrder, _newtonsoftSettings);
_mediumAyCodeJson = AcJsonSerializer.Serialize(_mediumOrder, _ayCodeWithRefs);
_mediumAyCodeNoRefJson = AcJsonSerializer.Serialize(_mediumOrder, _ayCodeNoRefs);
_mediumStjJson = JsonSerializer.Serialize(_mediumOrder, _stjWithRefs);
_mediumStjNoRefJson = JsonSerializer.Serialize(_mediumOrder, _stjNoRefs);
_mediumNewtonsoftJson = JsonConvert.SerializeObject(_mediumOrder, _newtonsoftSettings);
_largeAyCodeJson = AcJsonSerializer.Serialize(_largeOrder, _ayCodeWithRefs);
_largeAyCodeNoRefJson = AcJsonSerializer.Serialize(_largeOrder, _ayCodeNoRefs);
_largeStjJson = JsonSerializer.Serialize(_largeOrder, _stjWithRefs);
_largeStjNoRefJson = JsonSerializer.Serialize(_largeOrder, _stjNoRefs);
_largeNewtonsoftJson = JsonConvert.SerializeObject(_largeOrder, _newtonsoftSettings);
// Output sizes for comparison
Console.WriteLine("=== JSON Size Comparison ===");
Console.WriteLine($"Small: AyCode(refs)={_smallAyCodeJson.Length:N0}, AyCode(noRef)={_smallAyCodeNoRefJson.Length:N0}, STJ(refs)={_smallStjJson.Length:N0}, STJ(noRef)={_smallStjNoRefJson.Length:N0}");
Console.WriteLine($"Medium: AyCode(refs)={_mediumAyCodeJson.Length:N0}, AyCode(noRef)={_mediumAyCodeNoRefJson.Length:N0}, STJ(refs)={_mediumStjJson.Length:N0}, STJ(noRef)={_mediumStjNoRefJson.Length:N0}");
Console.WriteLine($"Large: AyCode(refs)={_largeAyCodeJson.Length:N0}, AyCode(noRef)={_largeAyCodeNoRefJson.Length:N0}, STJ(refs)={_largeStjJson.Length:N0}, STJ(noRef)={_largeStjNoRefJson.Length:N0}");
}
#region Serialize Large - With Refs
[Benchmark(Description = "AyCode Serialize")]
[BenchmarkCategory("Serialize-Large-WithRefs")]
public string Serialize_Large_AyCode_WithRefs()
=> AcJsonSerializer.Serialize(_largeOrder, _ayCodeWithRefs);
[Benchmark(Description = "STJ Serialize", Baseline = true)]
[BenchmarkCategory("Serialize-Large-WithRefs")]
public string Serialize_Large_STJ_WithRefs()
=> JsonSerializer.Serialize(_largeOrder, _stjWithRefs);
#endregion
#region Serialize Large - No Refs
[Benchmark(Description = "AyCode Serialize")]
[BenchmarkCategory("Serialize-Large-NoRefs")]
public string Serialize_Large_AyCode_NoRefs()
=> AcJsonSerializer.Serialize(_largeOrder, _ayCodeNoRefs);
[Benchmark(Description = "STJ Serialize", Baseline = true)]
[BenchmarkCategory("Serialize-Large-NoRefs")]
public string Serialize_Large_STJ_NoRefs()
=> JsonSerializer.Serialize(_largeOrder, _stjNoRefs);
#endregion
#region Serialize Medium - With Refs
[Benchmark(Description = "AyCode Serialize")]
[BenchmarkCategory("Serialize-Medium-WithRefs")]
public string Serialize_Medium_AyCode_WithRefs()
=> AcJsonSerializer.Serialize(_mediumOrder, _ayCodeWithRefs);
[Benchmark(Description = "STJ Serialize", Baseline = true)]
[BenchmarkCategory("Serialize-Medium-WithRefs")]
public string Serialize_Medium_STJ_WithRefs()
=> JsonSerializer.Serialize(_mediumOrder, _stjWithRefs);
#endregion
#region Serialize Medium - No Refs
[Benchmark(Description = "AyCode Serialize")]
[BenchmarkCategory("Serialize-Medium-NoRefs")]
public string Serialize_Medium_AyCode_NoRefs()
=> AcJsonSerializer.Serialize(_mediumOrder, _ayCodeNoRefs);
[Benchmark(Description = "STJ Serialize", Baseline = true)]
[BenchmarkCategory("Serialize-Medium-NoRefs")]
public string Serialize_Medium_STJ_NoRefs()
=> JsonSerializer.Serialize(_mediumOrder, _stjNoRefs);
#endregion
#region Small Data Deserialization - With Refs
[Benchmark(Description = "AyCode WithRefs")]
[BenchmarkCategory("Deserialize-Small-WithRefs")]
public TestOrder? Deserialize_Small_AyCode_WithRefs()
=> AcJsonDeserializer.Deserialize<TestOrder>(_smallAyCodeJson, _ayCodeWithRefs);
[Benchmark(Description = "STJ WithRefs", Baseline = true)]
[BenchmarkCategory("Deserialize-Small-WithRefs")]
public TestOrder? Deserialize_Small_STJ_WithRefs()
=> JsonSerializer.Deserialize<TestOrder>(_smallStjJson, _stjWithRefs);
#endregion
#region Small Data Deserialization - No Refs
[Benchmark(Description = "AyCode NoRefs")]
[BenchmarkCategory("Deserialize-Small-NoRefs")]
public TestOrder? Deserialize_Small_AyCode_NoRefs()
=> AcJsonDeserializer.Deserialize<TestOrder>(_smallAyCodeNoRefJson, _ayCodeNoRefs);
[Benchmark(Description = "STJ NoRefs", Baseline = true)]
[BenchmarkCategory("Deserialize-Small-NoRefs")]
public TestOrder? Deserialize_Small_STJ_NoRefs()
=> JsonSerializer.Deserialize<TestOrder>(_smallStjNoRefJson, _stjNoRefs);
#endregion
#region Medium Data Deserialization - With Refs
[Benchmark(Description = "AyCode WithRefs")]
[BenchmarkCategory("Deserialize-Medium-WithRefs")]
public TestOrder? Deserialize_Medium_AyCode_WithRefs()
=> AcJsonDeserializer.Deserialize<TestOrder>(_mediumAyCodeJson, _ayCodeWithRefs);
[Benchmark(Description = "STJ WithRefs", Baseline = true)]
[BenchmarkCategory("Deserialize-Medium-WithRefs")]
public TestOrder? Deserialize_Medium_STJ_WithRefs()
=> JsonSerializer.Deserialize<TestOrder>(_mediumStjJson, _stjWithRefs);
#endregion
#region Medium Data Deserialization - No Refs
[Benchmark(Description = "AyCode NoRefs")]
[BenchmarkCategory("Deserialize-Medium-NoRefs")]
public TestOrder? Deserialize_Medium_AyCode_NoRefs()
=> AcJsonDeserializer.Deserialize<TestOrder>(_mediumAyCodeNoRefJson, _ayCodeNoRefs);
[Benchmark(Description = "STJ NoRefs", Baseline = true)]
[BenchmarkCategory("Deserialize-Medium-NoRefs")]
public TestOrder? Deserialize_Medium_STJ_NoRefs()
=> JsonSerializer.Deserialize<TestOrder>(_mediumStjNoRefJson, _stjNoRefs);
#endregion
#region Large Data Deserialization - With Refs
[Benchmark(Description = "AyCode WithRefs")]
[BenchmarkCategory("Deserialize-Large-WithRefs")]
public TestOrder? Deserialize_Large_AyCode_WithRefs()
=> AcJsonDeserializer.Deserialize<TestOrder>(_largeAyCodeJson, _ayCodeWithRefs);
[Benchmark(Description = "STJ WithRefs", Baseline = true)]
[BenchmarkCategory("Deserialize-Large-WithRefs")]
public TestOrder? Deserialize_Large_STJ_WithRefs()
=> JsonSerializer.Deserialize<TestOrder>(_largeStjJson, _stjWithRefs);
#endregion
#region Large Data Deserialization - No Refs
[Benchmark(Description = "AyCode NoRefs")]
[BenchmarkCategory("Deserialize-Large-NoRefs")]
public TestOrder? Deserialize_Large_AyCode_NoRefs()
=> AcJsonDeserializer.Deserialize<TestOrder>(_largeAyCodeNoRefJson, _ayCodeNoRefs);
[Benchmark(Description = "STJ NoRefs", Baseline = true)]
[BenchmarkCategory("Deserialize-Large-NoRefs")]
public TestOrder? Deserialize_Large_STJ_NoRefs()
=> JsonSerializer.Deserialize<TestOrder>(_largeStjNoRefJson, _stjNoRefs);
#endregion
#region Populate Benchmarks - Small
[Benchmark(Description = "AyCode Populate")]
[BenchmarkCategory("Populate-Small")]
public void Populate_Small_AyCode()
{
var target = new TestOrder();
AcJsonDeserializer.Populate(_smallAyCodeJson, target);
}
[Benchmark(Description = "Newtonsoft PopulateObject", Baseline = true)]
[BenchmarkCategory("Populate-Small")]
public void Populate_Small_Newtonsoft()
{
var target = new TestOrder();
JsonConvert.PopulateObject(_smallNewtonsoftJson, target, _newtonsoftSettings);
}
#endregion
#region Populate Benchmarks - Medium
[Benchmark(Description = "AyCode Populate")]
[BenchmarkCategory("Populate-Medium")]
public void Populate_Medium_AyCode()
{
var target = new TestOrder();
AcJsonDeserializer.Populate(_mediumAyCodeJson, target);
}
[Benchmark(Description = "Newtonsoft PopulateObject", Baseline = true)]
[BenchmarkCategory("Populate-Medium")]
public void Populate_Medium_Newtonsoft()
{
var target = new TestOrder();
JsonConvert.PopulateObject(_mediumNewtonsoftJson, target, _newtonsoftSettings);
}
#endregion
#region Populate Benchmarks - Large
[Benchmark(Description = "AyCode Populate")]
[BenchmarkCategory("Populate-Large")]
public void Populate_Large_AyCode()
{
var target = new TestOrder();
AcJsonDeserializer.Populate(_largeAyCodeJson, target);
}
[Benchmark(Description = "Newtonsoft PopulateObject", Baseline = true)]
[BenchmarkCategory("Populate-Large")]
public void Populate_Large_Newtonsoft()
{
var target = new TestOrder();
JsonConvert.PopulateObject(_largeNewtonsoftJson, target, _newtonsoftSettings);
}
#endregion
}