【问题标题】:Performance issue when serializing multi-dimensional arrays using BinaryFormatter in .NET在 .NET 中使用 BinaryFormatter 序列化多维数组时的性能问题
【发布时间】:2011-11-09 02:55:21
【问题描述】:

我正在使用BinaryFormatter 序列化一个相当简单的多维浮点数组,尽管我怀疑任何原始类型都会出现问题。我的多维数组包含 10000x16 个浮点数(160k),并且在我的 PC 上以 ~8 MB/s 的速度进行序列化(60 秒基准写入 ~500 MB 到 SSD 驱动器)。代码:

        Stopwatch stopwatch = new Stopwatch();

        float[,] data = new float[10000 , 16];  // Two-dimensional array of 160,000 floats.
        // OR
        float[]  data = new float[10000 * 16];  // One-dimensional array of 160,000 floats.

        var formatter = new BinaryFormatter();
        var stream = new FileStream("C:\\Temp\\test_serialization.data", FileMode.Create, FileAccess.Write);

        // Serialize to disk the array 1000 times.
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < 1000; i++)
        {
            formatter.Serialize(stream, data);
        }
        stream.Close();
        stopwatch.Stop();

        TimeSpan ts = stopwatch.Elapsed;

        // Format and display the TimeSpan value.
        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:000}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds);
        Console.WriteLine("Runtime " + elapsedTime);
        var info = new FileInfo(stream.Name);
        Console.WriteLine("Speed: {0:0.00} MB/s", info.Length / ts.TotalSeconds / 1024.0 / 1024.0);

做同样的事情,但使用 160k 浮点数的一维数组,相同数量的数据以 ~179 MB/s 的速度序列化到磁盘。速度提高 20 倍以上! 为什么使用BinaryFormatter 序列化二维数组的性能如此糟糕? 两个数组在内存中的底层存储应该是相同的。 (我已经完成了不安全的本机 pin_ptr 并在 C++/CLI 中复制到二维数组和从二维数组复制)。

一个骇人听闻的解决方案是实现ISerializable 并将二维数组进行内存复制(不安全/ptr 固定/阻止内存复制)到一维数组中,并将其和维度序列化。我正在考虑的另一个选择是切换到protobuf-net

【问题讨论】:

  • 它在反思上花费了太多时间。数组很麻烦,因为它们在 .NET 中是协变的。您可以使用锯齿状数组加快速度。

标签: .net performance serialization multidimensional-array binaryformatter


【解决方案1】:

无需放弃数据结构或复制值,您可以使用以下代码来达到相同的性能:

            fixed (float* ptr = data)
            {
                byte* arr = (byte*)ptr;
                int size = sizeof(float);

                for (int j = 0; j < data.Length * size; j++)
                {
                    stream.WriteByte(arr[j]);
                }
            }

基本上,您是在自己编写输出流,就像您说的那样,您只是将 float[] 用作 byte[],因为内存结构是相同的。

反序列化是一样的,你可以使用 StreamReader 来读取浮点数或 unsafe 并将数据加载到内存中。

如果您有这样的基本需求,我强烈反对使用 protobuf.net。开发速度放缓并且基于一个人,所以风险很大(当我试图帮助解决性能问题时,他甚至懒得看我提出的改变)。 但是,如果你想序列化复杂的数据结构,二进制序列化不会比 protobuf 慢多少,虽然后者在 .NET 平台上没有正式支持(谷歌发布了它的 Java、Python 和 C++ 代码)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-07
    • 1970-01-01
    • 2016-02-03
    • 2012-10-18
    相关资源
    最近更新 更多