diff --git a/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/SerializersBenchmark.cs b/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/SerializersBenchmark.cs index a9496d4..597fc93 100644 --- a/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/SerializersBenchmark.cs +++ b/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/SerializersBenchmark.cs @@ -5,144 +5,132 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Order; +using BenchmarkDotNet.Toolchains.InProcess.Emit; +using MemoryPack; +using MessagePack; using Microsoft.IO; using ZiggyCreatures.Caching.Fusion.Serialization; +using ZiggyCreatures.Caching.Fusion.Serialization.CysharpMemoryPack; +using ZiggyCreatures.Caching.Fusion.Serialization.NeueccMessagePack; +using ZiggyCreatures.Caching.Fusion.Serialization.NewtonsoftJson; using ZiggyCreatures.Caching.Fusion.Serialization.ProtoBufNet; +using ZiggyCreatures.Caching.Fusion.Serialization.ServiceStackJson; using ZiggyCreatures.Caching.Fusion.Serialization.SystemTextJson; namespace ZiggyCreatures.Caching.Fusion.Benchmarks; -public abstract class AbstractSerializersBenchmark +[DataContract] +[MessagePackObject] +[MemoryPackable] +public partial class SampleModel { - protected static Random _MyRandom = new Random(2110); - - [DataContract] - protected class SampleModel + private static readonly Random _MyRandom = new(2110); + + [DataMember(Order = 1)] + [Key(0)] + public string? Name { get; set; } + [DataMember(Order = 2)] + [Key(1)] + public int Age { get; set; } + [DataMember(Order = 3)] + [Key(2)] + public DateTime Date { get; set; } + [DataMember(Order = 4)] + [Key(3)] + public List FavoriteNumbers { get; set; } = []; + + public static SampleModel GenerateRandom() { - [DataMember(Order = 1)] - public string? Name { get; set; } - [DataMember(Order = 2)] - public int Age { get; set; } - [DataMember(Order = 3)] - public DateTime Date { get; set; } - [DataMember(Order = 4)] - public List FavoriteNumbers { get; set; } = []; - - public static SampleModel GenerateRandom() + var model = new SampleModel + { + Name = Guid.NewGuid().ToString("N"), + Age = _MyRandom.Next(1, 100), + Date = DateTime.UtcNow, + }; + for (int i = 0; i < 10; i++) { - var model = new SampleModel - { - Name = Guid.NewGuid().ToString("N"), - Age = _MyRandom.Next(1, 100), - Date = DateTime.UtcNow, - }; - for (int i = 0; i < 10; i++) - { - model.FavoriteNumbers.Add(_MyRandom.Next(1, 1000)); - } - return model; + model.FavoriteNumbers.Add(_MyRandom.Next(1, 1000)); } + return model; } +} - protected class Config : ManualConfig +[Config(typeof(Config))] +public class SerializersBenchmark +{ + public class Config : ManualConfig { public Config() { AddColumn(StatisticColumn.P95); + AddDiagnoser(MemoryDiagnoser.Default); + AddLogicalGroupRules(BenchmarkLogicalGroupRule.ByMethod); + AddJob(Job.Default.WithToolchain(InProcessEmitToolchain.Instance)); + WithOrderer(new DefaultOrderer(summaryOrderPolicy: SummaryOrderPolicy.FastestToSlowest)); + WithSummaryStyle(BenchmarkDotNet.Reports.SummaryStyle.Default.WithMaxParameterColumnWidth(50)); } } - protected IFusionCacheSerializer _Normal = null!; - protected IFusionCacheSerializer _Recyclable = null!; + [ParamsSource(nameof(GetSerializers))] + public IFusionCacheSerializer Serializer = null!; protected List _Models = []; protected byte[] _Blob = null!; - [Params(1, 100, 1_000)] - public int Size; - [GlobalSetup] public void Setup() { - for (int i = 0; i < Size; i++) + for (int i = 0; i < 1000; i++) { _Models.Add(SampleModel.GenerateRandom()); } - _Blob = _Normal.Serialize(_Models); - } - [Benchmark(Baseline = true)] - public void Serialize_Normal() - { - _Normal.Serialize(_Models); + _Blob = Serializer.Serialize(_Models); } [Benchmark] - public void Serialize_Recyclable() + public void Serialize() { - _Recyclable.Serialize(_Models); + Serializer.Serialize(_Models); } [Benchmark] - public void Deserialize_Normal() + public void Deserialize() { - _Normal.Deserialize>(_Blob); + Serializer.Deserialize>(_Blob); } [Benchmark] - public void Deserialize_Recyclable() + public async Task SerializeAsync() { - _Recyclable.Deserialize>(_Blob); + await Serializer.SerializeAsync(_Models).ConfigureAwait(false); } [Benchmark] - public async Task SerializeAsync_Normal() + public async Task DeserializeAsync() { - await _Normal.SerializeAsync(_Models).ConfigureAwait(false); + await Serializer.DeserializeAsync>(_Blob).ConfigureAwait(false); } - [Benchmark] - public async Task SerializeAsync_Recyclable() - { - await _Recyclable.SerializeAsync(_Models).ConfigureAwait(false); - } - - [Benchmark] - public async Task DeserializeAsync_Normal() + public static IEnumerable GetSerializers() { - await _Normal.DeserializeAsync>(_Blob).ConfigureAwait(false); - } - - [Benchmark] - public async Task DeserializeAsync_Recyclable() - { - await _Recyclable.DeserializeAsync>(_Blob).ConfigureAwait(false); - } -} - -[MemoryDiagnoser] -[Config(typeof(Config))] -public class SystemTextJsonSerializerBenchmark - : AbstractSerializersBenchmark -{ - public SystemTextJsonSerializerBenchmark() - { - _Normal = new FusionCacheSystemTextJsonSerializer(); - _Recyclable = new FusionCacheSystemTextJsonSerializer(new FusionCacheSystemTextJsonSerializer.Options + yield return new FusionCacheCysharpMemoryPackSerializer(); + yield return new FusionCacheNeueccMessagePackSerializer(); + yield return new FusionCacheNewtonsoftJsonSerializer(); + yield return new FusionCacheProtoBufNetSerializer(); + yield return new FusionCacheProtoBufNetSerializer(new FusionCacheProtoBufNetSerializer.Options { StreamManager = new RecyclableMemoryStreamManager() }); - } -} - -[MemoryDiagnoser] -[Config(typeof(Config))] -public class ProtobufSerializerBenchmark - : AbstractSerializersBenchmark -{ - public ProtobufSerializerBenchmark() - { - _Normal = new FusionCacheProtoBufNetSerializer(); - _Recyclable = new FusionCacheProtoBufNetSerializer(new FusionCacheProtoBufNetSerializer.Options + yield return new FusionCacheServiceStackJsonSerializer(); + yield return new FusionCacheServiceStackJsonSerializer(new FusionCacheServiceStackJsonSerializer.Options + { + StreamManager = new RecyclableMemoryStreamManager() + }); + yield return new FusionCacheSystemTextJsonSerializer(); + yield return new FusionCacheSystemTextJsonSerializer(new FusionCacheSystemTextJsonSerializer.Options { StreamManager = new RecyclableMemoryStreamManager() }); diff --git a/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/ZiggyCreatures.FusionCache.Benchmarks.csproj b/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/ZiggyCreatures.FusionCache.Benchmarks.csproj index 6ef126d..646b9b7 100644 --- a/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/ZiggyCreatures.FusionCache.Benchmarks.csproj +++ b/benchmarks/ZiggyCreatures.FusionCache.Benchmarks/ZiggyCreatures.FusionCache.Benchmarks.csproj @@ -19,10 +19,11 @@ + + + - - diff --git a/src/ZiggyCreatures.FusionCache.Serialization.CysharpMemoryPack/FusionCacheCysharpMemoryPackSerializer.cs b/src/ZiggyCreatures.FusionCache.Serialization.CysharpMemoryPack/FusionCacheCysharpMemoryPackSerializer.cs index f80e5df..7cf64d1 100644 --- a/src/ZiggyCreatures.FusionCache.Serialization.CysharpMemoryPack/FusionCacheCysharpMemoryPackSerializer.cs +++ b/src/ZiggyCreatures.FusionCache.Serialization.CysharpMemoryPack/FusionCacheCysharpMemoryPackSerializer.cs @@ -74,4 +74,7 @@ public ValueTask SerializeAsync(T? obj, CancellationToken token = def { return new ValueTask(Deserialize(data)); } + + /// + public override string ToString() => $"{GetType().Name}"; } diff --git a/src/ZiggyCreatures.FusionCache.Serialization.NeueccMessagePack/FusionCacheNeueccMessagePackSerializer.cs b/src/ZiggyCreatures.FusionCache.Serialization.NeueccMessagePack/FusionCacheNeueccMessagePackSerializer.cs index 7f37510..fbefcdd 100644 --- a/src/ZiggyCreatures.FusionCache.Serialization.NeueccMessagePack/FusionCacheNeueccMessagePackSerializer.cs +++ b/src/ZiggyCreatures.FusionCache.Serialization.NeueccMessagePack/FusionCacheNeueccMessagePackSerializer.cs @@ -69,4 +69,7 @@ public ValueTask SerializeAsync(T? obj, CancellationToken token = def // PER @neuecc 'S SUGGESTION: AVOID AWAITING ON A MEMORY STREAM return new ValueTask(Deserialize(data)); } + + /// + public override string ToString() => $"{GetType().Name}"; } diff --git a/src/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson/FusionCacheNewtonsoftJsonSerializer.cs b/src/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson/FusionCacheNewtonsoftJsonSerializer.cs index b421034..04a6229 100644 --- a/src/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson/FusionCacheNewtonsoftJsonSerializer.cs +++ b/src/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson/FusionCacheNewtonsoftJsonSerializer.cs @@ -68,4 +68,7 @@ public ValueTask SerializeAsync(T? obj, CancellationToken token = def { return new ValueTask(Deserialize(data)); } + + /// + public override string ToString() => $"{GetType().Name}"; } diff --git a/src/ZiggyCreatures.FusionCache.Serialization.ProtoBufNet/FusionCacheProtoBufNetSerializer.cs b/src/ZiggyCreatures.FusionCache.Serialization.ProtoBufNet/FusionCacheProtoBufNetSerializer.cs index 463fffa..3b58598 100644 --- a/src/ZiggyCreatures.FusionCache.Serialization.ProtoBufNet/FusionCacheProtoBufNetSerializer.cs +++ b/src/ZiggyCreatures.FusionCache.Serialization.ProtoBufNet/FusionCacheProtoBufNetSerializer.cs @@ -185,4 +185,7 @@ public ValueTask SerializeAsync(T? obj, CancellationToken token = def { return new ValueTask(Deserialize(data)); } + + /// + public override string ToString() => $"{(_streamManager != null ? "Recyclable" : "")}{GetType().Name}"; } diff --git a/src/ZiggyCreatures.FusionCache.Serialization.ServiceStackJson/FusionCacheServiceStackJsonSerializer.cs b/src/ZiggyCreatures.FusionCache.Serialization.ServiceStackJson/FusionCacheServiceStackJsonSerializer.cs index 205cb0e..656070d 100644 --- a/src/ZiggyCreatures.FusionCache.Serialization.ServiceStackJson/FusionCacheServiceStackJsonSerializer.cs +++ b/src/ZiggyCreatures.FusionCache.Serialization.ServiceStackJson/FusionCacheServiceStackJsonSerializer.cs @@ -87,4 +87,7 @@ public ValueTask SerializeAsync(T? obj, CancellationToken token = def //using var stream = new MemoryStream(data); //return await JsonSerializer.DeserializeFromStreamAsync(stream); } + + /// + public override string ToString() => $"{(_streamManager != null ? "Recyclable" : "")}{GetType().Name}"; } diff --git a/src/ZiggyCreatures.FusionCache.Serialization.SystemTextJson/FusionCacheSystemTextJsonSerializer.cs b/src/ZiggyCreatures.FusionCache.Serialization.SystemTextJson/FusionCacheSystemTextJsonSerializer.cs index bb505cd..b518568 100644 --- a/src/ZiggyCreatures.FusionCache.Serialization.SystemTextJson/FusionCacheSystemTextJsonSerializer.cs +++ b/src/ZiggyCreatures.FusionCache.Serialization.SystemTextJson/FusionCacheSystemTextJsonSerializer.cs @@ -86,4 +86,7 @@ public async ValueTask SerializeAsync(T? obj, CancellationToken token using var stream = GetMemoryStream(data); return await JsonSerializer.DeserializeAsync(stream, _serializerOptions, token); } + + /// + public override string ToString() => $"{(_streamManager != null ? "Recyclable" : "")}{GetType().Name}"; } diff --git a/tests/ZiggyCreatures.FusionCache.Tests/SerializationTests.cs b/tests/ZiggyCreatures.FusionCache.Tests/SerializationTests.cs index a143cc6..9e63ad4 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/SerializationTests.cs +++ b/tests/ZiggyCreatures.FusionCache.Tests/SerializationTests.cs @@ -73,6 +73,36 @@ public void LoopSucceedsWithComplexTypes(SerializerType serializerType) Assert.Equal(data, looped); } + [Theory] + [ClassData(typeof(SerializerTypesClassData))] + public async Task LoopSucceedsWitComplexTypesArrayAsync(SerializerType serializerType) + { + var data = new ComplexType[1024 * 1024]; + for(int i = 0; i < data.Length; i++) + { + data[i] = ComplexType.CreateSample(); + } + + var serializer = TestsUtils.GetSerializer(serializerType); + var looped = await LoopDeLoopAsync(serializer, data); + Assert.Equal(data, looped); + } + + [Theory] + [ClassData(typeof(SerializerTypesClassData))] + public void LoopSucceedsWithComplexTypesArray(SerializerType serializerType) + { + var data = new ComplexType[1024 * 1024]; + for (int i = 0; i < data.Length; i++) + { + data[i] = ComplexType.CreateSample(); + } + + var serializer = TestsUtils.GetSerializer(serializerType); + var looped = LoopDeLoop(serializer, data); + Assert.Equal(data, looped); + } + [Theory] [ClassData(typeof(SerializerTypesClassData))] public async Task LoopDoesNotFailWithNullAsync(SerializerType serializerType)