This is an automated email from the ASF dual-hosted git repository. blankensteiner pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/pulsar-dotpulsar.git
The following commit(s) were added to refs/heads/master by this push: new 5357829 Added compression benchmarks 5357829 is described below commit 53578295ad9f2552f50f6e2b05c30171e274af6e Author: Daniel Blankensteiner <d...@vmail.dk> AuthorDate: Wed Feb 7 12:21:59 2024 +0100 Added compression benchmarks --- DotPulsar.sln | 9 +++ benchmarks/Compression/Compress.cs | 69 +++++++++++++++++++++ benchmarks/Compression/Compression.csproj | 32 ++++++++++ benchmarks/Compression/Decompress.cs | 89 ++++++++++++++++++++++++++++ benchmarks/Compression/Factories.cs | 64 ++++++++++++++++++++ benchmarks/Compression/MessageBytes.cs | 76 ++++++++++++++++++++++++ benchmarks/Compression/MessageSize.cs | 21 +++++++ benchmarks/Compression/MessageType.cs | 21 +++++++ benchmarks/Compression/Messages.proto | 30 ++++++++++ benchmarks/Compression/Program.cs | 40 +++++++++++++ tests/DotPulsar.Tests/DotPulsar.Tests.csproj | 4 +- 11 files changed, 453 insertions(+), 2 deletions(-) diff --git a/DotPulsar.sln b/DotPulsar.sln index 35c2690..1706858 100644 --- a/DotPulsar.sln +++ b/DotPulsar.sln @@ -26,6 +26,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Processing", "samples\Processing\Processing.csproj", "{CC1494FA-4EB5-4DB9-8BE9-0A6E8D0D963E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compression", "benchmarks\Compression\Compression.csproj", "{040F8253-074D-4977-BDB1-0D9798B52CE2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{2C57AF4B-0D23-42D7-86FE-80277FD52875}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -56,6 +60,10 @@ Global {CC1494FA-4EB5-4DB9-8BE9-0A6E8D0D963E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC1494FA-4EB5-4DB9-8BE9-0A6E8D0D963E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC1494FA-4EB5-4DB9-8BE9-0A6E8D0D963E}.Release|Any CPU.Build.0 = Release|Any CPU + {040F8253-074D-4977-BDB1-0D9798B52CE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {040F8253-074D-4977-BDB1-0D9798B52CE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {040F8253-074D-4977-BDB1-0D9798B52CE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {040F8253-074D-4977-BDB1-0D9798B52CE2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -66,6 +74,7 @@ Global {14934BED-A222-47B2-A58A-CFC4AAB89B49} = {E7106D0F-B255-4631-9FB8-734FC5748FA9} {6D44683B-865C-4D15-9F0A-1A8441354589} = {E7106D0F-B255-4631-9FB8-734FC5748FA9} {CC1494FA-4EB5-4DB9-8BE9-0A6E8D0D963E} = {E7106D0F-B255-4631-9FB8-734FC5748FA9} + {040F8253-074D-4977-BDB1-0D9798B52CE2} = {2C57AF4B-0D23-42D7-86FE-80277FD52875} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {88355922-E70A-4B73-B7F8-ABF8F2B59789} diff --git a/benchmarks/Compression/Compress.cs b/benchmarks/Compression/Compress.cs new file mode 100644 index 0000000..66b7b56 --- /dev/null +++ b/benchmarks/Compression/Compress.cs @@ -0,0 +1,69 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Compression; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Order; +using System.Buffers; + +[RankColumn, Orderer(SummaryOrderPolicy.FastestToSlowest)] +public class Compress +{ + [ParamsAllValues] + public MessageSize Size { get; set; } + + [ParamsAllValues] + public MessageType Type { get; set; } + + [GlobalSetup] + public void Setup() => Data = MessageBytes.GetBytes(Size, Type); + + public ReadOnlySequence<byte> Data { get; private set; } + + [Benchmark] + public void K4aosCompressionLz4() + { + using var compressor = Factories.K4aosCompressionLz4CompressorFactory.Create(); + _ = compressor.Compress(Data); + } + + [Benchmark] + public void IronSnappy() + { + using var compressor = Factories.IronSnappyCompressorFactory.Create(); + _ = compressor.Compress(Data); + } + + [Benchmark] + public void DotNetZip() + { + using var compressor = Factories.DotNetZipCompressorFactory.Create(); + _ = compressor.Compress(Data); + } + + [Benchmark] + public void ZstdNet() + { + using var compressor = Factories.ZstdNetCompressorFactory.Create(); + _ = compressor.Compress(Data); + } + + [Benchmark] + public void ZstdSharp() + { + using var compressor = Factories.ZstdSharpCompressorFactory.Create(); + _ = compressor.Compress(Data); + } +} diff --git a/benchmarks/Compression/Compression.csproj b/benchmarks/Compression/Compression.csproj new file mode 100644 index 0000000..288add9 --- /dev/null +++ b/benchmarks/Compression/Compression.csproj @@ -0,0 +1,32 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="BenchmarkDotNet" Version="0.13.12" /> + <PackageReference Include="DotNetZip" Version="1.16.0" /> + <PackageReference Include="Google.Protobuf" Version="3.25.2" /> + <PackageReference Include="Grpc.Tools" Version="2.61.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="IronSnappy" Version="1.3.1" /> + <PackageReference Include="K4os.Compression.LZ4" Version="1.3.6" /> + <PackageReference Include="ZstdNet" Version="1.4.5" /> + <PackageReference Include="ZstdSharp.Port" Version="0.7.5" /> + </ItemGroup> + + <ItemGroup> + <Protobuf Include="Messages.proto" GrpcServices="None" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\src\DotPulsar\DotPulsar.csproj" /> + </ItemGroup> + +</Project> diff --git a/benchmarks/Compression/Decompress.cs b/benchmarks/Compression/Decompress.cs new file mode 100644 index 0000000..902511a --- /dev/null +++ b/benchmarks/Compression/Decompress.cs @@ -0,0 +1,89 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Compression; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Order; +using System.Buffers; + +[RankColumn, Orderer(SummaryOrderPolicy.FastestToSlowest)] +public class Decompress +{ + [ParamsAllValues] + public MessageSize Size { get; set; } + + [ParamsAllValues] + public MessageType Type { get; set; } + + [GlobalSetup] + public void Setup() + { + var data = MessageBytes.GetBytes(Size, Type); + DecompressedSize = (int) data.Length; + + using (var compressor = Factories.K4aosCompressionLz4CompressorFactory.Create()) + K4aosCompressionLz4Data = compressor.Compress(data); + using (var compressor = Factories.IronSnappyCompressorFactory.Create()) + IronSnappyData = compressor.Compress(data); + using (var compressor = Factories.DotNetZipCompressorFactory.Create()) + DotNetZipData = compressor.Compress(data); + using (var compressor = Factories.ZstdNetCompressorFactory.Create()) + ZstdNetData = compressor.Compress(data); + using (var compressor = Factories.ZstdSharpCompressorFactory.Create()) + ZstdSharpData = compressor.Compress(data); + } + + public int DecompressedSize { get; private set; } + public ReadOnlySequence<byte> K4aosCompressionLz4Data { get; private set; } + public ReadOnlySequence<byte> IronSnappyData { get; private set; } + public ReadOnlySequence<byte> DotNetZipData { get; private set; } + public ReadOnlySequence<byte> ZstdNetData { get; private set; } + public ReadOnlySequence<byte> ZstdSharpData { get; private set; } + + [Benchmark] + public void K4aosCompressionLz4() + { + using var decompressor = Factories.K4aosCompressionLz4DecompressorFactory.Create(); + _ = decompressor.Decompress(K4aosCompressionLz4Data, DecompressedSize); + } + + [Benchmark] + public void IronSnappy() + { + using var decompressor = Factories.IronSnappyDecompressorFactory.Create(); + _ = decompressor.Decompress(IronSnappyData, DecompressedSize); + } + + [Benchmark] + public void DotNetZip() + { + using var decompressor = Factories.DotNetZipDecompressorFactory.Create(); + _ = decompressor.Decompress(DotNetZipData, DecompressedSize); + } + + [Benchmark] + public void ZstdNet() + { + using var decompressor = Factories.ZstdNetDecompressorFactory.Create(); + _ = decompressor.Decompress(ZstdNetData, DecompressedSize); + } + + [Benchmark] + public void ZstdSharp() + { + using var decompressor = Factories.ZstdSharpDecompressorFactory.Create(); + _ = decompressor.Decompress(ZstdSharpData, DecompressedSize); + } +} diff --git a/benchmarks/Compression/Factories.cs b/benchmarks/Compression/Factories.cs new file mode 100644 index 0000000..045e377 --- /dev/null +++ b/benchmarks/Compression/Factories.cs @@ -0,0 +1,64 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Compression; + +using DotPulsar.Internal.Abstractions; +using DotPulsar.Internal.Compression; + +public static class Factories +{ + static Factories() + { + if (!Lz4Compression.TryLoading(out var compressor, out var decompressor)) + throw new Exception("Could not load K4os.Compression.LZ4"); + K4aosCompressionLz4CompressorFactory = compressor!; + K4aosCompressionLz4DecompressorFactory = decompressor!; + + if (!SnappyCompression.TryLoading(out compressor, out decompressor)) + throw new Exception("Could not load IronSnappy"); + IronSnappyCompressorFactory = compressor!; + IronSnappyDecompressorFactory = decompressor!; + + if (!ZlibCompression.TryLoading(out compressor, out decompressor)) + throw new Exception("Could not load DotNetZip"); + DotNetZipCompressorFactory = compressor!; + DotNetZipDecompressorFactory = decompressor!; + + if (!ZstdCompression.TryLoading(out compressor, out decompressor)) + throw new Exception("Could not load ZstdNet"); + ZstdNetCompressorFactory = compressor!; + ZstdNetDecompressorFactory = decompressor!; + + if (!ZstdSharpCompression.TryLoading(out compressor, out decompressor)) + throw new Exception("Could not load ZstdSharp"); + ZstdSharpCompressorFactory = compressor!; + ZstdSharpDecompressorFactory = decompressor!; + } + + public static ICompressorFactory K4aosCompressionLz4CompressorFactory { get; } + public static IDecompressorFactory K4aosCompressionLz4DecompressorFactory { get; } + + public static ICompressorFactory IronSnappyCompressorFactory { get; } + public static IDecompressorFactory IronSnappyDecompressorFactory { get; } + + public static ICompressorFactory DotNetZipCompressorFactory { get; } + public static IDecompressorFactory DotNetZipDecompressorFactory { get; } + + public static ICompressorFactory ZstdNetCompressorFactory { get; } + public static IDecompressorFactory ZstdNetDecompressorFactory { get; } + + public static ICompressorFactory ZstdSharpCompressorFactory { get; } + public static IDecompressorFactory ZstdSharpDecompressorFactory { get; } +} diff --git a/benchmarks/Compression/MessageBytes.cs b/benchmarks/Compression/MessageBytes.cs new file mode 100644 index 0000000..7124e5f --- /dev/null +++ b/benchmarks/Compression/MessageBytes.cs @@ -0,0 +1,76 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Compression; + +using Compression.Messages; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using System.Buffers; +using System.Text; + +public static class MessageBytes +{ + static MessageBytes() + { + var smallMessage = CreateMessage(1024); //1KB + SmallProtobufMessageBytes = new ReadOnlySequence<byte>(smallMessage.ToByteArray()); + SmallJsonMessageBytes = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(JsonFormatter.Default.Format(smallMessage))); + + var largeMessage = CreateMessage(1024*1024); //1MB + LargeProtobufMessageBytes = new ReadOnlySequence<byte>(largeMessage.ToByteArray()); + LargeJsonMessageBytes = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(JsonFormatter.Default.Format(largeMessage))); + } + + public static ReadOnlySequence<byte> SmallProtobufMessageBytes { get; } + public static ReadOnlySequence<byte> LargeProtobufMessageBytes { get; } + public static ReadOnlySequence<byte> SmallJsonMessageBytes { get; } + public static ReadOnlySequence<byte> LargeJsonMessageBytes { get; } + + public static ReadOnlySequence<byte> GetBytes(MessageSize messageSize, MessageType messageType) + { + if (messageSize == MessageSize.Small && messageType == MessageType.Protobuf) + return SmallProtobufMessageBytes; + if (messageSize == MessageSize.Large && messageType == MessageType.Protobuf) + return LargeProtobufMessageBytes; + if (messageSize == MessageSize.Small && messageType == MessageType.Json) + return SmallJsonMessageBytes; + return LargeJsonMessageBytes; + } + + private static Message CreateMessage(int minimumByteSize) + { + var message = new Message() + { + Pi = 3.14159265359, + IsTrue = true, + OneHour = TimeSpan.FromHours(1).ToDuration(), + Now = DateTime.UtcNow.ToTimestamp(), + }; + + while (message.CalculateSize() < minimumByteSize) + { + var innerMessage = new InnerMessage + { + MyEnum = InnerMessage.Types.Enum.Five, + NewGuid = Guid.NewGuid().ToString("N"), + RandomNumber = Random.Shared.Next() + }; + + message.InnerMessages.Add(innerMessage); + } + + return message; + } +} diff --git a/benchmarks/Compression/MessageSize.cs b/benchmarks/Compression/MessageSize.cs new file mode 100644 index 0000000..2f4d88f --- /dev/null +++ b/benchmarks/Compression/MessageSize.cs @@ -0,0 +1,21 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Compression; + +public enum MessageSize +{ + Small = 0, + Large = 1 +} diff --git a/benchmarks/Compression/MessageType.cs b/benchmarks/Compression/MessageType.cs new file mode 100644 index 0000000..1919bad --- /dev/null +++ b/benchmarks/Compression/MessageType.cs @@ -0,0 +1,21 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Compression; + +public enum MessageType +{ + Protobuf = 0, + Json = 1 +} diff --git a/benchmarks/Compression/Messages.proto b/benchmarks/Compression/Messages.proto new file mode 100644 index 0000000..95a2f69 --- /dev/null +++ b/benchmarks/Compression/Messages.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +option csharp_namespace = "Compression.Messages"; + +package compression.Messages; + +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +message Message { + double pi = 1; + bool is_true = 2; + google.protobuf.Duration one_hour = 3; + google.protobuf.Timestamp now = 4; + repeated InnerMessage inner_messages = 5; +} + +message InnerMessage { + enum Enum { + ENUM_UNSPECIFIED = 0; + ENUM_ONE = 1; + ENUM_TWO = 2; + ENUM_THREE = 3; + ENUM_FOUR = 4; + ENUM_FIVE = 5; + } + Enum my_enum = 1; + int32 random_number = 2; + string new_guid = 3; +} diff --git a/benchmarks/Compression/Program.cs b/benchmarks/Compression/Program.cs new file mode 100644 index 0000000..1b91426 --- /dev/null +++ b/benchmarks/Compression/Program.cs @@ -0,0 +1,40 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using BenchmarkDotNet.Running; +using Compression; + +OutputCompressionInfo(MessageSize.Small, MessageType.Protobuf); +OutputCompressionInfo(MessageSize.Large, MessageType.Protobuf); +OutputCompressionInfo(MessageSize.Small, MessageType.Json); +OutputCompressionInfo(MessageSize.Large, MessageType.Json); + +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + +static void OutputCompressionInfo(MessageSize size, MessageType type) +{ + var data = MessageBytes.GetBytes(size, type); + Console.WriteLine($"{size} {type} message. Uncompressed size: {data.Length}"); + + using (var compressor = Factories.K4aosCompressionLz4CompressorFactory.Create()) + Console.WriteLine($"\tCompressed with K4os.Compression.LZ4: {compressor.Compress(data).Length}"); + using (var compressor = Factories.IronSnappyCompressorFactory.Create()) + Console.WriteLine($"\tCompressed with IronSnappy: {compressor.Compress(data).Length}"); + using (var compressor = Factories.DotNetZipCompressorFactory.Create()) + Console.WriteLine($"\tCompressed with DotNetZip: {compressor.Compress(data).Length}"); + using (var compressor = Factories.ZstdNetCompressorFactory.Create()) + Console.WriteLine($"\tCompressed with ZstdNet: {compressor.Compress(data).Length}"); + using (var compressor = Factories.ZstdSharpCompressorFactory.Create()) + Console.WriteLine($"\tCompressed with ZstdSharp.Port: {compressor.Compress(data).Length}"); +} diff --git a/tests/DotPulsar.Tests/DotPulsar.Tests.csproj b/tests/DotPulsar.Tests/DotPulsar.Tests.csproj index ec8627e..e4e6e7e 100644 --- a/tests/DotPulsar.Tests/DotPulsar.Tests.csproj +++ b/tests/DotPulsar.Tests/DotPulsar.Tests.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> @@ -36,7 +36,7 @@ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="ZstdNet" Version="1.4.5" /> - <PackageReference Include="ZstdSharp.Port" Version="0.7.4" /> + <PackageReference Include="ZstdSharp.Port" Version="0.7.5" /> </ItemGroup> <ItemGroup>