【问题标题】:How to access individual items in serialized array?如何访问序列化数组中的单个项目?
【发布时间】:2020-01-16 12:43:01
【问题描述】:

我想在二进制平面文件中存储一组时间戳。 我的一个要求是我可以稍后访问各个时间戳以进行高效查询,而不必先读取和反序列化整个数组(我使用二进制搜索算法来查找开始时间戳的文件位置和结束时间戳,这反过来又决定了在这两个时间戳之间读取和反序列化哪些字节,因为整个二进制文件的大小可以达到数 GB)。

显然,简单但缓慢的方法是使用BitConverter.GetBytes(timestamp) 将每个时间戳转换为字节,然后将它们存储在文件中。然后,我可以单独访问文件中的每个项目,并使用我的自定义二进制搜索算法来查找与所需时间戳匹配的时间戳。

但是,我发现 BinaryFormatter 在值类型数组的序列化/反序列化方面非常高效(比 protobuf-net 和我尝试过的任何其他序列化程序快几倍)。因此,我尝试尝试将时间戳数组序列化为二进制形式。但是,显然现在这将阻止我访问文件中的单个时间戳,而无需首先反序列化整个数组。

有没有办法在通过 BinaryFormatter 序列化整个项目数组后仍以二进制形式访问单个项目?

这里有一些代码 sn-p 说明了我的意思:

var sampleArray = new int[5] { 1,2,3,4,5};

        var serializedSingleValueArray = sampleArray.SelectMany(x => BitConverter.GetBytes(x)).ToArray();
        var serializedArrayofSingleValues = Serializers.BinarySerializeToArray(sampleArray);

        var deserializesToCorrectValue = BitConverter.ToInt32(serializedSingleValueArray, 0); //value = 1 (ok)
        var wrongDeserialization = BitConverter.ToInt32(serializedArrayofSingleValues, 0); //value = 256 (???)

这里是序列化函数:

public static byte[]BinarySerializeToArray(object toSerialize)
    {
        using (var stream = new MemoryStream())
        {
            Formatter.Serialize(stream, toSerialize);
            return stream.ToArray();
        }
    }

编辑:我不需要担心有效的内存消耗或文件大小,因为这些目前还不是瓶颈。序列化和反序列化的速度对我来说是数千兆字节的大型二进制文件以及非常大的原语数组的瓶颈。

【问题讨论】:

  • 我不认为通过BinaryFormatter 可以使用它,坦率地说:即使是这样,我仍然无法在我的推荐中找到它BinaryFormatter。这里的数据是什么; 只是时间戳吗?在这种情况下,使用固定大小的有效负载手动编写可能是您的最佳选择;注意:有很多BitConverter.GetBytes更好的选择
  • 特别是:请参阅BinaryPrimitives 下的方法,这些方法提供跨度和原语之间的字节序感知转换
  • @MarcGravell、long[]、double[] 和 decimal[],其中 long[] 是刻度表示中的时间戳。 BinaryFormatter 的序列化和反序列化比我遇到的任何其他序列化器快几个数量级(反序列化比最新版本的 protobuf-net [原始,未配置] 快 20 倍以上。
  • 如果有真正的“这可能会更快”,我可能会对使用示例尺寸查看您的场景感兴趣 - 但我想知道切换到固定长度编码是否会有所帮助(DataFormat) .然而! protobuf still 不是直接为随机访问元素而设计的(除非您使用原始阅读器 API 等)
  • @MarcGravell,点了,尽管我怀疑 BinaryPrimitives 总之会超过通过 BinaryFormatter 对整个基元数组的反序列化,尽管我需要对此进行测试。

标签: c# serialization deserialization binaryformatter bitconverter


【解决方案1】:

如果您的问题只是“如何将结构数组转换为 byte[]”,那么除了 BitConverter,您还有其他选择。 BitConverter 用于单个值,Buffer 类用于数组。

        double[] d = new double[100];
        d[4] = 1235;
        d[8] = 5678;
        byte[] b = new byte[800];
        Buffer.BlockCopy(d, 0, b, 0, d.Length*sizeof(double));

        // just to test it works
        double[] d1 = new double[100];
        Buffer.BlockCopy(b, 0, d1, 0, d.Length * sizeof(double));

这会进行字节级别的复制,无需转换任何内容,也无需迭代项目。

你可以把这个字节数组直接放到你的流中(不是 StreamWriter,也不是 Formatter)

        stream.Write(b, 0, 800);

这绝对是写入文件的最快方法,但它涉及完整的副本,但可能还有任何其他可以想到的方法,将读取一个项目,出于某种原因首先将其存储,然后再写入文件。

如果这是您写入文件的唯一内容 - 您不需要在文件中写入数组长度,您可以为此使用文件长度。

读取文件中的第 100 个双精度值:

    file.Seek(100*sizeof(double), SeekOrigin.Begin);
    byte[] tmp = new byte[8];
    f.Read(tmp, 0, 8);
    double value = BitConverter.ToDouble(tmp, 0);

这里,对于单个值,您可以使用BitConverter

这是 .NET Framework 的解决方案,C#

对于 .NET Standard/.NET Core、C# 8.0,您可以使用 Span<T> 获得更多选项,这使您可以访问内部存储器,而无需复制数据。

【讨论】:

  • 有趣,谢谢。猜猜是时候阅读 Spans 了。
  • 我将此答案标记为已接受,因为您解决了将大型 long 数组转换为字节并返回的原始性能问题。
【解决方案2】:

Bitconverter 不是“慢”版本,它只是一种将所有内容转换为 byte[] 序列的方法。这实际上并不昂贵,只是对内存的解释不同。

计算文件中的位置,加载8个字节,转换为DateTime,大功告成。

您应该只对简单的结构化文件执行此操作,而对于简单的结构化文件,您不需要二进制格式化程序。只需将您的一个数组加载/保存到一个文件。这样你就可以确定你的文件位置可以被计算出来。

换句话说。自己保存您的数组,日期字节日期,然后您也可以按日期加载它。

用一种处理方式写作,用另一种处理方式阅读,总是一个坏主意。

【讨论】:

  • 这就是我目前正在做的事情,它运行良好,但是文件更大,因此基元数组更大 BinaryFormatter 通过序列化/反序列化通过 BitConverter 在速度方面远远超过逐个元素序列化整个基元数组。
  • 不,没有数组到字节数组的转换器。也找了那个。但这只是这个数组的内存副本,速度相对较快。但我不认为这比一个一个地写入要快得多,因为无论如何,输出流都会被你正在写入的流兑现。但是,您使用 BinaryFormatter 是一种误导方式。这旨在序列化复杂的结构,例如包含类的类,包含类的列表。对于一个简单的数组,没有增益。它不会产生随机可访问的文件。
  • 我知道这不是随机访问文件结果,因此是我的问题。我将通过 Binaryformatter 对逐一处理与数组处理进行快速比较,并将结果发回此处,我记得通过 Binaryformatter(特别是基元数组)要快得多
  • 是否有办法在序列化后仍以二进制形式访问单个项目是对随机访问文件的直接请求。将文件指针设置到一个位置并读取它。
  • BinaryFormatter 在值类型数组上的平均执行速度几乎比 BitConverter 快 10 倍。这就是促使我提出问题的原因。到目前为止,一个核心要求是拥有一个随机访问文件,尽管我对 blob 的想法很感兴趣,并且只是为所述 blob 提供一个随机可访问的索引。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多