339 lines
13 KiB
C#
339 lines
13 KiB
C#
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
|
||
} |