AyCode.Core/AyCode.Benchmark/SourceGeneratorBenchmarks.cs

295 lines
12 KiB
C#

using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Binaries;
using BenchmarkDotNet.Attributes;
using MessagePack;
using MessagePack.Resolvers;
namespace AyCode.Core.Benchmarks;
/// <summary>
/// Pure contractless model - NO MessagePack attributes.
/// This tests TRUE runtime serialization without any source generation.
/// </summary>
[AcBinarySerializable]
public class PureContractlessModel
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public bool IsActive { get; set; }
public double Value { get; set; }
public decimal Price { get; set; }
public long BigNumber { get; set; }
public DateTime CreatedAt { get; set; }
public Guid UniqueId { get; set; }
public int Count { get; set; }
public string Category { get; set; } = "";
public string Status { get; set; } = "";
}
/// <summary>
/// Benchmark model with only primitive types - fully supported by Source Generator.
/// MessagePack attributes added for fair comparison with Source Generator.
/// </summary>
[AcBinarySerializable]
[MessagePackObject]
public class PrimitiveBenchmarkModel
{
[Key(0)] public int Id { get; set; }
[Key(1)] public string Name { get; set; } = "";
[Key(2)] public string Description { get; set; } = "";
[Key(3)] public bool IsActive { get; set; }
[Key(4)] public double Value { get; set; }
[Key(5)] public decimal Price { get; set; }
[Key(6)] public long BigNumber { get; set; }
[Key(7)] public DateTime CreatedAt { get; set; }
[Key(8)] public Guid UniqueId { get; set; }
[Key(9)] public int Count { get; set; }
[Key(10)] public string Category { get; set; } = "";
[Key(11)] public string Status { get; set; } = "";
}
/// <summary>
/// TRUE Contractless benchmark - no attributes, pure runtime serialization.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class PureContractlessBenchmark
{
private PureContractlessModel _testData = null!;
private byte[] _acBinaryData = null!;
private byte[] _msgPackContractlessData = null!;
private AcBinarySerializerOptions _binaryOptions = null!;
private MessagePackSerializerOptions _msgPackContractlessOptions = null!;
[GlobalSetup]
public void Setup()
{
_testData = new PureContractlessModel
{
Id = 12345,
Name = "Test Product Name",
Description = "This is a longer description for testing string serialization performance",
IsActive = true,
Value = 123.456789,
Price = 99.99m,
BigNumber = 9876543210L,
CreatedAt = DateTime.UtcNow,
UniqueId = Guid.NewGuid(),
Count = 42,
Category = "Electronics",
Status = "Available"
};
_binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling();
_msgPackContractlessOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);
_acBinaryData = AcBinarySerializer.Serialize(_testData, _binaryOptions);
_msgPackContractlessData = MessagePackSerializer.Serialize(_testData, _msgPackContractlessOptions);
Console.WriteLine($"=== Pure Contractless (NO attributes) ===");
Console.WriteLine($"AcBinary: {_acBinaryData.Length} bytes");
Console.WriteLine($"MsgPack Contractless: {_msgPackContractlessData.Length} bytes");
}
[Benchmark(Description = "AcBinary Serialize")]
public byte[] Serialize_AcBinary()
=> AcBinarySerializer.Serialize(_testData, _binaryOptions);
[Benchmark(Description = "MsgPack Contractless Serialize", Baseline = true)]
public byte[] Serialize_MsgPack_Contractless()
=> MessagePackSerializer.Serialize(_testData, _msgPackContractlessOptions);
[Benchmark(Description = "AcBinary Deserialize")]
public PureContractlessModel? Deserialize_AcBinary()
=> AcBinaryDeserializer.Deserialize<PureContractlessModel>(_acBinaryData);
[Benchmark(Description = "MsgPack Contractless Deserialize")]
public PureContractlessModel? Deserialize_MsgPack_Contractless()
=> MessagePackSerializer.Deserialize<PureContractlessModel>(_msgPackContractlessData, _msgPackContractlessOptions);
}
/// <summary>
/// Benchmark comparing Source Generator vs Runtime serialization for primitive-only types.
/// Uses MessagePack with [MessagePackObject] attributes for fair Source Generator comparison.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class SourceGeneratorVsRuntimeBenchmark
{
private PrimitiveBenchmarkModel _testData = null!;
private byte[] _runtimeSerializedData = null!;
private byte[] _msgPackData = null!;
private byte[] _msgPackContractlessData = null!;
private AcBinarySerializerOptions _binaryOptions = null!;
private MessagePackSerializerOptions _msgPackOptions = null!;
private MessagePackSerializerOptions _msgPackContractlessOptions = null!;
[GlobalSetup]
public void Setup()
{
_testData = new PrimitiveBenchmarkModel
{
Id = 12345,
Name = "Test Product Name",
Description = "This is a longer description for testing string serialization performance",
IsActive = true,
Value = 123.456789,
Price = 99.99m,
BigNumber = 9876543210L,
CreatedAt = DateTime.UtcNow,
UniqueId = Guid.NewGuid(),
Count = 42,
Category = "Electronics",
Status = "Available"
};
_binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling();
// MessagePack with Source Generator (uses [MessagePackObject] + [Key] attributes)
_msgPackOptions = MessagePackSerializerOptions.Standard;
// MessagePack without Source Generator (Contractless - reflection based, like AcBinary runtime)
_msgPackContractlessOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);
// Pre-serialize for deserialize benchmarks
_runtimeSerializedData = AcBinarySerializer.Serialize(_testData, _binaryOptions);
_msgPackData = MessagePackSerializer.Serialize(_testData, _msgPackOptions);
_msgPackContractlessData = MessagePackSerializer.Serialize(_testData, _msgPackContractlessOptions);
// Print sizes
Console.WriteLine($"=== Primitive Model Serialization ===");
Console.WriteLine($"AcBinary Runtime: {_runtimeSerializedData.Length} bytes");
Console.WriteLine($"MessagePack SourceGen: {_msgPackData.Length} bytes");
Console.WriteLine($"MessagePack Contractless:{_msgPackContractlessData.Length} bytes");
// Verify generated serializer exists
var generatedType = typeof(PrimitiveBenchmarkModel).Assembly.GetType(
$"{typeof(PrimitiveBenchmarkModel).FullName}_AcBinarySerializer");
Console.WriteLine($"AcBinary Generated serializer found: {generatedType != null}");
}
#region Serialize Benchmarks
[Benchmark(Description = "AcBinary Runtime Serialize")]
public byte[] Serialize_AcBinary_Runtime()
=> AcBinarySerializer.Serialize(_testData, _binaryOptions);
[Benchmark(Description = "MsgPack SourceGen Serialize", Baseline = true)]
public byte[] Serialize_MsgPack_SourceGen()
=> MessagePackSerializer.Serialize(_testData, _msgPackOptions);
[Benchmark(Description = "MsgPack Contractless Serialize")]
public byte[] Serialize_MsgPack_Contractless()
=> MessagePackSerializer.Serialize(_testData, _msgPackContractlessOptions);
#endregion
#region Deserialize Benchmarks
[Benchmark(Description = "AcBinary Runtime Deserialize")]
public PrimitiveBenchmarkModel? Deserialize_AcBinary_Runtime()
=> AcBinaryDeserializer.Deserialize<PrimitiveBenchmarkModel>(_runtimeSerializedData);
[Benchmark(Description = "MsgPack SourceGen Deserialize")]
public PrimitiveBenchmarkModel? Deserialize_MsgPack_SourceGen()
=> MessagePackSerializer.Deserialize<PrimitiveBenchmarkModel>(_msgPackData, _msgPackOptions);
[Benchmark(Description = "MsgPack Contractless Deserialize")]
public PrimitiveBenchmarkModel? Deserialize_MsgPack_Contractless()
=> MessagePackSerializer.Deserialize<PrimitiveBenchmarkModel>(_msgPackContractlessData, _msgPackContractlessOptions);
#endregion
}
/// <summary>
/// Repeated string benchmark model - tests string interning performance.
/// MessagePack attributes added for fair comparison.
/// </summary>
[AcBinarySerializable]
[MessagePackObject]
public class RepeatedStringBenchmarkModel
{
[Key(0)] public int Id { get; set; }
[Key(1)] public string Status { get; set; } = "";
[Key(2)] public string Category { get; set; } = "";
[Key(3)] public string Priority { get; set; } = "";
[Key(4)] public string Type { get; set; } = "";
}
/// <summary>
/// Benchmark for types with repeated string values - where AcBinary string interning helps.
/// Compares against both MessagePack SourceGen and Contractless modes.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class RepeatedStringBenchmark
{
private List<RepeatedStringBenchmarkModel> _items = null!;
private byte[] _acBinaryData = null!;
private byte[] _msgPackData = null!;
private byte[] _msgPackContractlessData = null!;
private AcBinarySerializerOptions _binaryOptions = null!;
private MessagePackSerializerOptions _msgPackOptions = null!;
private MessagePackSerializerOptions _msgPackContractlessOptions = null!;
[Params(100, 500)]
public int ItemCount { get; set; }
[GlobalSetup]
public void Setup()
{
_items = Enumerable.Range(0, ItemCount).Select(i => new RepeatedStringBenchmarkModel
{
Id = i,
Status = i % 3 == 0 ? "Pending" : i % 3 == 1 ? "Processing" : "Completed",
Category = $"Category_{i % 5}",
Priority = i % 2 == 0 ? "High" : "Low",
Type = i % 4 == 0 ? "TypeA" : i % 4 == 1 ? "TypeB" : i % 4 == 2 ? "TypeC" : "TypeD"
}).ToList();
_binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling();
_msgPackOptions = MessagePackSerializerOptions.Standard;
_msgPackContractlessOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);
_acBinaryData = AcBinarySerializer.Serialize(_items, _binaryOptions);
_msgPackData = MessagePackSerializer.Serialize(_items, _msgPackOptions);
_msgPackContractlessData = MessagePackSerializer.Serialize(_items, _msgPackContractlessOptions);
Console.WriteLine($"=== Repeated Strings ({ItemCount} items) ===");
Console.WriteLine($"AcBinary: {_acBinaryData.Length} bytes");
Console.WriteLine($"MsgPack SourceGen: {_msgPackData.Length} bytes");
Console.WriteLine($"MsgPack Contractless: {_msgPackContractlessData.Length} bytes");
}
[Benchmark(Description = "AcBinary Serialize")]
public byte[] Serialize_AcBinary()
=> AcBinarySerializer.Serialize(_items, _binaryOptions);
[Benchmark(Description = "MsgPack SourceGen Serialize", Baseline = true)]
public byte[] Serialize_MsgPack_SourceGen()
=> MessagePackSerializer.Serialize(_items, _msgPackOptions);
[Benchmark(Description = "MsgPack Contractless Serialize")]
public byte[] Serialize_MsgPack_Contractless()
=> MessagePackSerializer.Serialize(_items, _msgPackContractlessOptions);
[Benchmark(Description = "AcBinary Deserialize")]
public List<RepeatedStringBenchmarkModel>? Deserialize_AcBinary()
=> AcBinaryDeserializer.Deserialize<List<RepeatedStringBenchmarkModel>>(_acBinaryData);
[Benchmark(Description = "MsgPack SourceGen Deserialize")]
public List<RepeatedStringBenchmarkModel>? Deserialize_MsgPack_SourceGen()
=> MessagePackSerializer.Deserialize<List<RepeatedStringBenchmarkModel>>(_msgPackData, _msgPackOptions);
[Benchmark(Description = "MsgPack Contractless Deserialize")]
public List<RepeatedStringBenchmarkModel>? Deserialize_MsgPack_Contractless()
=> MessagePackSerializer.Deserialize<List<RepeatedStringBenchmarkModel>>(_msgPackContractlessData, _msgPackContractlessOptions);
}