using AyCode.Core.Serializers.Binaries; using AyCode.Core.Tests.TestModels; using System.Buffers; using System.Runtime.CompilerServices; namespace AyCode.Core.Serializers.Console.Benchmarks; /// /// Benchmarks AcBinary via the IBufferWriter overload, allocating a FRESH ArrayBufferWriter on EVERY call. /// One-shot scenario — represents code that doesn't reuse a writer across calls. /// Uses BufferWriterChunkSize=4096 (production-realistic, SignalR-aligned) instead of the 65535 default — /// otherwise AcBinary would request 64KB upfront via GetSpan(), forcing the fresh ABW to allocate 64KB /// regardless of payload size (heavy over-allocation for small payloads). /// internal sealed class AcBinaryFreshBufferWriterBenchmark : ISerializerBenchmark { private readonly TestOrder_All_True _order; private readonly AcBinarySerializerOptions _options; private readonly byte[] _serialized; public BenchmarkEngine Engine => BenchmarkEngine.AcBinary; public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrNew; public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime; public string OptionsPreset { get; } public int SerializedSize => _serialized.Length; public long SetupSerializeAllocBytes => 0; public long SetupDeserializeAllocBytes => 0; public string OptionsDescription => BenchmarkOptions.BuildAcBinary(_options, $", BufferSize={_options.BufferWriterChunkSize}B"); public AcBinaryFreshBufferWriterBenchmark(TestOrder_All_True order, AcBinarySerializerOptions options, string optionsPreset) { _order = order; // BufferWriterChunkSize comes from the caller (central source of truth in CreateSerializers // — the binaryFastMode4KbChunk options instance). Do NOT mutate _options here; tune the chunk // size in CreateSerializers only. _options = options; OptionsPreset = optionsPreset; _serialized = AcBinarySerializer.Serialize(order, _options); } [MethodImpl(MethodImplOptions.NoInlining)] public void Serialize() { var abw = new ArrayBufferWriter(); // FRESH every call — alloc + grow as needed AcBinarySerializer.Serialize(_order, abw, _options); } // BufWr semantic: read from a ReadOnlySequence (the ROS overload), NOT from byte[] — // single-segment array-backed sequence triggers the fast-path in AcBinaryDeserializer.cs:298 which // redirects to the byte[] overload. This means the bench actually exercises the ROS-input path // (the production-realistic surface for SignalR / Pipe consumers) rather than secretly testing // byte[] Deser under the BufWr label. [MethodImpl(MethodImplOptions.NoInlining)] public void Deserialize() => AcBinaryDeserializer.Deserialize(new ReadOnlySequence(_serialized), _options); public bool VerifyRoundTrip() { var abw = new ArrayBufferWriter(); AcBinarySerializer.Serialize(_order, abw, _options); var roundTripped = AcBinaryDeserializer.Deserialize(new ReadOnlySequence(abw.WrittenMemory), _options); return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped); } }