序列化已知的小结构数组的好方法

A good way to serialize a known array of small structs

我有一大堆小结构,我想序列化到文件中。

结构:

public struct Voxel
{
  public byte density;
  public byte material;
}

虽然有很多序列化库可以非常有效地进行一般序列化,但我怀疑我们可以在磁盘大小方面做得更好serialization/deserialization 速度,假设我们知道并控制这个结构。

这个结构是非常最终的,所以我们可以不用许多序列化库支持的花哨版本控制。

根据我的搜索,Marshal 似乎是做这种事情的一种不错的方式,但我不想担心 Endianness 之类的事情。

所以我想知道,有什么好的方法可以序列化此类数据。假设数组大小可以在 100 到 100 万之间?

(假设我们不怕以不同的格式存储它们,这样 RLE 可以进一步减少磁盘大小。)

假设您使用的是最新的框架:跨度是您的朋友。作为 编写 它们的简单方法:

Voxel[] arr = ...
var bytes = MemoryMarshal.Cast<Voxel, byte>(arr);
using (var s = File.OpenWrite("some.path"))
{
    s.Write(bytes);
}

阅读有点,但不多:

Voxel[] arr;
using (var s = File.OpenRead("some.path"))
{
    int len = checked((int)(s.Length / Unsafe.SizeOf<Voxel>())), read;
    arr = new Voxel[len];
    var bytes = MemoryMarshal.Cast<Voxel, byte>(arr);
    while (!bytes.IsEmpty && (read = s.Read(bytes)) > 0)
    {
        bytes = bytes.Slice(read);
    }
}

请注意,这假定您要从向量开始 (Voxel[]);如果您乐于在兔子洞中更进一步,“内存映射文件”也是此处的一个选项,再次使用 Span<T>(或 Memory<T>)——然后它变成真正的零拷贝(您的实时数据 文件,通过 OS magic)。

对于未来的读者,我找到了一个解决方案,允许我在 Unity Engine 中使用 @Marc Gravell 提出的解决方案,它只允许 .Net Standard 2.0;

诀窍是从微软获得高性能包:

https://docs.microsoft.com/en-us/windows/communitytoolkit/high-performance/introduction

This means that you can use it from anything from UWP or legacy .NET Framework applications, games written in Unity, cross-platform mobile applications using Xamarin, to .NET Standard libraries and modern .NET Core 2.1 or .NET Core 3.1 applications.

它通过扩展方法支持 Stream.WriteStream.ReadSpan<T>:

https://docs.microsoft.com/en-us/dotnet/api/microsoft.toolkit.highperformance.extensions.streamextensions?view=win-comm-toolkit-dotnet-6.1

我还比较了二进制序列化的磁盘大小:

  • 内存中:8KB(16x16x16 数组)
  • 消息包格式:97KB
  • MemoryMarshal.Cast: 8KB

所以它按预期工作!