feat: Replaces Zlib with LibDeflate (#1774)

> [!Warning]
> Users on Linux/OSX will need to follow the Readme
> and make sure `libdeflate` is properly installed

> [!Note]
> **Developer Note**
> The API for compression has changed. Use `Deflate.Standard` for the same functionality.

### Summary
* Replaces Zlib with LibDeflate for a 50% performance improvement!
* Adds MacOS 14 to properly test Arm64
This commit is contained in:
Kamron Batman 2024-05-16 22:53:01 -07:00 committed by GitHub
parent 35a292d2c7
commit 1f701e7b55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 279 additions and 282 deletions

View file

@ -0,0 +1,23 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Deflate.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.IO.Compression;
namespace Server.Compression;
public static class Deflate
{
public static LibDeflateBinding Standard { get; } = new();
}

View file

@ -19,6 +19,7 @@ using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using Server.Buffers;
using Server.Compression;
namespace Server;
@ -72,8 +73,8 @@ public static class MultiData
}
var decompressedSize = entry.Size;
if (Zlib.Unpack(compressionBuffer, ref decompressedSize, buffer, entry.CompressedSize) != ZlibError.Okay
|| decompressedSize != entry.Size)
if (Deflate.Standard.Unpack(compressionBuffer, buffer, out var bytesDecompressed) != LibDeflateResult.Success
|| decompressedSize != bytesDecompressed)
{
throw new FileLoadException($"Error loading file {stream.Name}. Failed to unpack entry {i}.");
}
@ -118,11 +119,7 @@ public static class MultiData
}
STArrayPool<byte>.Shared.Return(buffer);
if (compressionBuffer != null)
{
STArrayPool<byte>.Shared.Return(compressionBuffer);
}
STArrayPool<byte>.Shared.Return(compressionBuffer);
}
private static void LoadMul(bool postHSMulFormat)

View file

@ -16,10 +16,9 @@
using System;
using System.Buffers;
using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices;
using Server.Buffers;
using Server.Collections;
using Server.Compression;
using Server.Gumps;
using Server.Logging;
@ -49,7 +48,6 @@ public static class OutgoingGumpPackets
private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
private static readonly byte[] _packBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
private static readonly OrderedSet<string> _stringsList = new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -63,36 +61,21 @@ public static class OutgoingGumpPackets
return;
}
var wantLength = Zlib.MaxPackSize(length);
var packBuffer = _packBuffer;
byte[] rentedBuffer = null;
var dest = writer.RawBuffer[(writer.Position + 8)..];
if (wantLength > packBuffer.Length)
var bytesPacked = Deflate.Standard.Pack(dest, span);
if (bytesPacked == 0)
{
packBuffer = rentedBuffer = STArrayPool<byte>.Shared.Rent(wantLength);
}
var packLength = wantLength;
var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default);
if (error != ZlibError.Okay)
{
logger.Warning("Gump compression failed: {Error}", error);
logger.Warning("Gump compression failed");
writer.Write(4);
writer.Write(0);
return;
}
writer.Write(4 + packLength);
writer.Write(4 + bytesPacked);
writer.Write(length);
writer.Write(packBuffer.AsSpan(0, packLength));
if (rentedBuffer != null)
{
STArrayPool<byte>.Shared.Return(rentedBuffer);
}
writer.Seek(bytesPacked, SeekOrigin.Current);
}
public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries)
@ -141,11 +124,8 @@ public static class OutgoingGumpPackets
stringsWriter.WriteBigUni(s);
}
var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten);
var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten);
var maxLength = 40 + worstLayoutLength + worstStringsLength;
var writer = new SpanWriter(maxLength);
var writer = new SpanWriter(0x10000);
writer.Write((byte)0xDD); // Packet ID
writer.Seek(2, SeekOrigin.Current);
@ -171,6 +151,8 @@ public static class OutgoingGumpPackets
{
_stringsList.Clear();
}
writer.Dispose();
}
public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption)

View file

@ -29,13 +29,15 @@
<Delete Files="..\..\Distribution\Serilog.Sinks.Async.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Serilog.Sinks.Console.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\wepoll.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\ZLib.Bindings.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\LibDeflate.Bindings.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libdeflate.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libdeflate.dylib" ContinueOnError="true" />
</Target>
<ItemGroup>
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
<PackageReference Include="LibDeflate.Bindings" Version="1.0.0.120" />
<PackageReference Include="PollGroup" Version="1.4.3" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
<PackageReference Include="Zlib.Bindings" Version="1.11.0" />
<PackageReference Include="ModernUO.Serialization.Annotations" Version="2.9.1" />
<PackageReference Include="ModernUO.Serialization.Generator" Version="2.10.9" />