From e2632a730fcc328fdaa6fa4bebe5c9016c3fed9d Mon Sep 17 00:00:00 2001 From: Vitalii Kotenko Date: Fri, 30 Aug 2024 14:07:19 +0300 Subject: [PATCH] added compression benchmarks --- Void.slnx | 13 +- src/Void.Benchmarks/Program.cs | 4 + src/Void.Benchmarks/Streams/Compression.cs | 126 ++++++++++++++++++ .../Streams/MinecraftMemoryStream.cs | 78 +++++++++++ src/Void.Benchmarks/Void.Benchmarks.csproj | 18 +++ 5 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 src/Void.Benchmarks/Program.cs create mode 100644 src/Void.Benchmarks/Streams/Compression.cs create mode 100644 src/Void.Benchmarks/Streams/MinecraftMemoryStream.cs create mode 100644 src/Void.Benchmarks/Void.Benchmarks.csproj diff --git a/Void.slnx b/Void.slnx index 0cff4d5..7761f6f 100644 --- a/Void.slnx +++ b/Void.slnx @@ -9,6 +9,17 @@ - + + + + + + + + + + + + diff --git a/src/Void.Benchmarks/Program.cs b/src/Void.Benchmarks/Program.cs new file mode 100644 index 0000000..8f9b729 --- /dev/null +++ b/src/Void.Benchmarks/Program.cs @@ -0,0 +1,4 @@ +using BenchmarkDotNet.Running; +using Void.Benchmarks.Streams; + +BenchmarkRunner.Run(); \ No newline at end of file diff --git a/src/Void.Benchmarks/Streams/Compression.cs b/src/Void.Benchmarks/Streams/Compression.cs new file mode 100644 index 0000000..453a684 --- /dev/null +++ b/src/Void.Benchmarks/Streams/Compression.cs @@ -0,0 +1,126 @@ +using BenchmarkDotNet.Attributes; +using Microsoft.IO; +using Void.Proxy.API.Network.IO.Messages; +using Void.Proxy.API.Network.IO.Streams.Compression; + +namespace Void.Benchmarks.Streams; + +public class Compression +{ + private static readonly RecyclableMemoryStreamManager MemoryStreamManager = new(new RecyclableMemoryStreamManager.Options + { + BlockSize = 1024, + LargeBufferMultiple = 1024 * 1024, + MaximumBufferSize = 16 * 1024 * 1024, + GenerateCallStacks = false, + AggressiveBufferReturn = true, + MaximumLargePoolFreeBytes = 16 * 1024 * 1024, + MaximumSmallPoolFreeBytes = 100 * 1024 + }); + + private readonly IonicZlibCompressionMessageStream _ionicZlibStream = new() { BaseStream = new MinecraftMemoryStream(), CompressionThreshold = 256 }; + private readonly SharpZipLibCompressionMessageStream _sharpZipLibStream = new() { BaseStream = new MinecraftMemoryStream(), CompressionThreshold = 256 }; + private byte[] _buffer = []; + + [Params(32, 1024)] public int BufferSize { get; set; } + + /* + * BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4112/23H2/2023Update/SunValley3) + * 12th Gen Intel Core i9-12900KF, 1 CPU, 24 logical and 16 physical cores + * .NET SDK 9.0.100-preview.7.24407.12 + * [Host] : .NET 9.0.0 (9.0.24.40507), X64 RyuJIT AVX2 [AttachedDebugger] + * DefaultJob : .NET 9.0.0 (9.0.24.40507), X64 RyuJIT AVX2 + * + * + * 1KB buffer (with compression) + * + * | Method | BufferSize | Mean | Error | StdDev | + * |------------------ |----------- |------------:|------------:|----------:| + * | SharpZipLib_Write | 1024 | 25,013.6 us | 384.20 us | 359.38 us | + * | SharpZipLib_Read | 1024 | 1,229.1 us | 24.51 us | 46.04 us | + * | IonicZlib_Write | 1024 | 80,828.7 us | 1,184.75 us | 989.32 us | + * | IonicZlib_Read | 1024 | 5,429.7 us | 108.03 us | 280.79 us | + * + * 32B buffer (without compression) + * + * | Method | BufferSize | Mean | Error | StdDev | + * |------------------ |----------- |------------:|------------:|----------:| + * | SharpZipLib_Write | 32 | 1,081.5 us | 40.94 us | 120.06 us | + * | SharpZipLib_Read | 32 | 947.2 us | 27.13 us | 79.58 us | + * | IonicZlib_Write | 32 | 1,022.6 us | 20.42 us | 35.23 us | + * | IonicZlib_Read | 32 | 928.0 us | 21.75 us | 62.05 us | + */ + + [GlobalSetup] + public void GlobalSetup() + { + _buffer = new byte[BufferSize]; + Random.Shared.NextBytes(_buffer); + } + + [IterationSetup] + public void IterationSetup() + { + var sharpZipLibMemoryStream = (MinecraftMemoryStream)_sharpZipLibStream.BaseStream!; + var ionicZlibMemoryStream = (MinecraftMemoryStream)_ionicZlibStream.BaseStream!; + + sharpZipLibMemoryStream.Reset(0); + ionicZlibMemoryStream.Reset(0); + + SharpZipLib_Write().GetAwaiter().GetResult(); + IonicZlib_Write().GetAwaiter().GetResult(); + + sharpZipLibMemoryStream.Reset(); + ionicZlibMemoryStream.Reset(); + } + + [Benchmark] + public async ValueTask SharpZipLib_Write() + { + for (var i = 0; i < 1000; i++) + { + var stream = MemoryStreamManager.GetStream(); + stream.Write(_buffer); + + var message = new CompleteBinaryMessage(stream); + await _sharpZipLibStream.WriteMessageAsync(message); + } + } + + [Benchmark] + public async ValueTask SharpZipLib_Read() + { + var sharpZipLibMemoryStream = (MinecraftMemoryStream)_sharpZipLibStream.BaseStream!; + sharpZipLibMemoryStream.Reset(); + + for (var i = 0; i < 1000; i++) + { + var message = await _sharpZipLibStream.ReadMessageAsync(); + } + } + + [Benchmark] + public async ValueTask IonicZlib_Write() + { + for (var i = 0; i < 1000; i++) + { + var stream = MemoryStreamManager.GetStream(); + stream.Write(_buffer); + + var message = new CompleteBinaryMessage(stream); + await _ionicZlibStream.WriteMessageAsync(message); + } + } + + [Benchmark] + public async ValueTask IonicZlib_Read() + { + var ionicZlibMemoryStream = (MinecraftMemoryStream)_ionicZlibStream.BaseStream!; + ionicZlibMemoryStream.Reset(); + + for (var i = 0; i < 1000; i++) + { + var message = await _ionicZlibStream.ReadMessageAsync(); + } + } +} \ No newline at end of file diff --git a/src/Void.Benchmarks/Streams/MinecraftMemoryStream.cs b/src/Void.Benchmarks/Streams/MinecraftMemoryStream.cs new file mode 100644 index 0000000..151eabb --- /dev/null +++ b/src/Void.Benchmarks/Streams/MinecraftMemoryStream.cs @@ -0,0 +1,78 @@ +using System.Net.Sockets; +using Void.Proxy.API.Network.IO.Streams; + +namespace Void.Benchmarks.Streams; + +internal class MinecraftMemoryStream : IMinecraftNetworkStream +{ + private readonly MemoryStream _memoryStream = new(); + public NetworkStream BaseStream => null!; + + public void PrependBuffer(Memory buffer) + { + throw new NotSupportedException(); + } + + public void Dispose() + { + _memoryStream.Dispose(); + } + + public async ValueTask DisposeAsync() + { + await _memoryStream.DisposeAsync(); + } + + public void Flush() + { + _memoryStream.Flush(); + } + + public async ValueTask FlushAsync(CancellationToken cancellationToken = default) + { + await _memoryStream.FlushAsync(cancellationToken); + } + + public void Close() + { + _memoryStream.Close(); + } + + public int Read(Span span) + { + return _memoryStream.Read(span); + } + + public async ValueTask ReadAsync(Memory memory, CancellationToken cancellationToken = default) + { + return await _memoryStream.ReadAsync(memory, cancellationToken); + } + + public void ReadExactly(Span span) + { + _memoryStream.ReadExactly(span); + } + + public async ValueTask ReadExactlyAsync(Memory memory, CancellationToken cancellationToken = default) + { + await _memoryStream.ReadExactlyAsync(memory, cancellationToken); + } + + public void Write(ReadOnlySpan span) + { + _memoryStream.Write(span); + } + + public async ValueTask WriteAsync(ReadOnlyMemory memory, CancellationToken cancellationToken = default) + { + await _memoryStream.WriteAsync(memory, cancellationToken); + } + + public void Reset(int length = -1) + { + _memoryStream.Position = 0; + + if (length >= 0) + _memoryStream.SetLength(length); + } +} \ No newline at end of file diff --git a/src/Void.Benchmarks/Void.Benchmarks.csproj b/src/Void.Benchmarks/Void.Benchmarks.csproj new file mode 100644 index 0000000..d7c9b81 --- /dev/null +++ b/src/Void.Benchmarks/Void.Benchmarks.csproj @@ -0,0 +1,18 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + + + + +