优化多维泛型数组的二进制序列化

本文关键字:二进制 序列化 数组 泛型 优化 | 更新日期: 2023-09-27 17:48:49

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

private T[,] m_data;

这些多维数组可以相当大(数十万个元素),也可以是任何基元类型。当我在一个对象上尝试标准的.net序列化时,写入磁盘的文件很大,我认为.net存储了很多关于元素类型的重复数据,可能没有尽可能有效。

我四处寻找自定义序列化程序,但没有看到任何处理多维泛型数组的序列化程序。在序列化之后,我还对内存流的字节数组进行了内置.net压缩的实验,取得了一些成功,但并不像我希望的那样快速/压缩。

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

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

优化多维泛型数组的二进制序列化

以下是我的想法。下面的代码生成一个int[1000][10000],并使用BinaryFormatter将其写入两个文件——一个是压缩文件,另一个不是压缩文件。

压缩后的文件为1.19 MB(1255339字节)解压缩是38.2 MB(40150034字节)

        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);
            }

尝试一下;您将看到40MB的temp_notZipped.txt,62MB的temp_zipped.txt。没那么吸引人。。。

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

你能定义"大"吗?1000x10000xint的例子(另一篇文章)以40Mb的速度出现;1000x10000x4字节(=int)为38MB。就管理费用而言,这并不可怕。

T可能是什么样的数据?只是初级词?我想我可能可以编辑protobuf-net以支持矩形阵列*,但为了保持某种有线兼容性,我们可能需要每个元素一个标头(一个字节),即1000x10000示例的9MB开销。

对于floatdouble等来说,这可能不值得(因为它们被逐字存储在"协议缓冲区"下),但对于int这样的东西,可能会因为它如何打包int而节省一些。。。(尤其是当它们倾向于较小的一侧[幅度]时)。最后,如果T实际上是像Person等对象,那么它应该比二进制序列化好很多,因为它非常擅长打包对象。

在矩形阵列中穿鞋喇叭不是一件小事,但如果你有兴趣尝试,请告诉我。

*:目前还没有,因为"协议缓冲区"规范不支持它们,但我们可以绕过它。。。

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

但正如其他人所指出的,这种冗余数据使其成为压缩的好候选者。