【问题标题】:Optimising binary serialization for multi-dimensional generic arrays优化多维泛型数组的二进制序列化
【发布时间】:2010-09-18 10:21:18
【问题描述】:

我有一个需要二进制序列化的类。该类包含一个字段如下:

private T[,] m_data;

这些多维数组可以相当大(数十万个元素)并且可以是任何原始类型。当我尝试对一个对象进行标准 .net 序列化时,写入磁盘的文件很大,我认为 .net 存储了大量有关元素类型的重复数据,并且可能效率不高。

我四处寻找自定义序列化程序,但没有看到任何处理多维泛型数组的方法。在序列化成功后,我还尝试了对内存流的字节数组的内置 .net 压缩,但没有我希望的那么快/压缩。

我的问题是,我应该尝试编写一个自定义序列化程序来优化序列化这个数组以获得适当的类型(这似乎有点令人生畏),还是应该使用标准的 .net 序列化并添加压缩?

任何关于最佳方法的建议,或显示如何处理多维泛型数组序列化的资源的链接都将不胜感激 - 正如提到的 existing examples 我发现不支持这种结构。

【问题讨论】:

    标签: c# .net serialization binary


    【解决方案1】:

    这就是我想出的。下面的代码生成一个 int[1000][10000] 并使用 BinaryFormatter 将其写入 2 个文件 - 一个已压缩,一个未压缩。

    压缩文件为 1.19 MB(1,255,339 字节) 解压后为 38.2 MB(40,150,034 字节)

            int width = 1000;
            int height = 10000;
            List<int[]> list = new List<int[]>();
            for (int i = 0; i < height; i++)
            {
                list.Add(Enumerable.Range(0, width).ToArray());
            }
            int[][] bazillionInts = list.ToArray();
            using (FileStream fsZ = new FileStream("c:\\temp_zipped.txt", FileMode.Create))
            using (FileStream fs = new FileStream("c:\\temp_notZipped.txt", FileMode.Create))
            using (GZipStream gz = new GZipStream(fsZ, CompressionMode.Compress))
            {
                BinaryFormatter f = new BinaryFormatter();
                f.Serialize(gz, bazillionInts);
                f.Serialize(fs, bazillionInts);
            }
    

    我想不出更好/更简单的方法来做到这一点。压缩版本非常紧。

    我会选择 BinaryFormatter + GZipStream。定制一些东西一点也不好玩。


    [由 MG 编辑] 我希望您不会被编辑冒犯,但是统一重复的 Range(0,width) 极大地扭曲了事情;改为:

            int width = 1000;
            int height = 10000;
            Random rand = new Random(123456);
            int[,] bazillionInts = new int[width, height];
            for(int i = 0 ; i < width;i++)
                for (int j = 0; j < height; j++)
                {
                    bazillionInts[i, j] = rand.Next(50000);
                }
    

    试试看;你会看到temp_notZipped.txt 40MB,temp_zipped.txt 62MB。没那么吸引人……

    【讨论】:

    • 这有两个问题;首先是OP询问了矩形(不是锯齿状)。更重要的是您的压缩受到统一数据的影响。我将添加一个随机运动来表明我的意思......
    • 当然不能压缩随机数据。但是可以合理压缩大量有意义的数据。所以在我看来,压缩可能很有吸引力。
    【解决方案2】:

    最好的代码长度/输出大小比率是使用 BitConverter 对数组进行编码,将所有元素转换为其紧凑的二进制格式。我知道这是手动的,但与 .NET 二进制序列化相比,它将节省 80-90% 的空间。

    【讨论】:

    • 但是,BitConverter 与泛型一起使用很痛苦(您需要使用反射,大概与 Delegate.CreateDelegate 结合以提高效率),并且不适用于所有类型(甚至不是所有内置结构)...
    【解决方案3】:

    你能定义“大”吗? 1000x10000xint 示例(另一篇文章)的大小为 40Mb; 1000x10000x4 字节 (=int) 为 38MB。随着开销的增加,这并不可怕。

    T 可能是什么类型的数据?只是原语? 我在想我可以编辑protobuf-net 以支持矩形数组* - 但为了保持某种电线兼容性,我们可能需要一个标题(一个字节)每个元素 -即 1000x10000 示例的 9MB 开销。

    这对于floatdouble 等东西可能不值得(因为它们被逐字存储在“协议缓冲区”下)——但对于int 之类的东西可能会节省一些费用,这仅仅是因为它包含整数......(特别是如果它们倾向于在较小的一侧[幅度])。最后,如果T实际上是Person之类的对象,那么它应该比二进制序列化更好很多,因为它非常擅长打包对象。

    在矩形阵列中使用鞋拔并非易事,但如果您有兴趣尝试,请告诉我。

    *:目前不支持,因为“协议缓冲区”规范不支持它们,但我们可以解决这个问题...

    【讨论】:

    • 非常感谢您对 Marc 的看法。我查看了 protobuf-net,它看起来很有趣。对于我的课程,会有很多重复/冗余数据,所以我认为标准二进制序列化加压缩就足够了。
    【解决方案4】:

    需要关于类型的这么多数据的原因是您的 T 数组可以是任何类型,但更具体地说,T 可以是 SomeBaseClass 类型,您仍然可以将 SomeDerivedClass 存储在该数组中,并且反序列化器需要知道这一点。

    但正如其他人所指出的那样,这种冗余数据使其成为压缩的理想选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-29
      • 2017-08-27
      • 2022-11-29
      相关资源
      最近更新 更多